mirror of
https://github.com/Schrolli91/BOSWatch.git
synced 2025-12-06 07:42:03 +01:00
commit
432ff35222
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
*.psd
|
||||
*.pyc
|
||||
*.log
|
||||
config.ini
|
||||
config.ini
|
||||
log/
|
||||
|
|
|
|||
1
LICENSE
1
LICENSE
|
|
@ -337,4 +337,3 @@ proprietary programs. If your program is a subroutine library, you may
|
|||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
|
|
|
|||
70
README.md
70
README.md
|
|
@ -1,6 +1,6 @@
|
|||

|
||||
|
||||
Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG
|
||||
:satellite: Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG :satellite:
|
||||
|
||||
#### Notice:
|
||||
The intercept of the German BOS radio is **strictly prohibited** and will be prosecuted. the use is **only authorized** personnel permitted.
|
||||
|
|
@ -15,25 +15,34 @@ unless you are developer you can use the develop-Branch - may be unstable!
|
|||
##### Implemented features:
|
||||
- FMS, ZVEI and POCSAG512/1200/2400 decoding and displaying
|
||||
- Plugin support for easy functional extension
|
||||
- Filtering double alarms with adjustable time
|
||||
- Filtering double alarms with adjustable time and check width
|
||||
- Filtering allowed, denied and range of POCSAG RIC´s
|
||||
- Filtering data for each typ/plugin combination with RegEX
|
||||
- All configurations in a seperate config file
|
||||
- Data validation (plausibility test)
|
||||
- Description look-up from csv-files
|
||||
- Logfiles for better troubleshooting
|
||||
- verbose/quiet mode for more/none information
|
||||
- Ready for use BOSWatch as daemon
|
||||
|
||||
##### Features for the future:
|
||||
- more plugins
|
||||
- other Ideas per Issues please
|
||||
|
||||
|
||||
###Plugins
|
||||
If you want to code your own Plugin, see Section `Code your own Plugin` at the end of this readme.MD
|
||||
|
||||
##### Implemented plugins:
|
||||
- MySQL (insert data into MySQL database [FMS|ZVEI|POC])
|
||||
- httpRequest (send a request with parameter to an URL [FMS|ZVEI|POC])
|
||||
- eMail (send Mails [FMS|ZVEI|POC])
|
||||
- BosMon (send data to BosMon server [FMS|ZVEI|POC])
|
||||
- firEmergency (send data to firEmergency server [ZVEI|POC])
|
||||
|
||||
|Plugin|Function|FMS|ZVEI|POC|
|
||||
|-----|---------|:-:|:--:|:-:|
|
||||
|MySQL|insert data into MySQL database|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|
||||
|httpRequest|send a request with parameter to an URL|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|
||||
|eMail|send Mails with own text|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|
||||
|BosMon|send data to BosMon server|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|
||||
|firEmergency|send data to firEmergency server|:x:|:white_check_mark:|:white_check_mark:|
|
||||
|jsonSocket|send data as jsonString to a socket server|:white_check_mark:|:white_check_mark:|:white_check_mark:|
|
||||
|
||||
- for more Information to the plugins see `config.ini`
|
||||
|
||||
|
|
@ -54,25 +63,28 @@ For the other functions see "Usage" below.
|
|||
##### Filtering Functions (RegEX)
|
||||
For the RegEX filter functions see Section `[Filters]`
|
||||
http://www.regexr.com/ - RegEX test tool an documentation
|
||||
|
||||
No filter for a combination typ/plugin = all data will pass
|
||||
|
||||
Syntax: INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX (separator ";")
|
||||
- TYP = the data typ (FMS|ZVEI|POC)
|
||||
- DATAFIELD = the field of the data array (See interface.txt)
|
||||
- PLUGIN = the name of the plugin to call with this filter (* for all)
|
||||
- FREQUENZ = the frequenz to use the filter (for more SDR sticks (* for all))
|
||||
- REGEX = the RegEX
|
||||
Syntax: `INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX` (separator `;`)
|
||||
- `TYP` = the data typ (FMS|ZVEI|POC)
|
||||
- `DATAFIELD` = the field of the data array (See interface.txt)
|
||||
- `PLUGIN` = the name of the plugin to call with this filter (* for all)
|
||||
- `FREQUENZ` = the frequenz to use the filter (for more SDR sticks (* for all))
|
||||
- `REGEX` = the RegEX
|
||||
|
||||
only ZVEI to all plugins with 25### at 85.5MHz
|
||||
testfilter = ZVEI;zvei;*;85500000;25[0-9]{3}
|
||||
`testfilter = ZVEI;zvei;*;85500000;25[0-9]{3}`
|
||||
|
||||
only POCSAG to MySQL with the text "ALARM:" in the message
|
||||
pocTest = POC;msg;MySQL;*;ALARM:
|
||||
`pocTest = POC;msg;MySQL;*;ALARM:`
|
||||
|
||||
##### Web frontend
|
||||
Put the files in folder /wwww/ into your local webserver folder (f.e. /var/www/).
|
||||
##### Web frontend (obsolete)
|
||||
New version in future - old data in folder `/www/`
|
||||
|
||||
~~Put the files in folder /wwww/ into your local webserver folder (f.e. /var/www/).
|
||||
Now you must edit the "config.php" with your userdata to your local database.
|
||||
Take a look into the parser.php for the parsing functions
|
||||
Take a look into the parser.php for the parsing functions~~
|
||||
|
||||
|
||||
### Usage
|
||||
|
|
@ -90,12 +102,13 @@ usage: boswatch.py [-h] -f FREQ [-d DEVICE] [-e ERROR] -a
|
|||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-f FREQ, --freq FREQ Frequency you want to listen
|
||||
-d DEVICE, --device DEVICE Device you want to use (Check with rtl_test)
|
||||
-d DEVICE, --device DEVICE Device you want to use (Check with rtl_test)
|
||||
-e ERROR, --error ERROR Frequency-Error of your device in PPM
|
||||
-a {FMS,ZVEI,POC512,POC1200,POC2400} [{FMS,ZVEI,POC512,POC1200,POC2400} ...],
|
||||
--demod {FMS,ZVEI,POC512,POC1200,POC2400} [{FMS,ZVEI,POC512,POC1200,POC2400} ...]
|
||||
Demodulation functions
|
||||
-s SQUELCH, --squelch SQUELCH level of squelch
|
||||
Demodulation functions
|
||||
-s SQUELCH, --squelch SQUELCH level of squelch
|
||||
-u, --usevarlog Use '/var/log/boswatch' for logfiles instead of subdir 'log' in BOSWatch directory
|
||||
-v, --verbose Shows more information
|
||||
-q, --quiet Shows no information. Only logfiles
|
||||
```
|
||||
|
|
@ -114,10 +127,25 @@ In case of an error during the installation, check the logfile in `~/boswatch/in
|
|||
|
||||
Caution, script don't install a webserver with PHP and MySQL.
|
||||
So you have to make up manually if you want to use MySQL support.
|
||||
Database Structure `boswatch.sql` in the MySQL Plugin Folder
|
||||
|
||||
If you want to use BOSWatch as a daemon, you have to set your
|
||||
configuration in `service/boswatch.sh` and copy it to `/etc/init.d`.
|
||||
Then you can start BOSWatch with `sudo /etc/init.d/boswatch.sh start`.
|
||||
For configuration-details see `service/README.md`.
|
||||
|
||||
### Requirements
|
||||
- RTL_SDR (rtl_fm)
|
||||
- Multimon-NG
|
||||
- Python Support
|
||||
- MySQL Connector for Python (for MySQL-plugin)
|
||||
|
||||
Thanks to smith_fms and McBo from Funkmeldesystem.de - Forum for Inspiration and Groundwork!
|
||||
|
||||
|
||||
### Code your own Plugin
|
||||
See `plugins/README.md`
|
||||
|
||||
~~To code your own Plugin look at the litte example `/plugins/template/template.py`~~
|
||||
|
||||
~~In the text-file `plugins/interface.txt` are all relevant data, that your plugin can use.~~
|
||||
|
|
|
|||
545
boswatch.py
545
boswatch.py
|
|
@ -10,6 +10,8 @@ For more information see the README.md
|
|||
@author: Bastian Schroll
|
||||
@author: Jens Herrmann
|
||||
|
||||
Thanks to smith_fms and McBo from Funkmeldesystem.de - Forum for Inspiration and Groundwork!
|
||||
|
||||
GitHUB: https://github.com/Schrolli91/BOSWatch
|
||||
"""
|
||||
|
||||
|
|
@ -19,54 +21,24 @@ import logging.handlers
|
|||
import argparse # for parse the args
|
||||
import ConfigParser # for parse the config file
|
||||
import os # for log mkdir
|
||||
import time # for timestamp
|
||||
import time # for time.sleep()
|
||||
import subprocess # for starting rtl_fm and multimon-ng
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
##
|
||||
#
|
||||
# This Class extended the TimedRotatingFileHandler with the possibility to change the backupCount after initialization.
|
||||
#
|
||||
##
|
||||
class MyTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
|
||||
"""Extended Version of TimedRotatingFileHandler"""
|
||||
def setBackupCount(self, backupCount):
|
||||
"""Set/Change backupCount"""
|
||||
self.backupCount = backupCount
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# convert frequency to Hz
|
||||
#
|
||||
def freqToHz(freq):
|
||||
"""
|
||||
gets a frequency and resolve it in Hz
|
||||
|
||||
@type freq: string
|
||||
@param freq: frequency of the SDR Stick
|
||||
|
||||
@return: frequency in Hz
|
||||
@exception: Exception if Error by recalc
|
||||
"""
|
||||
try:
|
||||
freq = freq.replace("k","e3").replace("M","e6")
|
||||
# freq has to be interpreted as float first...
|
||||
# otherwise you will get the error: an invalid literal for int() with base 10
|
||||
return int(float(freq))
|
||||
except:
|
||||
logging.exception("Error in freqToHz()")
|
||||
from includes import MyTimedRotatingFileHandler # extension of TimedRotatingFileHandler
|
||||
from includes import converter # converter functions
|
||||
from includes import signalHandler # TERM-Handler for use script as a daemon
|
||||
from includes import checkSubprocesses # check startup of the subprocesses
|
||||
|
||||
|
||||
#
|
||||
# ArgParser
|
||||
# Have to be before main program
|
||||
#
|
||||
try:
|
||||
try:
|
||||
# With -h or --help you get the Args help
|
||||
parser = argparse.ArgumentParser(prog="boswatch.py",
|
||||
description="BOSWatch is a Python Script to recive and decode german BOS information with rtl_fm and multimon-NG",
|
||||
parser = argparse.ArgumentParser(prog="boswatch.py",
|
||||
description="BOSWatch is a Python Script to recive and decode german BOS information with rtl_fm and multimon-NG",
|
||||
epilog="More options you can find in the extern config.ini file in the folder /config")
|
||||
# parser.add_argument("-c", "--channel", help="BOS Channel you want to listen")
|
||||
parser.add_argument("-f", "--freq", help="Frequency you want to listen", required=True)
|
||||
|
|
@ -74,247 +46,342 @@ try:
|
|||
parser.add_argument("-e", "--error", help="Frequency-Error of your device in PPM", type=int, default=0)
|
||||
parser.add_argument("-a", "--demod", help="Demodulation functions", choices=['FMS', 'ZVEI', 'POC512', 'POC1200', 'POC2400'], required=True, nargs="+")
|
||||
parser.add_argument("-s", "--squelch", help="Level of squelch", type=int, default=0)
|
||||
parser.add_argument("-u", "--usevarlog", help="Use '/var/log/boswatch' for logfiles instead of subdir 'log' in BOSWatch directory", action="store_true")
|
||||
parser.add_argument("-v", "--verbose", help="Shows more information", action="store_true")
|
||||
parser.add_argument("-q", "--quiet", help="Shows no information. Only logfiles", action="store_true")
|
||||
args = parser.parse_args()
|
||||
# We need this argument for testing (skip instantiate of rtl-fm and multimon-ng):
|
||||
parser.add_argument("-t", "--test", help=argparse.SUPPRESS, action="store_true")
|
||||
args = parser.parse_args()
|
||||
except SystemExit:
|
||||
# -h or --help called, exit right now
|
||||
exit(0)
|
||||
except:
|
||||
print "cannot parsing the arguments"
|
||||
# we couldn't work without arguments -> exit
|
||||
print "ERROR: cannot parsing the arguments"
|
||||
exit(1)
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Main program
|
||||
#
|
||||
try:
|
||||
# initialization
|
||||
# initialization:
|
||||
rtl_fm = None
|
||||
multimon_ng = None
|
||||
|
||||
try:
|
||||
#
|
||||
# Script-pathes
|
||||
#
|
||||
globals.script_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
#
|
||||
# Set log_path
|
||||
#
|
||||
if args.usevarlog:
|
||||
globals.log_path = "/var/log/BOSWatch/"
|
||||
else:
|
||||
globals.log_path = globals.script_path+"/log/"
|
||||
|
||||
#
|
||||
# If necessary create log-path
|
||||
#
|
||||
if not os.path.exists(globals.script_path+"/log/"):
|
||||
os.mkdir(globals.script_path+"/log/")
|
||||
|
||||
#
|
||||
# Create new myLogger...
|
||||
#
|
||||
if not os.path.exists(globals.log_path):
|
||||
os.mkdir(globals.log_path)
|
||||
except:
|
||||
# we couldn't work without logging -> exit
|
||||
print "ERROR: cannot initialize paths"
|
||||
exit(1)
|
||||
|
||||
#
|
||||
# Create new myLogger...
|
||||
#
|
||||
try:
|
||||
myLogger = logging.getLogger()
|
||||
myLogger.setLevel(logging.DEBUG)
|
||||
# set log string format
|
||||
#formatter = logging.Formatter('%(asctime)s - %(module)-15s %(funcName)-15s [%(levelname)-8s] %(message)s', '%d.%m.%Y %H:%M:%S')
|
||||
formatter = logging.Formatter('%(asctime)s - %(module)-15s [%(levelname)-8s] %(message)s', '%d.%m.%Y %H:%M:%S')
|
||||
# create a file logger
|
||||
fh = MyTimedRotatingFileHandler(globals.script_path+"/log/boswatch.log", "midnight", interval=1, backupCount=999)
|
||||
fh = MyTimedRotatingFileHandler.MyTimedRotatingFileHandler(globals.log_path+"boswatch.log", "midnight", interval=1, backupCount=999)
|
||||
# Starts with log level >= Debug
|
||||
# will be changed with config.ini-param later
|
||||
fh.setLevel(logging.DEBUG)
|
||||
fh.setLevel(logging.DEBUG)
|
||||
fh.setFormatter(formatter)
|
||||
myLogger.addHandler(fh)
|
||||
# create a display logger
|
||||
ch = logging.StreamHandler()
|
||||
# log level for display >= info
|
||||
# will be changed later after parsing args
|
||||
ch.setLevel(logging.INFO)
|
||||
ch.setFormatter(formatter)
|
||||
myLogger.addHandler(ch)
|
||||
except:
|
||||
logging.exception("cannot create logger")
|
||||
else:
|
||||
# initialization of the logging was fine, continue...
|
||||
|
||||
try:
|
||||
#
|
||||
# Clear the logfiles
|
||||
#
|
||||
fh.doRollover()
|
||||
rtl_log = open(globals.script_path+"/log/rtl_fm.log", "w")
|
||||
mon_log = open(globals.script_path+"/log/multimon.log", "w")
|
||||
rtl_log.write("")
|
||||
mon_log.write("")
|
||||
rtl_log.close()
|
||||
mon_log.close()
|
||||
logging.debug("BOSWatch has started")
|
||||
logging.debug("Logfiles cleared")
|
||||
except:
|
||||
logging.exception("cannot clear Logfiles")
|
||||
|
||||
try:
|
||||
#
|
||||
# For debug display/log args
|
||||
#
|
||||
logging.debug(" - Frequency: %s", freqToHz(args.freq))
|
||||
logging.debug(" - Device: %s", args.device)
|
||||
logging.debug(" - PPM Error: %s", args.error)
|
||||
logging.debug(" - Squelch: %s", args.squelch)
|
||||
|
||||
demodulation = ""
|
||||
if "FMS" in args.demod:
|
||||
demodulation += "-a FMSFSK "
|
||||
logging.debug(" - Demod: FMS")
|
||||
if "ZVEI" in args.demod:
|
||||
demodulation += "-a ZVEI2 "
|
||||
logging.debug(" - Demod: ZVEI")
|
||||
if "POC512" in args.demod:
|
||||
demodulation += "-a POCSAG512 "
|
||||
logging.debug(" - Demod: POC512")
|
||||
if "POC1200" in args.demod:
|
||||
demodulation += "-a POCSAG1200 "
|
||||
logging.debug(" - Demod: POC1200")
|
||||
if "POC2400" in args.demod:
|
||||
demodulation += "-a POCSAG2400 "
|
||||
logging.debug(" - Demod: POC2400")
|
||||
|
||||
logging.debug(" - Verbose Mode: %s", args.verbose)
|
||||
logging.debug(" - Quiet Mode: %s", args.quiet)
|
||||
|
||||
if args.verbose:
|
||||
ch.setLevel(logging.DEBUG)
|
||||
if args.quiet:
|
||||
ch.setLevel(logging.CRITICAL)
|
||||
|
||||
if not args.quiet: #only if not quiet mode
|
||||
from includes import shellHeader
|
||||
shellHeader.printHeader(args)
|
||||
except:
|
||||
logging.exception("cannot display/log args")
|
||||
|
||||
try:
|
||||
#
|
||||
# Read config.ini
|
||||
#
|
||||
logging.debug("reading config file")
|
||||
globals.config = ConfigParser.ConfigParser()
|
||||
globals.config.read(globals.script_path+"/config/config.ini")
|
||||
# if given loglevel is debug:
|
||||
if globals.config.getint("BOSWatch","loglevel") == 10:
|
||||
logging.debug(" - BOSWatch:")
|
||||
for key,val in globals.config.items("BOSWatch"):
|
||||
logging.debug(" -- %s = %s", key, val)
|
||||
logging.debug(" - FMS:")
|
||||
for key,val in globals.config.items("FMS"):
|
||||
logging.debug(" -- %s = %s", key, val)
|
||||
logging.debug(" - ZVEI:")
|
||||
for key,val in globals.config.items("ZVEI"):
|
||||
logging.debug(" -- %s = %s", key, val)
|
||||
logging.debug(" - POC:")
|
||||
for key,val in globals.config.items("POC"):
|
||||
logging.debug(" -- %s = %s", key, val)
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
# log level for display: Default: info
|
||||
if args.verbose:
|
||||
ch.setLevel(logging.DEBUG)
|
||||
elif args.quiet:
|
||||
ch.setLevel(logging.CRITICAL)
|
||||
else:
|
||||
# initialization was fine, continue with main program...
|
||||
|
||||
try:
|
||||
#
|
||||
# Set the loglevel and backupCount of the file handler
|
||||
#
|
||||
logging.debug("set loglevel of fileHandler to: %s",globals.config.getint("BOSWatch","loglevel") )
|
||||
fh.setLevel(globals.config.getint("BOSWatch","loglevel"))
|
||||
logging.debug("set backupCount of fileHandler to: %s", globals.config.getint("BOSWatch","backupCount"))
|
||||
fh.setBackupCount(globals.config.getint("BOSWatch","backupCount"))
|
||||
except:
|
||||
logging.exception("cannot set loglevel of fileHandler")
|
||||
|
||||
#
|
||||
# Load plugins
|
||||
#
|
||||
from includes import pluginLoader
|
||||
pluginLoader.loadPlugins()
|
||||
|
||||
#
|
||||
# Load filters
|
||||
#
|
||||
if globals.config.getint("BOSWatch","useRegExFilter"):
|
||||
from includes import filter
|
||||
filter.loadFilters()
|
||||
|
||||
#
|
||||
# Load description lists
|
||||
#
|
||||
if globals.config.getint("BOSWatch","useDescription"):
|
||||
from includes import descriptionList
|
||||
descriptionList.loadDescriptionLists()
|
||||
|
||||
try:
|
||||
#
|
||||
# Start rtl_fm
|
||||
#
|
||||
logging.debug("starting rtl_fm")
|
||||
rtl_fm = subprocess.Popen("rtl_fm -d "+str(args.device)+" -f "+str(freqToHz(args.freq))+" -M fm -s 22050 -p "+str(args.error)+" -E DC -F 0 -l "+str(args.squelch)+" -g 100",
|
||||
#stdin=rtl_fm.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=open(globals.script_path+"/log/rtl_fm.log","a"),
|
||||
shell=True)
|
||||
except:
|
||||
logging.exception("cannot start rtl_fm")
|
||||
else:
|
||||
# rtl_fm started, continue...
|
||||
|
||||
try:
|
||||
#
|
||||
# Start multimon
|
||||
#
|
||||
logging.debug("starting multimon-ng")
|
||||
multimon_ng = subprocess.Popen("multimon-ng "+str(demodulation)+" -f alpha -t raw /dev/stdin - ",
|
||||
stdin=rtl_fm.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=open(globals.script_path+"/log/multimon.log","a"),
|
||||
shell=True)
|
||||
except:
|
||||
logging.exception("cannot start multimon-ng")
|
||||
else:
|
||||
# multimon-ng started, continue...
|
||||
|
||||
logging.debug("start decoding")
|
||||
|
||||
while True:
|
||||
#
|
||||
# Get decoded data from multimon-ng and call BOSWatch-decoder
|
||||
#
|
||||
|
||||
# RAW Data from Multimon-NG
|
||||
# ZVEI2: 25832
|
||||
# FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST2=III(mit NA,ohneSIGNAL)) CRC correct\n'
|
||||
# POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World
|
||||
decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout
|
||||
|
||||
# Test-strings only for develop
|
||||
#decoded = "ZVEI2: 25832"
|
||||
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=I (ohneNA,ohneSIGNAL)) CRC correct\n'"
|
||||
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=I (ohneNA,ohneSIGNAL)) CRC correct\n'"
|
||||
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=II (ohneNA,mit SIGNAL)) CRC correct\n'"
|
||||
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=III(mit NA,ohneSIGNAL)) CRC correct\n'"
|
||||
#decoded = "FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=IV (mit NA,mit SIGNAL)) CRC correct\n'"
|
||||
#decoded = "POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World"
|
||||
#time.sleep(1)
|
||||
|
||||
from includes import decoder
|
||||
decoder.decode(freqToHz(args.freq), decoded)
|
||||
|
||||
ch.setLevel(logging.INFO)
|
||||
ch.setFormatter(formatter)
|
||||
myLogger.addHandler(ch)
|
||||
|
||||
except:
|
||||
# we couldn't work without logging -> exit
|
||||
print "ERROR: cannot create logger"
|
||||
exit(1)
|
||||
|
||||
# initialization of the logging was fine, continue...
|
||||
try:
|
||||
#
|
||||
# Clear the logfiles
|
||||
#
|
||||
fh.doRollover()
|
||||
rtl_log = open(globals.log_path+"rtl_fm.log", "w")
|
||||
mon_log = open(globals.log_path+"multimon.log", "w")
|
||||
rtl_log.write("")
|
||||
mon_log.write("")
|
||||
rtl_log.close()
|
||||
mon_log.close()
|
||||
logging.debug("BOSWatch has started")
|
||||
logging.debug("Logfiles cleared")
|
||||
|
||||
except:
|
||||
# It's an error, but we could work without that stuff...
|
||||
logging.error("cannot clear Logfiles")
|
||||
logging.debug("cannot clear Logfiles", exc_info=True)
|
||||
pass
|
||||
|
||||
#
|
||||
# For debug display/log args
|
||||
#
|
||||
try:
|
||||
logging.debug("SW Version: %s",globals.getVers("vers"))
|
||||
logging.debug("Build Date: %s",globals.getVers("date"))
|
||||
logging.debug("BOSWatch given arguments")
|
||||
if args.test:
|
||||
logging.debug(" - Test-Mode!")
|
||||
|
||||
logging.debug(" - Frequency: %s", converter.freqToHz(args.freq))
|
||||
logging.debug(" - Device: %s", args.device)
|
||||
logging.debug(" - PPM Error: %s", args.error)
|
||||
logging.debug(" - Squelch: %s", args.squelch)
|
||||
|
||||
demodulation = ""
|
||||
if "FMS" in args.demod:
|
||||
demodulation += "-a FMSFSK "
|
||||
logging.debug(" - Demod: FMS")
|
||||
if "ZVEI" in args.demod:
|
||||
demodulation += "-a ZVEI2 "
|
||||
logging.debug(" - Demod: ZVEI")
|
||||
if "POC512" in args.demod:
|
||||
demodulation += "-a POCSAG512 "
|
||||
logging.debug(" - Demod: POC512")
|
||||
if "POC1200" in args.demod:
|
||||
demodulation += "-a POCSAG1200 "
|
||||
logging.debug(" - Demod: POC1200")
|
||||
if "POC2400" in args.demod:
|
||||
demodulation += "-a POCSAG2400 "
|
||||
logging.debug(" - Demod: POC2400")
|
||||
|
||||
logging.debug(" - Use /var/log: %s", args.usevarlog)
|
||||
logging.debug(" - Verbose Mode: %s", args.verbose)
|
||||
logging.debug(" - Quiet Mode: %s", args.quiet)
|
||||
|
||||
if not args.quiet: #only if not quiet mode
|
||||
from includes import shellHeader
|
||||
shellHeader.printHeader(args)
|
||||
|
||||
if args.test:
|
||||
logging.warning("!!! We are in Test-Mode !!!")
|
||||
|
||||
except:
|
||||
# we couldn't work without config -> exit
|
||||
logging.critical("cannot display/log args")
|
||||
logging.debug("cannot display/log args", exc_info=True)
|
||||
exit(1)
|
||||
|
||||
#
|
||||
# Read config.ini
|
||||
#
|
||||
try:
|
||||
logging.debug("reading config file")
|
||||
globals.config = ConfigParser.ConfigParser()
|
||||
globals.config.read(globals.script_path+"/config/config.ini")
|
||||
# if given loglevel is debug:
|
||||
if globals.config.getint("BOSWatch","loglevel") == 10:
|
||||
logging.debug(" - BOSWatch:")
|
||||
for key,val in globals.config.items("BOSWatch"):
|
||||
logging.debug(" -- %s = %s", key, val)
|
||||
logging.debug(" - FMS:")
|
||||
for key,val in globals.config.items("FMS"):
|
||||
logging.debug(" -- %s = %s", key, val)
|
||||
logging.debug(" - ZVEI:")
|
||||
for key,val in globals.config.items("ZVEI"):
|
||||
logging.debug(" -- %s = %s", key, val)
|
||||
logging.debug(" - POC:")
|
||||
for key,val in globals.config.items("POC"):
|
||||
logging.debug(" -- %s = %s", key, val)
|
||||
except:
|
||||
# we couldn't work without config -> exit
|
||||
logging.critical("cannot read config file")
|
||||
logging.debug("cannot read config file", exc_info=True)
|
||||
exit(1)
|
||||
|
||||
# initialization was fine, continue with main program...
|
||||
try:
|
||||
#
|
||||
# Set the loglevel and backupCount of the file handler
|
||||
#
|
||||
logging.debug("set loglevel of fileHandler to: %s",globals.config.getint("BOSWatch","loglevel"))
|
||||
fh.setLevel(globals.config.getint("BOSWatch","loglevel"))
|
||||
logging.debug("set backupCount of fileHandler to: %s", globals.config.getint("BOSWatch","backupCount"))
|
||||
fh.setBackupCount(globals.config.getint("BOSWatch","backupCount"))
|
||||
except:
|
||||
# It's an error, but we could work without that stuff...
|
||||
logging.error("cannot set loglevel of fileHandler")
|
||||
logging.debug("cannot set loglevel of fileHandler", exc_info=True)
|
||||
pass
|
||||
|
||||
#
|
||||
# Load plugins
|
||||
#
|
||||
try:
|
||||
from includes import pluginLoader
|
||||
pluginLoader.loadPlugins()
|
||||
except:
|
||||
# we couldn't work without plugins -> exit
|
||||
logging.critical("cannot load Plugins")
|
||||
logging.debug("cannot load Plugins", exc_info=True)
|
||||
exit(1)
|
||||
|
||||
#
|
||||
# Load filters
|
||||
#
|
||||
try:
|
||||
if globals.config.getboolean("BOSWatch","useRegExFilter"):
|
||||
from includes import filter
|
||||
filter.loadFilters()
|
||||
except:
|
||||
# It's an error, but we could work without that stuff...
|
||||
logging.error("cannot load filters")
|
||||
logging.debug("cannot load filters", exc_info=True)
|
||||
pass
|
||||
|
||||
#
|
||||
# Load description lists
|
||||
#
|
||||
try:
|
||||
if globals.config.getboolean("FMS","idDescribed") or globals.config.getboolean("ZVEI","idDescribed") or globals.config.getboolean("POC","idDescribed"):
|
||||
from includes import descriptionList
|
||||
descriptionList.loadDescriptionLists()
|
||||
except:
|
||||
# It's an error, but we could work without that stuff...
|
||||
logging.error("cannot load description lists")
|
||||
logging.debug("cannot load description lists", exc_info=True)
|
||||
pass
|
||||
|
||||
#
|
||||
# Start rtl_fm
|
||||
#
|
||||
try:
|
||||
if not args.test:
|
||||
logging.debug("starting rtl_fm")
|
||||
command = ""
|
||||
if globals.config.has_option("BOSWatch","rtl_path"):
|
||||
command = globals.config.get("BOSWatch","rtl_path")
|
||||
command = command+"rtl_fm -d "+str(args.device)+" -f "+str(converter.freqToHz(args.freq))+" -M fm -s 22050 -p "+str(args.error)+" -E DC -F 0 -l "+str(args.squelch)+" -g 100"
|
||||
rtl_fm = subprocess.Popen(command.split(),
|
||||
#stdin=rtl_fm.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=open(globals.log_path+"rtl_fm.log","a"),
|
||||
shell=False)
|
||||
# rtl_fm doesn't self-destruct, when an error occurs
|
||||
# wait a moment to give the subprocess a chance to write the logfile
|
||||
time.sleep(3)
|
||||
checkSubprocesses.checkRTL()
|
||||
else:
|
||||
logging.warning("!!! Test-Mode: rtl_fm not started !!!")
|
||||
except:
|
||||
# we couldn't work without rtl_fm -> exit
|
||||
logging.critical("cannot start rtl_fm")
|
||||
logging.debug("cannot start rtl_fm", exc_info=True)
|
||||
exit(1)
|
||||
|
||||
#
|
||||
# Start multimon
|
||||
#
|
||||
try:
|
||||
if not args.test:
|
||||
logging.debug("starting multimon-ng")
|
||||
command = ""
|
||||
if globals.config.has_option("BOSWatch","multimon_path"):
|
||||
command = globals.config.get("BOSWatch","multimon_path")
|
||||
command = command+"multimon-ng "+str(demodulation)+" -f alpha -t raw /dev/stdin - "
|
||||
multimon_ng = subprocess.Popen(command.split(),
|
||||
stdin=rtl_fm.stdout,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=open(globals.log_path+"multimon.log","a"),
|
||||
shell=False)
|
||||
# multimon-ng doesn't self-destruct, when an error occurs
|
||||
# wait a moment to give the subprocess a chance to write the logfile
|
||||
time.sleep(3)
|
||||
checkSubprocesses.checkMultimon()
|
||||
else:
|
||||
logging.warning("!!! Test-Mode: multimon-ng not started !!!")
|
||||
except:
|
||||
# we couldn't work without multimon-ng -> exit
|
||||
logging.critical("cannot start multimon-ng")
|
||||
logging.debug("cannot start multimon-ng", exc_info=True)
|
||||
exit(1)
|
||||
|
||||
#
|
||||
# Get decoded data from multimon-ng and call BOSWatch-decoder
|
||||
#
|
||||
if not args.test:
|
||||
logging.debug("start decoding")
|
||||
while True:
|
||||
decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout
|
||||
from includes import decoder
|
||||
decoder.decode(converter.freqToHz(args.freq), decoded)
|
||||
|
||||
else:
|
||||
logging.debug("start testing")
|
||||
testFile = open(globals.script_path+"/testdata/testdata.txt","r")
|
||||
for testData in testFile:
|
||||
if (len(testData.rstrip(' \t\n\r')) > 1) and ("#" not in testData[0]):
|
||||
logging.info("Testdata: %s", testData.rstrip(' \t\n\r'))
|
||||
from includes import decoder
|
||||
decoder.decode(converter.freqToHz(args.freq), testData)
|
||||
time.sleep(1)
|
||||
logging.debug("test finished")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.warning("Keyboard Interrupt")
|
||||
logging.warning("Keyboard Interrupt")
|
||||
except SystemExit:
|
||||
# SystemExitException is thrown if daemon was terminated
|
||||
logging.warning("SystemExit received")
|
||||
# only exit to call finally-block
|
||||
exit()
|
||||
except:
|
||||
logging.exception("unknown error")
|
||||
finally:
|
||||
try:
|
||||
logging.debug("BOSWatch shuting down")
|
||||
rtl_fm.terminate()
|
||||
logging.debug("rtl_fm terminated")
|
||||
multimon_ng.terminate()
|
||||
logging.debug("multimon-ng terminated")
|
||||
logging.debug("exiting BOSWatch")
|
||||
if multimon_ng and multimon_ng.pid:
|
||||
logging.debug("terminate multimon-ng (%s)", multimon_ng.pid)
|
||||
multimon_ng.terminate()
|
||||
multimon_ng.wait()
|
||||
logging.debug("multimon-ng terminated")
|
||||
if rtl_fm and rtl_fm.pid:
|
||||
logging.debug("terminate rtl_fm (%s)", rtl_fm.pid)
|
||||
rtl_fm.terminate()
|
||||
rtl_fm.wait()
|
||||
logging.debug("rtl_fm terminated")
|
||||
logging.debug("exiting BOSWatch")
|
||||
except:
|
||||
logging.warning("failed in clean-up routine")
|
||||
finally:
|
||||
logging.warning("failed in clean-up routine")
|
||||
logging.debug("failed in clean-up routine", exc_info=True)
|
||||
|
||||
finally:
|
||||
# Close Logging
|
||||
logging.debug("close Logging")
|
||||
logging.debug("close Logging")
|
||||
logging.info("BOSWatch exit()")
|
||||
logging.shutdown()
|
||||
fh.close()
|
||||
ch.close()
|
||||
exit(0)
|
||||
|
|
@ -17,49 +17,57 @@ loglevel = 10
|
|||
# backupCount = 7 (keeps logfiles for a week)
|
||||
backupCount = 7
|
||||
|
||||
# if you want to start BOSWatch as a daemon with rc2.d,
|
||||
# you have to set the path to rtl_fm and multimon-ng !
|
||||
# the path have to end with an /
|
||||
#rtl_path = /usr/local/bin/
|
||||
#multimon_path = /usr/local/bin/
|
||||
|
||||
# Using RegEx-Filter (0|1)
|
||||
# Filter-configuration in section [Filters]
|
||||
useRegExFilter = 0
|
||||
|
||||
# Using Description (0|1)
|
||||
# You have to be enabled it for every typ in the sections below too
|
||||
useDescription = 0
|
||||
# for double check save the last n IDs
|
||||
# it is used in combination with double_ignore_time
|
||||
# 1 is required if you want to use the double alarm filter
|
||||
doubleFilter_ignore_entries = 10
|
||||
|
||||
# time to ignore same alarm (only ID is checked) (sek)
|
||||
doubleFilter_ignore_time = 5
|
||||
|
||||
# ignore msg is only usefull for POCSAG (0|1)
|
||||
# 0: double check ignores the msg-text (only check ID + function)
|
||||
# 1: if you want to differentiate between with/ without msg
|
||||
# f.e. if they use quick-alarm (without text, then same ric with msg)
|
||||
# you will get more then one alarm anyway if the msg is different (receiving-problems)
|
||||
doubleFilter_check_msg = 0
|
||||
|
||||
[FMS]
|
||||
# time to ignore same alarm in a row (sek)
|
||||
double_ignore_time = 5
|
||||
|
||||
# look-up-table for adding a description
|
||||
# turn on functionality (0|1)
|
||||
# Using Description (0|1)
|
||||
idDescribed = 0
|
||||
|
||||
[ZVEI]
|
||||
# time to ignore same alarm in a row (sek)
|
||||
double_ignore_time = 5
|
||||
|
||||
# look-up-table for adding a description
|
||||
# turn on functionality (0|1)
|
||||
# Using Description (0|1)
|
||||
idDescribed = 0
|
||||
|
||||
[POC]
|
||||
# time to ignore same alarm in a row (sek)
|
||||
double_ignore_time = 5
|
||||
|
||||
# some very simple filters:
|
||||
# Allow only this RICs (empty: allow all, separator ",")
|
||||
# f.e.: allow_ric = 1234566,1234567,1234568
|
||||
allow_ric =
|
||||
allow_ric =
|
||||
|
||||
# Deny this RICs (empty: allow all, separator ",")
|
||||
# f.e.: deny_ric = 1234566,1234567,1234568
|
||||
deny_ric =
|
||||
deny_ric =
|
||||
|
||||
# start and end of an allowed filter range
|
||||
filter_range_start = 0000000
|
||||
filter_range_end = 9999999
|
||||
|
||||
# look-up-table for adding a description
|
||||
# turn on functionality (0|1)
|
||||
# Using Description (0|1)
|
||||
idDescribed = 0
|
||||
|
||||
|
||||
|
|
@ -88,6 +96,7 @@ httpRequest = 0
|
|||
eMail = 0
|
||||
BosMon = 0
|
||||
firEmergency = 0
|
||||
jsonSocket = 0
|
||||
|
||||
# for developing template-module
|
||||
template = 0
|
||||
|
|
@ -114,27 +123,38 @@ tablePOC = bos_pocsag
|
|||
|
||||
# %FMS% = FMS Code
|
||||
# %STATUS% = FMS Status
|
||||
# %DIR% = Direction of the telegram
|
||||
# %TSI% = Tactical Short Information
|
||||
# %DIR% = Direction of the telegram (0/1)
|
||||
# %DIRT% = Direction of the telegram (Text-String)
|
||||
# %TSI% = Tactical Short Information (I-IV)
|
||||
# %DESCR% = Description from csv-file
|
||||
# %TIME% = Time (by script)
|
||||
# %DATE% = Date (by script)
|
||||
#fms_url = www.google.de?code=%FMS%&stat=%STATUS%
|
||||
fms_url =
|
||||
fms_url =
|
||||
|
||||
# %ZVEI% = ZVEI 5-tone Code
|
||||
# %DESCR% = Description from csv-file
|
||||
# %TIME% = Time (by script)
|
||||
# %DATE% = Date (by script)
|
||||
#zvei_url = www.google.de?zvei=%ZVEI%
|
||||
zvei_url =
|
||||
zvei_url =
|
||||
|
||||
# %RIC% = Pocsag RIC
|
||||
# %FUNC% = Pocsac function/Subric
|
||||
# %FUNC% = Pocsac function/Subric (1-4)
|
||||
# %FUNCCHAR% = Pocsac function/Subric als character (a-d)
|
||||
# %MSG% = Message of the Pocsag telegram
|
||||
# %BITRATE% = Bitrate of the Pocsag telegram
|
||||
# %DESCR% = Description from csv-file
|
||||
# %TIME% = Time (by script)
|
||||
# %DATE% = Date (by script)
|
||||
#poc_url = www.google.de?ric=%RIC%&subric=%FUNC%&msg=%MSG%
|
||||
poc_url =
|
||||
poc_url =
|
||||
|
||||
|
||||
[eMail]
|
||||
# SMTP-Server
|
||||
smtp_server = localhost
|
||||
# Port of SMTP-Server (default:
|
||||
# Port of SMTP-Server (default:
|
||||
smtp_port =
|
||||
# use tls for connection (0|1)
|
||||
tls = 0
|
||||
|
|
@ -156,23 +176,29 @@ priority = urgent
|
|||
# %DIR% = Direction of the telegram (0/1)
|
||||
# %DIRT% = Direction of the telegram (Text-String)
|
||||
# %TSI% = Tactical Short Information (I-IV)
|
||||
# %TIME% = Date/Time (by script)
|
||||
# %DESCR% = Description from csv-file
|
||||
# %TIME% = Time (by script)
|
||||
# %DATE% = Date (by script)
|
||||
fms_subject = FMS: %FMS%
|
||||
fms_message = %TIME%: %FMS% - Status: %STATUS% - Direction: %DIRT% - TSI: %TSI%
|
||||
fms_message = %DATE% %TIME%: %FMS% - Status: %STATUS% - Direction: %DIRT% - TSI: %TSI%
|
||||
|
||||
# %ZVEI% = ZVEI 5-tone Code
|
||||
# %TIME% = Date/Time (by script)
|
||||
# %DESCR% = Description from csv-file
|
||||
# %TIME% = Time (by script)
|
||||
# %DATE% = Date (by script)
|
||||
zvei_subject = Alarm: %ZVEI%
|
||||
zvei_message = %TIME%: %ZVEI%
|
||||
zvei_message = %DATE% %TIME%: %ZVEI%
|
||||
|
||||
# %RIC% = Pocsag RIC
|
||||
# %FUNC% = Pocsac function/Subric (1-4)
|
||||
# %FUNCCHAR% = Pocsac function/Subric als character (a-d)
|
||||
# %MSG% = Message of the Pocsag telegram
|
||||
# %BITRATE% = Bitrate of the Pocsag telegram
|
||||
# %TIME% = Date/Time (by script)
|
||||
# %DESCR% = Description from csv-file
|
||||
# %TIME% = Time (by script)
|
||||
# %DATE% = Date (by script)
|
||||
poc_subject = Alarm: %RIC%%FUNCCHAR%
|
||||
poc_message = %TIME%: %MSG%
|
||||
poc_message = %DATE% %TIME%: %MSG%
|
||||
|
||||
|
||||
[BosMon]
|
||||
|
|
@ -185,8 +211,8 @@ bosmon_port = 80
|
|||
bosmon_channel = channel
|
||||
|
||||
# Use this, when BosMon has restricted access
|
||||
bosmon_user =
|
||||
bosmon_password =
|
||||
bosmon_user =
|
||||
bosmon_password =
|
||||
|
||||
|
||||
[firEmergency]
|
||||
|
|
@ -194,6 +220,13 @@ bosmon_password =
|
|||
firserver = localhost
|
||||
firport = 9001
|
||||
|
||||
[jsonSocket]
|
||||
# Protocol for socket (TCP|UDP)
|
||||
protocol = UDP
|
||||
# Server as IP of DNS-Name (without http://)
|
||||
server = 192.168.0.1
|
||||
port = 8888
|
||||
|
||||
|
||||
#####################
|
||||
##### Not ready yet #
|
||||
|
|
@ -201,4 +234,4 @@ firport = 9001
|
|||
|
||||
[template]
|
||||
test1 = testString
|
||||
test2 = 123456
|
||||
test2 = 123456
|
||||
|
|
|
|||
20
csv/fms.csv
20
csv/fms.csv
|
|
@ -1,10 +1,10 @@
|
|||
fms,description
|
||||
#
|
||||
# BOSWatch CSV file for describing FMS-Addresses
|
||||
#
|
||||
# For each FMS-Address you could set a description-text
|
||||
# Use the structure: fms,"Description-Text"
|
||||
#
|
||||
# !!! DO NOT delete the first line !!!
|
||||
#
|
||||
93377141,"John Q. Publics car"
|
||||
fms,description
|
||||
#
|
||||
# BOSWatch CSV file for describing FMS-Addresses
|
||||
#
|
||||
# For each FMS-Address you could set a description-text
|
||||
# Use the structure: fms,"Description-Text"
|
||||
#
|
||||
# !!! DO NOT delete the first line !!!
|
||||
#
|
||||
12345678,"FMS testdata"
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 2.
|
20
csv/poc.csv
20
csv/poc.csv
|
|
@ -1,10 +1,10 @@
|
|||
ric,description
|
||||
#
|
||||
# BOSWatch CSV file for describing POCSAG-Addresses
|
||||
#
|
||||
# For each RIC-Address you could set a description-text
|
||||
# Use the structure: ric,"Description-Text"
|
||||
#
|
||||
# !!! DO NOT delete the first line !!!
|
||||
#
|
||||
1234567,"John Q. Public"
|
||||
ric,description
|
||||
#
|
||||
# BOSWatch CSV file for describing POCSAG-Addresses
|
||||
#
|
||||
# For each RIC-Address you could set a description-text
|
||||
# Use the structure: ric,"Description-Text"
|
||||
#
|
||||
# !!! DO NOT delete the first line !!!
|
||||
#
|
||||
1234567,"POCSAG testdata"
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 2.
|
20
csv/zvei.csv
20
csv/zvei.csv
|
|
@ -1,10 +1,10 @@
|
|||
zvei,description
|
||||
#
|
||||
# BOSWatch CSV file for describing ZVEI-Addresses
|
||||
#
|
||||
# For each ZVEI-Address you could set a description-text
|
||||
# Use the structure: zvei,"Description-Text"
|
||||
#
|
||||
# !!! DO NOT delete the first line !!!
|
||||
#
|
||||
25832,"John Q. Public"
|
||||
zvei,description
|
||||
#
|
||||
# BOSWatch CSV file for describing ZVEI-Addresses
|
||||
#
|
||||
# For each ZVEI-Address you could set a description-text
|
||||
# Use the structure: zvei,"Description-Text"
|
||||
#
|
||||
# !!! DO NOT delete the first line !!!
|
||||
#
|
||||
12345,"ZVEI testdata"
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 2.
|
18
includes/MyTimedRotatingFileHandler.py
Normal file
18
includes/MyTimedRotatingFileHandler.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
#
|
||||
|
||||
"""
|
||||
This Class extended the TimedRotatingFileHandler with the possibility
|
||||
to change the backupCount after initialization.
|
||||
|
||||
@author: Jens Herrmann
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
class MyTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
|
||||
"""Extended Version of TimedRotatingFileHandler"""
|
||||
def setBackupCount(self, backupCount):
|
||||
"""Set/Change backupCount"""
|
||||
self.backupCount = backupCount
|
||||
|
|
@ -30,9 +30,9 @@ def processAlarm(typ,freq,data):
|
|||
@param data: Contains the parameter
|
||||
|
||||
@requires: active plugins in pluginList
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if Alarm processing failed
|
||||
@exception: Exception if Alarm processing itself failed
|
||||
"""
|
||||
try:
|
||||
logging.debug("[ ALARM ]")
|
||||
|
|
@ -41,14 +41,22 @@ def processAlarm(typ,freq,data):
|
|||
# if enabled use RegEx-filter
|
||||
if globals.config.getint("BOSWatch","useRegExFilter"):
|
||||
from includes import filter
|
||||
if filter.checkFilters(typ,data,pluginName,freq):
|
||||
if filter.checkFilters(typ,data,pluginName,freq):
|
||||
logging.debug("call Plugin: %s", pluginName)
|
||||
plugin.run(typ,freq,data)
|
||||
logging.debug("return from: %s", pluginName)
|
||||
try:
|
||||
plugin.run(typ,freq,data)
|
||||
logging.debug("return from: %s", pluginName)
|
||||
except:
|
||||
# call next plugin, if one has thrown an exception
|
||||
pass
|
||||
else: # RegEX filter off - call plugin directly
|
||||
logging.debug("call Plugin: %s", pluginName)
|
||||
plugin.run(typ,freq,data)
|
||||
logging.debug("return from: %s", pluginName)
|
||||
try:
|
||||
plugin.run(typ,freq,data)
|
||||
logging.debug("return from: %s", pluginName)
|
||||
except:
|
||||
# call next plugin, if one has thrown an exception
|
||||
pass
|
||||
logging.debug("[END ALARM]")
|
||||
except:
|
||||
logging.exception("Error in Alarm processing")
|
||||
logging.exception("Error in alarm processing")
|
||||
|
|
|
|||
54
includes/checkSubprocesses.py
Normal file
54
includes/checkSubprocesses.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
#
|
||||
"""
|
||||
Functions for checking the subprocesses rtl_fm and multimon-ng
|
||||
Used in boswatch.py at startup and designated for watching-service
|
||||
|
||||
@author: Jens Herrmann
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
|
||||
def checkRTL():
|
||||
"""
|
||||
check startup of rtl_fm
|
||||
|
||||
@exception: OSError when rtl_fm returns an error
|
||||
@exception: Exception when checkRTL throws an unexpected error
|
||||
"""
|
||||
try:
|
||||
rtlLog = open(globals.log_path+"rtl_fm.log","r").read()
|
||||
if ("exiting" in rtlLog) or ("Failed to open" in rtlLog):
|
||||
logging.debug("\n%s", rtlLog)
|
||||
raise OSError("starting rtl_fm returns an error")
|
||||
except OSError:
|
||||
raise
|
||||
except:
|
||||
# we couldn't work without rtl_fm
|
||||
logging.critical("cannot check rtl_fm.log")
|
||||
logging.debug("cannot check rtl_fm.log", exc_info=True)
|
||||
raise
|
||||
|
||||
def checkMultimon():
|
||||
"""
|
||||
check startup of multimon-ng
|
||||
|
||||
@exception: OSError when multimon-ng returns an error
|
||||
@exception: Exception when checkMultimon throws an unexpected error
|
||||
"""
|
||||
try:
|
||||
multimonLog = open(globals.log_path+"multimon.log","r").read()
|
||||
if ("invalid" in multimonLog) or ("error" in multimonLog):
|
||||
logging.debug("\n%s", multimonLog)
|
||||
raise OSError("starting multimon-ng returns an error")
|
||||
except OSError:
|
||||
raise
|
||||
except:
|
||||
# we couldn't work without multimon-ng
|
||||
logging.critical("cannot check multimon.log")
|
||||
logging.debug("cannot check multimon.log", exc_info=True)
|
||||
raise
|
||||
29
includes/converter.py
Normal file
29
includes/converter.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
#
|
||||
|
||||
"""
|
||||
convert frequency to Hz
|
||||
|
||||
@author: Bastian Schroll
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
def freqToHz(freq):
|
||||
"""
|
||||
gets a frequency and resolve it in Hz
|
||||
|
||||
@type freq: string
|
||||
@param freq: frequency of the SDR Stick
|
||||
|
||||
@return: frequency in Hz
|
||||
@exception: Exception if Error by recalc
|
||||
"""
|
||||
try:
|
||||
freq = freq.replace("k","e3").replace("M","e6")
|
||||
# freq has to be interpreted as float first...
|
||||
# otherwise you will get the error: an invalid literal for int() with base 10
|
||||
return int(float(freq))
|
||||
except:
|
||||
logging.exception("Error in freqToHz()")
|
||||
|
|
@ -1,53 +1,49 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Search for decode string and call the right decoder function
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: none
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
def decode(freq, decoded):
|
||||
"""
|
||||
Search for decode string and call the right decoder function
|
||||
|
||||
@type freq: string
|
||||
@param freq: frequency of the SDR Stick
|
||||
@type decoded: string
|
||||
@param decoded: RAW Information from Multimon-NG
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if decoder File call failed
|
||||
"""
|
||||
try:
|
||||
# FMS Decoder Section
|
||||
# check FMS: -> check CRC -> validate -> check double alarm -> log
|
||||
if "FMS:" in decoded:
|
||||
logging.debug("recieved FMS")
|
||||
from includes.decoders import fms
|
||||
fms.decode(freq, decoded)
|
||||
|
||||
# ZVEI Decoder Section
|
||||
# check ZVEI: -> validate -> check double alarm -> log
|
||||
elif "ZVEI2:" in decoded:
|
||||
logging.debug("recieved ZVEI")
|
||||
from includes.decoders import zvei
|
||||
zvei.decode(freq, decoded)
|
||||
|
||||
# For POCSAG we have to ignore the multimon-ng line "Enabled demodulators:"
|
||||
elif "Enabled demodulators:" in decoded:
|
||||
pass
|
||||
|
||||
# POCSAG Decoder Section
|
||||
# check POCSAG -> validate -> check double alarm -> log
|
||||
elif "POCSAG" in decoded:
|
||||
logging.debug("recieved POCSAG")
|
||||
from includes.decoders import poc
|
||||
poc.decode(freq, decoded)
|
||||
|
||||
except:
|
||||
logging.exception("cannot start decoder")
|
||||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Search for decode string and call the right decoder function
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: none
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
def decode(freq, decoded):
|
||||
"""
|
||||
Search for decode string and call the right decoder function
|
||||
|
||||
@type freq: string
|
||||
@param freq: frequency of the SDR Stick
|
||||
@type decoded: string
|
||||
@param decoded: RAW Information from Multimon-NG
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if decoder file call failed
|
||||
"""
|
||||
try:
|
||||
# FMS Decoder Section
|
||||
# check FMS: -> check CRC -> validate -> check double alarm -> log
|
||||
if "FMS:" in decoded:
|
||||
logging.debug("recieved FMS")
|
||||
from includes.decoders import fms
|
||||
fms.decode(freq, decoded)
|
||||
|
||||
# ZVEI Decoder Section
|
||||
# check ZVEI: -> validate -> check double alarm -> log
|
||||
elif "ZVEI2:" in decoded:
|
||||
logging.debug("recieved ZVEI")
|
||||
from includes.decoders import zvei
|
||||
zvei.decode(freq, decoded)
|
||||
|
||||
# POCSAG Decoder Section
|
||||
# check POCSAG -> validate -> check double alarm -> log
|
||||
elif "POCSAG512:" in decoded or "POCSAG1200:" in decoded or "POCSAG2400:" in decoded:
|
||||
logging.debug("recieved POCSAG")
|
||||
from includes.decoders import poc
|
||||
poc.decode(freq, decoded)
|
||||
|
||||
except:
|
||||
logging.exception("cannot start decoder")
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ FMS Decoder
|
|||
"""
|
||||
|
||||
import logging # Global logger
|
||||
import time # timestamp for doublealarm
|
||||
import re # Regex for validation
|
||||
|
||||
from includes import globals # Global variables
|
||||
from includes import doubleFilter # double alarm filter
|
||||
|
||||
##
|
||||
#
|
||||
# FMS decoder function
|
||||
# validate -> check double alarm -> log
|
||||
# validate -> check double alarm -> log
|
||||
#
|
||||
def decode(freq, decoded):
|
||||
"""
|
||||
|
|
@ -30,44 +30,46 @@ def decode(freq, decoded):
|
|||
@param decoded: RAW Information from Multimon-NG
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if FMS decode failed
|
||||
"""
|
||||
timestamp = int(time.time()) # Get Timestamp
|
||||
try:
|
||||
fms_service = decoded[19] # Organisation
|
||||
fms_country = decoded[36] # Bundesland
|
||||
fms_location = decoded[65:67] # Ort
|
||||
fms_vehicle = decoded[72:76] # Fahrzeug
|
||||
fms_status = decoded[84] # Status
|
||||
fms_direction = decoded[101] # Richtung
|
||||
fms_directionText = decoded[103:110] # Richtung (Text)
|
||||
fms_tsi = decoded[114:117] # Taktische Kruzinformation
|
||||
|
||||
fms_service = decoded[19] # Organisation
|
||||
fms_country = decoded[36] # Bundesland
|
||||
fms_location = decoded[65:67] # Ort
|
||||
fms_vehicle = decoded[72:76] # Fahrzeug
|
||||
fms_status = decoded[84] # Status
|
||||
fms_direction = decoded[101] # Richtung
|
||||
fms_directionText = decoded[103:110] # Richtung (Text)
|
||||
fms_tsi = decoded[114:117] # Taktische Kruzinformation
|
||||
|
||||
if "CRC correct" in decoded: #check CRC is correct
|
||||
fms_id = fms_service+fms_country+fms_location+fms_vehicle+fms_status+fms_direction # build FMS id
|
||||
# if FMS is valid
|
||||
if re.search("[0-9a-f]{8}[0-9a-f]{1}[01]{1}", fms_id):
|
||||
# check for double alarm
|
||||
if fms_id == globals.fms_id_old and timestamp < globals.fms_time_old + globals.config.getint("FMS", "double_ignore_time"):
|
||||
logging.info("FMS double alarm: %s within %s second(s)", globals.fms_id_old, timestamp-globals.fms_time_old)
|
||||
# in case of double alarm, fms_double_ignore_time set new
|
||||
globals.fms_time_old = timestamp
|
||||
if "CRC correct" in decoded: #check CRC is correct
|
||||
fms_id = fms_service+fms_country+fms_location+fms_vehicle+fms_status+fms_direction # build FMS id
|
||||
# if FMS is valid
|
||||
if re.search("[0-9a-f]{8}[0-9a-f]{1}[01]{1}", fms_id):
|
||||
# check for double alarm
|
||||
if doubleFilter.checkID("FMS", fms_id):
|
||||
logging.info("FMS:%s Status:%s Richtung:%s TSI:%s", fms_id[0:8], fms_status, fms_direction, fms_tsi)
|
||||
data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "directionText":fms_directionText, "tsi":fms_tsi, "description":fms_id[0:8]}
|
||||
# If enabled, look up description
|
||||
if globals.config.getint("FMS", "idDescribed"):
|
||||
from includes import descriptionList
|
||||
data["description"] = descriptionList.getDescription("FMS", fms_id[0:8])
|
||||
# processing the alarm
|
||||
try:
|
||||
from includes import alarmHandler
|
||||
alarmHandler.processAlarm("FMS", freq, data)
|
||||
except:
|
||||
logging.error("processing alarm failed")
|
||||
logging.debug("processing alarm failed", exc_info=True)
|
||||
pass
|
||||
# in every time save old data for double alarm
|
||||
doubleFilter.newEntry(fms_id)
|
||||
else:
|
||||
logging.info("FMS:%s Status:%s Richtung:%s TSI:%s", fms_id[0:8], fms_status, fms_direction, fms_tsi)
|
||||
data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "directionText":fms_directionText, "tsi":fms_tsi, "description":fms_id[0:8]}
|
||||
# If enabled, look up description
|
||||
if globals.config.getint("FMS", "idDescribed"):
|
||||
from includes import descriptionList
|
||||
data["description"] = descriptionList.getDescription("FMS", fms_id[0:8])
|
||||
# processing the alarm
|
||||
from includes import alarmHandler
|
||||
alarmHandler.processAlarm("FMS",freq,data)
|
||||
|
||||
globals.fms_id_old = fms_id #save last id
|
||||
globals.fms_time_old = timestamp #save last time
|
||||
logging.warning("No valid FMS: %s", fms_id)
|
||||
else:
|
||||
logging.warning("No valid FMS: %s", fms_id)
|
||||
else:
|
||||
logging.warning("FMS CRC incorrect")
|
||||
logging.warning("FMS CRC incorrect")
|
||||
except:
|
||||
logging.error("error while decoding")
|
||||
logging.debug("error while decoding", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ POCSAG Decoder
|
|||
"""
|
||||
|
||||
import logging # Global logger
|
||||
import time # timestamp for doublealarm
|
||||
import re # Regex for validation
|
||||
|
||||
from includes import globals # Global variables
|
||||
from includes import doubleFilter # double alarm filter
|
||||
|
||||
##
|
||||
#
|
||||
|
|
@ -28,11 +28,11 @@ def isAllowed(poc_id):
|
|||
@param poc_id: POCSAG Ric
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
|
||||
|
||||
@return: True if the Ric is allowed, other False
|
||||
@exception: none
|
||||
"""
|
||||
# 1.) If allowed RICs is set, only they will path,
|
||||
# 1.) If allowed RICs is set, only they will path,
|
||||
# If RIC is the right one return True, else False
|
||||
if globals.config.get("POC", "allow_ric"):
|
||||
if poc_id in globals.config.get("POC", "allow_ric"):
|
||||
|
|
@ -55,7 +55,7 @@ def isAllowed(poc_id):
|
|||
return True
|
||||
|
||||
##
|
||||
#
|
||||
#
|
||||
# POCSAG decoder function
|
||||
# validate -> check double alarm -> log
|
||||
#
|
||||
|
|
@ -69,62 +69,65 @@ def decode(freq, decoded):
|
|||
@param decoded: RAW Information from Multimon-NG
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if POCSAG decode failed
|
||||
"""
|
||||
bitrate = 0
|
||||
timestamp = int(time.time())#Get Timestamp
|
||||
|
||||
if "POCSAG512:" in decoded:
|
||||
bitrate = 512
|
||||
poc_id = decoded[20:27].replace(" ", "").zfill(7)
|
||||
poc_sub = decoded[39].replace("3", "4").replace("2", "3").replace("1", "2").replace("0", "1")
|
||||
|
||||
elif "POCSAG1200:" in decoded:
|
||||
bitrate = 1200
|
||||
poc_id = decoded[21:28].replace(" ", "").zfill(7)
|
||||
poc_sub = decoded[40].replace("3", "4").replace("2", "3").replace("1", "2").replace("0", "1")
|
||||
|
||||
elif "POCSAG2400:" in decoded:
|
||||
bitrate = 2400
|
||||
poc_id = decoded[21:28].replace(" ", "").zfill(7)
|
||||
poc_sub = decoded[40].replace("3", "4").replace("2", "3").replace("1", "2").replace("0", "1")
|
||||
|
||||
if bitrate is 0:
|
||||
logging.warning("POCSAG Bitrate not found")
|
||||
logging.debug(" - (%s)", decoded)
|
||||
else:
|
||||
logging.debug("POCSAG Bitrate: %s", bitrate)
|
||||
|
||||
if "Alpha:" in decoded: #check if there is a text message
|
||||
poc_text = decoded.split('Alpha: ')[1].strip().rstrip('<EOT>').strip()
|
||||
try:
|
||||
bitrate = 0
|
||||
|
||||
if "POCSAG512:" in decoded:
|
||||
bitrate = 512
|
||||
poc_id = decoded[20:27].replace(" ", "").zfill(7)
|
||||
poc_sub = str(int(decoded[39])+1)
|
||||
|
||||
elif "POCSAG1200:" in decoded:
|
||||
bitrate = 1200
|
||||
poc_id = decoded[21:28].replace(" ", "").zfill(7)
|
||||
poc_sub = str(int(decoded[40])+1)
|
||||
|
||||
elif "POCSAG2400:" in decoded:
|
||||
bitrate = 2400
|
||||
poc_id = decoded[21:28].replace(" ", "").zfill(7)
|
||||
poc_sub = str(int(decoded[40])+1)
|
||||
|
||||
if bitrate is 0:
|
||||
logging.warning("POCSAG Bitrate not found")
|
||||
logging.debug(" - (%s)", decoded)
|
||||
else:
|
||||
poc_text = ""
|
||||
|
||||
if re.search("[0-9]{7}", poc_id): #if POC is valid
|
||||
if isAllowed(poc_id):
|
||||
# check for double alarm
|
||||
if poc_id == globals.poc_id_old and timestamp < globals.poc_time_old + globals.config.getint("POC", "double_ignore_time"):
|
||||
logging.info("POCSAG%s double alarm: %s within %s second(s)", bitrate, globals.poc_id_old, timestamp-globals.poc_time_old)
|
||||
# in case of double alarm, poc_double_ignore_time set new
|
||||
globals.poc_time_old = timestamp
|
||||
else:
|
||||
logging.info("POCSAG%s: %s %s %s ", bitrate, poc_id, poc_sub, poc_text)
|
||||
data = {"ric":poc_id, "function":poc_sub, "msg":poc_text, "bitrate":bitrate, "description":poc_id}
|
||||
# Add function as character a-d to dataset
|
||||
data["functionChar"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d")
|
||||
# If enabled, look up description
|
||||
if globals.config.getint("POC", "idDescribed"):
|
||||
from includes import descriptionList
|
||||
data["description"] = descriptionList.getDescription("POC", poc_id)
|
||||
# processing the alarm
|
||||
from includes import alarmHandler
|
||||
alarmHandler.processAlarm("POC",freq,data)
|
||||
|
||||
globals.poc_id_old = poc_id #save last id
|
||||
globals.poc_time_old = timestamp #save last time
|
||||
logging.debug("POCSAG Bitrate: %s", bitrate)
|
||||
|
||||
if "Alpha:" in decoded: #check if there is a text message
|
||||
poc_text = decoded.split('Alpha: ')[1].strip().rstrip('<EOT>').strip()
|
||||
else:
|
||||
logging.debug("POCSAG%s: %s is not allowed", bitrate, poc_id)
|
||||
else:
|
||||
logging.warning("No valid POCSAG%s RIC: %s", bitrate, poc_id)
|
||||
poc_text = ""
|
||||
|
||||
if re.search("[0-9]{7}", poc_id) and re.search("[1-4]{1}", poc_sub): #if POC is valid
|
||||
if isAllowed(poc_id):
|
||||
# check for double alarm
|
||||
if doubleFilter.checkID("POC", poc_id+poc_sub, poc_text):
|
||||
logging.info("POCSAG%s: %s %s %s ", bitrate, poc_id, poc_sub, poc_text)
|
||||
data = {"ric":poc_id, "function":poc_sub, "msg":poc_text, "bitrate":bitrate, "description":poc_id}
|
||||
# Add function as character a-d to dataset
|
||||
data["functionChar"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d")
|
||||
# If enabled, look up description
|
||||
if globals.config.getint("POC", "idDescribed"):
|
||||
from includes import descriptionList
|
||||
data["description"] = descriptionList.getDescription("POC", poc_id)
|
||||
# processing the alarm
|
||||
try:
|
||||
from includes import alarmHandler
|
||||
alarmHandler.processAlarm("POC", freq, data)
|
||||
except:
|
||||
logging.error("processing alarm failed")
|
||||
logging.debug("processing alarm failed", exc_info=True)
|
||||
pass
|
||||
# in every time save old data for double alarm
|
||||
doubleFilter.newEntry(poc_id+poc_sub, poc_text)
|
||||
else:
|
||||
logging.debug("POCSAG%s: %s is not allowed", bitrate, poc_id)
|
||||
else:
|
||||
logging.warning("No valid POCSAG%s RIC: %s SUB: %s", bitrate, poc_id, poc_sub)
|
||||
except:
|
||||
logging.error("error while decoding")
|
||||
logging.debug("error while decoding", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ ZVEI Decoder
|
|||
"""
|
||||
|
||||
import logging # Global logger
|
||||
import time # timestamp for doublealarm
|
||||
import re # Regex for validation
|
||||
|
||||
from includes import globals # Global variables
|
||||
from includes import doubleFilter # double alarm filter
|
||||
|
||||
##
|
||||
#
|
||||
|
|
@ -25,7 +25,7 @@ def removeF(zvei):
|
|||
|
||||
@type zvei: string
|
||||
@param zvei: ZVEI Information
|
||||
|
||||
|
||||
@return: ZVEI without F
|
||||
@exception: none
|
||||
"""
|
||||
|
|
@ -40,7 +40,7 @@ def removeF(zvei):
|
|||
##
|
||||
#
|
||||
# ZVEI decoder function
|
||||
# validate -> check double alarm -> log
|
||||
# validate -> check double alarm -> log
|
||||
#
|
||||
def decode(freq, decoded):
|
||||
"""
|
||||
|
|
@ -52,32 +52,34 @@ def decode(freq, decoded):
|
|||
@param decoded: RAW Information from Multimon-NG
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if ZVEI decode failed
|
||||
"""
|
||||
timestamp = int(time.time()) # Get Timestamp
|
||||
|
||||
zvei_id = decoded[7:12] # ZVEI Code
|
||||
zvei_id = removeF(zvei_id) # resolve F
|
||||
if re.search("[0-9]{5}", zvei_id): # if ZVEI is valid
|
||||
# check for double alarm
|
||||
if zvei_id == globals.zvei_id_old and timestamp < globals.zvei_time_old + globals.config.getint("ZVEI", "double_ignore_time"):
|
||||
logging.info("ZVEI double alarm: %s within %s second(s)", globals.zvei_id_old, timestamp-globals.zvei_time_old)
|
||||
# in case of double alarm, zvei_double_ignore_time set new
|
||||
globals.zvei_time_old = timestamp
|
||||
try:
|
||||
zvei_id = decoded[7:12] # ZVEI Code
|
||||
zvei_id = removeF(zvei_id) # resolve F
|
||||
if re.search("[0-9]{5}", zvei_id): # if ZVEI is valid
|
||||
# check for double alarm
|
||||
if doubleFilter.checkID("ZVEI", zvei_id):
|
||||
logging.info("5-Ton: %s", zvei_id)
|
||||
data = {"zvei":zvei_id, "description":zvei_id}
|
||||
# If enabled, look up description
|
||||
if globals.config.getint("ZVEI", "idDescribed"):
|
||||
from includes import descriptionList
|
||||
data["description"] = descriptionList.getDescription("ZVEI", zvei_id)
|
||||
# processing the alarm
|
||||
try:
|
||||
from includes import alarmHandler
|
||||
alarmHandler.processAlarm("ZVEI", freq, data)
|
||||
except:
|
||||
logging.error("processing alarm failed")
|
||||
logging.debug("processing alarm failed", exc_info=True)
|
||||
pass
|
||||
# in every time save old data for double alarm
|
||||
doubleFilter.newEntry(zvei_id)
|
||||
else:
|
||||
logging.info("5-Ton: %s", zvei_id)
|
||||
data = {"zvei":zvei_id, "description":zvei_id}
|
||||
# If enabled, look up description
|
||||
if globals.config.getint("ZVEI", "idDescribed"):
|
||||
from includes import descriptionList
|
||||
data["description"] = descriptionList.getDescription("ZVEI", zvei_id)
|
||||
# processing the alarm
|
||||
from includes import alarmHandler
|
||||
alarmHandler.processAlarm("ZVEI",freq,data)
|
||||
|
||||
globals.zvei_id_old = zvei_id # save last id
|
||||
globals.zvei_time_old = timestamp # save last time
|
||||
else:
|
||||
logging.warning("No valid ZVEI: %s", zvei_id)
|
||||
logging.warning("No valid ZVEI: %s", zvei_id)
|
||||
except:
|
||||
logging.error("error while decoding")
|
||||
logging.debug("error while decoding", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -1,104 +1,114 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Function to expand the dataset with a description.
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
import csv # for loading the description files
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
##
|
||||
#
|
||||
# Local function will load the csv-file
|
||||
#
|
||||
def loadCSV(typ, idField):
|
||||
"""
|
||||
Local function for loading csv-file into python list
|
||||
Structure: [id] = description
|
||||
|
||||
@return: Python list of descriptions
|
||||
@exception: Exception if loading failed
|
||||
"""
|
||||
resultList = {}
|
||||
try:
|
||||
logging.debug("-- loading %s.csv", typ)
|
||||
with open(globals.script_path+'/csv/'+typ+'.csv') as csvfile:
|
||||
# DictReader expected structure described in first line of csv-file
|
||||
reader = csv.DictReader(csvfile)
|
||||
for row in reader:
|
||||
logging.debug(row)
|
||||
# only import rows with an integer as id
|
||||
if row[idField].isdigit() == True:
|
||||
resultList[row[idField]] = row['description']
|
||||
logging.debug("-- loading csv finished")
|
||||
except:
|
||||
logging.exception("loading csvList for typ: %s failed", typ)
|
||||
return resultList;
|
||||
|
||||
##
|
||||
#
|
||||
# call this for loading the description lists
|
||||
#
|
||||
def loadDescriptionLists():
|
||||
"""
|
||||
Load data from the csv-files in global description list for FMS, ZVEI and POCSAG
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if loading failed
|
||||
"""
|
||||
try:
|
||||
logging.debug("loading description lists")
|
||||
|
||||
if globals.config.getint("FMS", "idDescribed"):
|
||||
logging.debug("- load FMS description list")
|
||||
globals.fmsDescribtionList = loadCSV("fms", "fms")
|
||||
|
||||
if globals.config.getint("ZVEI", "idDescribed"):
|
||||
logging.debug("- load ZVEI description list")
|
||||
globals.zveiDescribtionList = loadCSV("zvei", "zvei")
|
||||
|
||||
if globals.config.getint("POC", "idDescribed"):
|
||||
logging.debug("- load pocsag description list")
|
||||
globals.ricDescribtionList = loadCSV("poc", "ric")
|
||||
|
||||
except:
|
||||
logging.exception("cannot load description lists")
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# public function for getting a description
|
||||
#
|
||||
def getDescription(typ, id):
|
||||
"""
|
||||
Get description for id.
|
||||
Will return id if no description will be found.
|
||||
|
||||
@return: description as string
|
||||
"""
|
||||
resultStr = id;
|
||||
logging.debug("look up description lists")
|
||||
try:
|
||||
if typ == "FMS":
|
||||
resultStr = globals.fmsDescribtionList[id]
|
||||
elif typ == "ZVEI":
|
||||
resultStr = globals.zveiDescribtionList[id]
|
||||
elif typ == "POC":
|
||||
resultStr = globals.ricDescribtionList[id]
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
except KeyError:
|
||||
# will be thrown when there is no description for the id
|
||||
# -> nothing to do...
|
||||
pass
|
||||
except:
|
||||
logging.debug("Error during look up description lists")
|
||||
return resultStr
|
||||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Function to expand the dataset with a description.
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
import csv # for loading the description files
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
##
|
||||
#
|
||||
# Local function will load the csv-file
|
||||
#
|
||||
def loadCSV(typ, idField):
|
||||
"""
|
||||
Local function for loading csv-file into python list
|
||||
Structure: [id] = description
|
||||
|
||||
@return: Python list of descriptions
|
||||
"""
|
||||
resultList = {}
|
||||
try:
|
||||
logging.debug("-- loading %s.csv", typ)
|
||||
with open(globals.script_path+'/csv/'+typ+'.csv') as csvfile:
|
||||
# DictReader expected structure described in first line of csv-file
|
||||
reader = csv.DictReader(csvfile)
|
||||
for row in reader:
|
||||
logging.debug(row)
|
||||
# only import rows with an integer as id
|
||||
if row[idField].isdigit() == True:
|
||||
resultList[row[idField]] = row['description']
|
||||
logging.debug("-- loading csv finished")
|
||||
except:
|
||||
logging.error("loading csvList for typ: %s failed", typ)
|
||||
logging.debug("loading csvList for typ: %s failed", typ, exc_info=True)
|
||||
raise
|
||||
return resultList;
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# call this for loading the description lists
|
||||
#
|
||||
def loadDescriptionLists():
|
||||
"""
|
||||
Load data from the csv-files in global description list for FMS, ZVEI and POCSAG
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if loading failed
|
||||
"""
|
||||
try:
|
||||
logging.debug("loading description lists")
|
||||
|
||||
if globals.config.getint("FMS", "idDescribed"):
|
||||
logging.debug("- load FMS description list")
|
||||
globals.fmsDescribtionList = loadCSV("fms", "fms")
|
||||
|
||||
if globals.config.getint("ZVEI", "idDescribed"):
|
||||
logging.debug("- load ZVEI description list")
|
||||
globals.zveiDescribtionList = loadCSV("zvei", "zvei")
|
||||
|
||||
if globals.config.getint("POC", "idDescribed"):
|
||||
logging.debug("- load pocsag description list")
|
||||
globals.ricDescribtionList = loadCSV("poc", "ric")
|
||||
|
||||
except:
|
||||
logging.error("cannot load description lists")
|
||||
logging.debug("cannot load description lists", exc_info=True)
|
||||
pass
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# public function for getting a description
|
||||
#
|
||||
def getDescription(typ, id):
|
||||
"""
|
||||
Get description for id.
|
||||
Will return id if no description will be found.
|
||||
|
||||
@return: description as string
|
||||
"""
|
||||
resultStr = id;
|
||||
logging.debug("look up description lists")
|
||||
try:
|
||||
if typ == "FMS":
|
||||
resultStr = globals.fmsDescribtionList[id]
|
||||
elif typ == "ZVEI":
|
||||
resultStr = globals.zveiDescribtionList[id]
|
||||
elif typ == "POC":
|
||||
resultStr = globals.ricDescribtionList[id]
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
||||
except KeyError:
|
||||
# will be thrown when there is no description for the id
|
||||
# -> nothing to do...
|
||||
pass
|
||||
|
||||
except:
|
||||
logging.error("Error during look up description lists")
|
||||
logging.debug("Error during look up description lists", exc_info=True)
|
||||
pass
|
||||
|
||||
logging.debug(" - result for %s: %s", id, resultStr)
|
||||
return resultStr
|
||||
|
|
|
|||
65
includes/doubleFilter.py
Normal file
65
includes/doubleFilter.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
doubleFilter is the central function to filter out double alarms.
|
||||
You can set the number of historical entries the filter will check
|
||||
and the time ignoring the id in case of a double alarm
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
import time # timestamp for doublealarm
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
#
|
||||
# ListStructure [0..n] = (ID, TimeStamp, msg)
|
||||
#
|
||||
|
||||
def checkID(typ, id, msg=""):
|
||||
"""
|
||||
check if id was called in the last x sec and n entries
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
|
||||
@return: True if check was OK
|
||||
@return: False if double was found
|
||||
"""
|
||||
timestamp = int(time.time()) # Get Timestamp
|
||||
|
||||
for i in range(len(globals.doubleList)):
|
||||
(xID, xTimestamp, xMsg) = globals.doubleList[i]
|
||||
# given ID found?
|
||||
# return False if the first entry in double_ignore_time is found, we will not check for younger ones...
|
||||
if id == xID and timestamp < xTimestamp + globals.config.getint("BOSWatch", "doubleFilter_ignore_time"):
|
||||
# if wanted, we have to check the msg additional
|
||||
if "POC" in typ and globals.config.getint("BOSWatch", "doubleFilter_check_msg"):
|
||||
# if msg is a substring of xMsg we found a double
|
||||
if msg in xMsg:
|
||||
logging.info("%s double alarm (id+msg): %s within %s second(s)", typ, xID, timestamp-xTimestamp)
|
||||
return False
|
||||
else:
|
||||
logging.info("%s double alarm (id): %s within %s second(s)", typ, xID, timestamp-xTimestamp)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def newEntry(id, msg = ""):
|
||||
"""
|
||||
new entry in double alarm list
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
timestamp = int(time.time()) # Get Timestamp
|
||||
globals.doubleList.append((id, timestamp, msg))
|
||||
|
||||
logging.debug("Added %s to doubleList", id)
|
||||
|
||||
# now check if list has more than n entries:
|
||||
if len(globals.doubleList) > globals.config.getint("BOSWatch", "doubleFilter_ignore_entries"):
|
||||
# we have to kill the oldest one
|
||||
globals.doubleList.pop(0)
|
||||
|
|
@ -15,15 +15,16 @@ import re #Regex for Filter Check
|
|||
|
||||
from includes import globals # Global variables
|
||||
|
||||
from includes import converter # converter functions
|
||||
|
||||
|
||||
def loadFilters():
|
||||
"""
|
||||
load all filters from the config.ini into globals.filterList
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if filter loading failed
|
||||
"""
|
||||
try:
|
||||
logging.debug("loading filters")
|
||||
|
|
@ -31,17 +32,24 @@ def loadFilters():
|
|||
for key,val in globals.config.items("Filters"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
filter = val.split(";")
|
||||
|
||||
# resolve the * for freqToHz()
|
||||
if not filter[3] == "*":
|
||||
filter[3] = converter.freqToHz(filter[3])
|
||||
|
||||
# insert splitet data into globals.filterList
|
||||
globals.filterList.append({"name": key, "typ": filter[0], "dataField": filter[1], "plugin": filter[2], "freq": freqToHz(filter[3]), "regex": filter[4]})
|
||||
globals.filterList.append({"name": key, "typ": filter[0], "dataField": filter[1], "plugin": filter[2], "freq": filter[3], "regex": filter[4]})
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
|
||||
|
||||
logging.error("cannot read config file")
|
||||
logging.debug("cannot read config file", exc_info=True)
|
||||
return
|
||||
|
||||
|
||||
def checkFilters(typ,data,plugin,freq):
|
||||
"""
|
||||
Check the Typ/Plugin combination with the RegEX filter
|
||||
If no filter for the combination is found, function returns True.
|
||||
|
||||
|
||||
@type typ: string (FMS|ZVEI|POC)
|
||||
@param typ: Typ of the dataset
|
||||
@type data: map of data (structure see interface.txt)
|
||||
|
|
@ -50,15 +58,14 @@ def checkFilters(typ,data,plugin,freq):
|
|||
@param plugin: Name of the plugin to checked
|
||||
@type freq: string
|
||||
@param freq: frequency of the SDR Stick
|
||||
|
||||
|
||||
@requires: all filters in the filterList
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if filter check failed
|
||||
"""
|
||||
try:
|
||||
logging.debug("search Filter for %s to %s at %s Hz", typ, plugin, freq)
|
||||
|
||||
|
||||
foundFilter = False
|
||||
# go to all filter in globals.filterList
|
||||
for i in globals.filterList:
|
||||
|
|
@ -72,13 +79,16 @@ def checkFilters(typ,data,plugin,freq):
|
|||
return True
|
||||
else:
|
||||
logging.debug("Filter not passed: %s", i["name"])
|
||||
|
||||
|
||||
if foundFilter:
|
||||
logging.debug("no Filter passed")
|
||||
return False
|
||||
else:
|
||||
logging.debug("no Filter found")
|
||||
return True
|
||||
|
||||
|
||||
except:
|
||||
logging.exception("Error in Filter checking")
|
||||
logging.error("Error in filter checking")
|
||||
logging.debug("Error in filter checking", exc_info=True)
|
||||
# something goes wrong, data will path
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -11,16 +11,10 @@ Global variables
|
|||
# Global variables
|
||||
config = 0
|
||||
script_path = ""
|
||||
log_path = ""
|
||||
|
||||
# double alarm
|
||||
fms_id_old = 0
|
||||
fms_time_old = 0
|
||||
|
||||
zvei_id_old = 0
|
||||
zvei_time_old = 0
|
||||
|
||||
poc_id_old = 0
|
||||
poc_time_old = 0
|
||||
doubleList = []
|
||||
|
||||
# pluginLoader
|
||||
pluginList = {}
|
||||
|
|
@ -33,3 +27,10 @@ fmsDescribtionList = {}
|
|||
zveiDescribtionList = {}
|
||||
ricDescribtionList = {}
|
||||
|
||||
# returns the version or build date
|
||||
# function -> read only in script
|
||||
def getVers(mode="vers"):
|
||||
if mode == "vers":
|
||||
return "2.0"
|
||||
elif mode == "date":
|
||||
return " 2015/07/13"
|
||||
|
|
|
|||
0
includes/helper/__init__.py
Normal file
0
includes/helper/__init__.py
Normal file
37
includes/helper/configHandler.py
Normal file
37
includes/helper/configHandler.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
#
|
||||
|
||||
"""
|
||||
little Helper to handle config data
|
||||
for direct use in plugins to save code
|
||||
|
||||
@author: Bastian Schroll
|
||||
"""
|
||||
|
||||
import logging
|
||||
from includes import globals
|
||||
|
||||
|
||||
def checkConfig(section=""):
|
||||
"""
|
||||
Reads the config option from an section and prints it to debug log
|
||||
|
||||
@type section: string
|
||||
@param section: Section name from config.ini
|
||||
|
||||
@return: true (false if reading failed)
|
||||
@exception: Exception if Error at read an debug
|
||||
"""
|
||||
try:
|
||||
if section is not "": # read only data if section is given
|
||||
logging.debug("read [%s] from config file", section)
|
||||
|
||||
for key,val in globals.config.items(section):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
|
||||
return True
|
||||
except:
|
||||
logging.warning("error in config read/debug")
|
||||
logging.debug("error in config read/debug", exc_info=True)
|
||||
return False
|
||||
57
includes/helper/timeHandler.py
Normal file
57
includes/helper/timeHandler.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
#
|
||||
|
||||
"""
|
||||
little Helper to get easy the curent date or time
|
||||
for direct use in plugins to save code
|
||||
|
||||
@author: Bastian Schroll
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
import time
|
||||
|
||||
|
||||
def curtime(format="%d.%m.%Y %H:%M:%S"):
|
||||
"""
|
||||
Returns formated date and/or time
|
||||
see: https://docs.python.org/2/library/time.html#time.strftime
|
||||
|
||||
@type format: string
|
||||
@param format: Python time Format-String
|
||||
|
||||
@return: Formated Time and/or Date
|
||||
@exception: Exception if Error in format
|
||||
"""
|
||||
try:
|
||||
return time.strftime(format)
|
||||
except:
|
||||
logging.warning("error in time-format-string")
|
||||
logging.debug("error in time-format-string", exc_info=True)
|
||||
|
||||
|
||||
def getDate():
|
||||
"""
|
||||
Returns the date
|
||||
|
||||
@return: Formated date
|
||||
"""
|
||||
return curtime("%d.%m.%Y")
|
||||
|
||||
def getTime():
|
||||
"""
|
||||
Returns the time
|
||||
|
||||
@return: Formated time
|
||||
"""
|
||||
return curtime("%H:%M:%S")
|
||||
|
||||
def getTimestamp():
|
||||
"""
|
||||
Returns a integer timestamp
|
||||
|
||||
@return: integer timestamp
|
||||
"""
|
||||
return int(time.time())
|
||||
59
includes/helper/wildcardHandler.py
Normal file
59
includes/helper/wildcardHandler.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
#
|
||||
|
||||
"""
|
||||
little Helper to replace fast and easy the standard wildcards
|
||||
for direct use in plugins to save code
|
||||
|
||||
@author: Bastian Schroll
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from includes.helper import timeHandler
|
||||
|
||||
|
||||
def replaceWildcards(text,data):
|
||||
"""
|
||||
Replace all official Wildcards with the Information from the data[] var
|
||||
|
||||
@type text: string
|
||||
@param text: Input text with wildcards
|
||||
@type data: map
|
||||
@param data: map of data (structure see interface.txt)
|
||||
|
||||
@return: text with replaced wildcards
|
||||
@exception: Exception if Error at replace
|
||||
"""
|
||||
try:
|
||||
# replace date and time wildcards
|
||||
text = text.replace("%TIME%", timeHandler.getTime()).replace("%DATE%", timeHandler.getDate())
|
||||
|
||||
# replace FMS data
|
||||
if "fms" in data: text = text.replace("%FMS%", data["fms"])
|
||||
if "status" in data: text = text.replace("%STATUS%", data["status"])
|
||||
if "direction" in data: text = text.replace("%DIR%", data["direction"])
|
||||
if "directionText" in data: text = text.replace("%DIRT%", data["directionText"])
|
||||
if "tsi" in data: text = text.replace("%TSI%", data["tsi"])
|
||||
|
||||
# replace ZVEI data
|
||||
if "zvei" in data: text = text.replace("%ZVEI%", data["zvei"])
|
||||
|
||||
# replace POC data
|
||||
if "ric" in data: text = text.replace("%RIC%", data["ric"])
|
||||
if "function" in data: text = text.replace("%FUNC%", data["function"])
|
||||
if "functionChar" in data: text = text.replace("%FUNCCHAR%", data["functionChar"])
|
||||
if "msg" in data: text = text.replace("%MSG%", data["msg"])
|
||||
if "bitrate" in data: text = text.replace("%BITRATE%", str(data["bitrate"]))
|
||||
|
||||
# replace description (exists by all)
|
||||
if "description" in data: text = text.replace("%DESCR%", data["description"])
|
||||
|
||||
logging.debug("wildcards been replaced")
|
||||
|
||||
return text
|
||||
|
||||
except:
|
||||
logging.warning("error in wildcard replacement")
|
||||
logging.debug("error in wildcard replacement", exc_info=True)
|
||||
|
|
@ -18,7 +18,7 @@ from includes import globals # Global variables
|
|||
|
||||
def loadPlugins():
|
||||
"""
|
||||
Load all Plugins into globals.pluginList
|
||||
Load all plugins into globals.pluginList
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if insert into globals.pluginList failed
|
||||
|
|
@ -27,35 +27,54 @@ def loadPlugins():
|
|||
logging.debug("loading plugins")
|
||||
# go to all Plugins from getPlugins()
|
||||
for i in getPlugins():
|
||||
# call for each Plugin the loadPlugin() Methode
|
||||
plugin = loadPlugin(i)
|
||||
# Add it to globals.pluginList
|
||||
globals.pluginList[i["name"]] = plugin
|
||||
try:
|
||||
# call for each Plugin the loadPlugin() Methode
|
||||
plugin = loadPlugin(i)
|
||||
except:
|
||||
# call next plugin, if one has thrown an exception
|
||||
logging.error("error loading plugin: %s", i["name"])
|
||||
logging.debug("error loading plugin: %s", i["name"], exc_info=True)
|
||||
pass
|
||||
else: # only call onLoad() and insert into pluginList[] if import is succesfull
|
||||
|
||||
try:
|
||||
# Try to call the .onLoad() routine for all active plugins
|
||||
logging.debug("call %s.onLoad()", i["name"])
|
||||
plugin.onLoad()
|
||||
# Add it to globals.pluginList
|
||||
globals.pluginList[i["name"]] = plugin
|
||||
except:
|
||||
# call next plugin, if one has thrown an exception
|
||||
logging.error("error calling %s.onLoad()", i["name"])
|
||||
logging.debug("error calling %s.onLoad()", exc_info=True)
|
||||
pass
|
||||
except:
|
||||
logging.exception("cannot load Plugins")
|
||||
logging.error("cannot load plugins")
|
||||
logging.debug("cannot load plugins", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
def getPlugins():
|
||||
"""
|
||||
get a Python Dict of all activeated Plugins
|
||||
get a Python Dict of all activeated plugins
|
||||
|
||||
@return: Plugins as Python Dict
|
||||
@exception: Exception if Plugin search failed
|
||||
@return: plugins as Python Dict
|
||||
@exception: Exception if plugin search failed
|
||||
"""
|
||||
try:
|
||||
logging.debug("Search in Plugin Folder")
|
||||
logging.debug("Search in plugin folder")
|
||||
PluginFolder = globals.script_path+"/plugins"
|
||||
plugins = []
|
||||
# Go to all Folders in the Plugin-Dir
|
||||
for i in os.listdir(PluginFolder):
|
||||
location = os.path.join(PluginFolder, i)
|
||||
|
||||
# Skip if Path.isdir() or no File DIR_NAME.py is found
|
||||
|
||||
# Skip if Path.isdir() or no File DIR_NAME.py is found
|
||||
if not os.path.isdir(location) or not i + ".py" in os.listdir(location):
|
||||
continue
|
||||
|
||||
# is the plugin enabled in the config-file?
|
||||
try:
|
||||
try:
|
||||
if globals.config.getint("Plugins", i):
|
||||
info = imp.find_module(i, [location])
|
||||
plugins.append({"name": i, "info": info})
|
||||
|
|
@ -63,28 +82,32 @@ def getPlugins():
|
|||
else:
|
||||
logging.debug("Plugin [DISABLED] %s ", i)
|
||||
# no entry for plugin found in config-file
|
||||
except NoOptionError:
|
||||
logging.warning("Plugin [NO CONF ] %s", i)
|
||||
except NoOptionError:
|
||||
logging.warning("Plugin [NO CONF ] %s", i)
|
||||
pass
|
||||
except:
|
||||
logging.exception("Error during Plugin search")
|
||||
logging.error("Error during plugin search")
|
||||
logging.debug("Error during plugin search", exc_info=True)
|
||||
raise
|
||||
|
||||
return plugins
|
||||
|
||||
|
||||
def loadPlugin(plugin):
|
||||
"""
|
||||
Imports a single Plugin
|
||||
Imports a single plugin
|
||||
|
||||
@type plugin: plugin Data
|
||||
@param plugin: Contains the information to import a plugin
|
||||
|
||||
|
||||
@type plugin: Plugin Data
|
||||
@param plugin: Contains the information to import a Plugin
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if Plugin import failed
|
||||
@exception: Exception if plugin import failed
|
||||
"""
|
||||
try:
|
||||
logging.debug("load Plugin: %s", plugin["name"])
|
||||
logging.debug("load plugin: %s", plugin["name"])
|
||||
return imp.load_module(plugin["name"], *plugin["info"])
|
||||
except:
|
||||
logging.exception("cannot load Plugin: %s", plugin["name"])
|
||||
logging.error("cannot load plugin: %s", plugin["name"])
|
||||
logging.debug("cannot load plugin: %s", plugin["name"], exc_info=True)
|
||||
raise
|
||||
|
|
|
|||
|
|
@ -1,52 +1,59 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Shows the header in shell if quiet mode is not active
|
||||
|
||||
@author: Bastian Schroll
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: none
|
||||
"""
|
||||
|
||||
def printHeader(args):
|
||||
"""
|
||||
Prints the header to the shell
|
||||
|
||||
@type args: Array
|
||||
@param args: All given arguments from argsparser
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if display of the shell header failed
|
||||
"""
|
||||
try:
|
||||
print " ____ ____ ______ __ __ __ "
|
||||
print " / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ b"
|
||||
print " / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ e"
|
||||
print " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / t"
|
||||
print " /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ a"
|
||||
print " German BOS Information Script "
|
||||
print " by Bastian Schroll "
|
||||
print ""
|
||||
|
||||
print "Frequency: "+args.freq
|
||||
print "Device-ID: "+str(args.device)
|
||||
print "Error in PPM: "+str(args.error)
|
||||
print "Active Demods: "+str(len(args.demod))
|
||||
if "FMS" in args.demod:
|
||||
print "- FMS"
|
||||
if "ZVEI" in args.demod:
|
||||
print "- ZVEI"
|
||||
if "POC512" in args.demod:
|
||||
print "- POC512"
|
||||
if "POC1200" in args.demod:
|
||||
print "- POC1200"
|
||||
if "POC2400" in args.demod:
|
||||
print "- POC2400"
|
||||
print "Squelch: "+str(args.squelch)
|
||||
if args.verbose:
|
||||
print "Verbose Mode!"
|
||||
print ""
|
||||
except:
|
||||
logging.exception("cannot display shell header")
|
||||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Shows the header in shell if quiet mode is not active
|
||||
|
||||
@author: Bastian Schroll
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: none
|
||||
"""
|
||||
|
||||
from includes import globals
|
||||
|
||||
def printHeader(args):
|
||||
"""
|
||||
Prints the header to the shell
|
||||
|
||||
@type args: Array
|
||||
@param args: All given arguments from argsparser
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
try:
|
||||
print " ____ ____ ______ __ __ __ "
|
||||
print " / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ "
|
||||
print " / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ "
|
||||
print " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / "
|
||||
print " /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ "
|
||||
print " German BOS Information Script "
|
||||
print " by Bastian Schroll, Jens Herrmann "
|
||||
print ""
|
||||
print "SW Version: "+globals.getVers("vers")
|
||||
print "Build Date: "+globals.getVers("date")
|
||||
print ""
|
||||
|
||||
print "Frequency: "+args.freq
|
||||
print "Device-ID: "+str(args.device)
|
||||
print "Error in PPM: "+str(args.error)
|
||||
print "Active Demods: "+str(len(args.demod))
|
||||
if "FMS" in args.demod:
|
||||
print "- FMS"
|
||||
if "ZVEI" in args.demod:
|
||||
print "- ZVEI"
|
||||
if "POC512" in args.demod:
|
||||
print "- POC512"
|
||||
if "POC1200" in args.demod:
|
||||
print "- POC1200"
|
||||
if "POC2400" in args.demod:
|
||||
print "- POC2400"
|
||||
print "Squelch: "+str(args.squelch)
|
||||
if args.verbose:
|
||||
print "Verbose Mode!"
|
||||
if args.test:
|
||||
print "Test Mode!"
|
||||
print ""
|
||||
except:
|
||||
logging.error("cannot display shell header")
|
||||
logging.debug("cannot display shell header", exc_info=True)
|
||||
|
|
|
|||
32
includes/signalHandler.py
Normal file
32
includes/signalHandler.py
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
#
|
||||
|
||||
"""
|
||||
TERM-Handler for use script as a daemon
|
||||
In order for the Python program to exit gracefully when the TERM signal is received,
|
||||
it must have a function that exits the program when signal.SIGTERM is received.
|
||||
|
||||
@author: Jens Herrmann
|
||||
"""
|
||||
|
||||
import logging
|
||||
import signal # for use as daemon
|
||||
import sys # throw SystemExitException when daemon is terminated
|
||||
|
||||
def sigterm_handler(_signo, _stack_frame):
|
||||
"""
|
||||
TERM-Handler for use script as a daemon
|
||||
|
||||
@type _signo: signalnum
|
||||
@param _signo: signal number
|
||||
@type _stack_frame: frame object
|
||||
@param _stack_frame: current stack frame
|
||||
|
||||
@exception: SystemExitException when daemon is terminated
|
||||
"""
|
||||
logging.warning("TERM signal received")
|
||||
sys.exit(0)
|
||||
|
||||
# Set the handler for signal to the function handler.
|
||||
signal.signal(signal.SIGTERM, sigterm_handler)
|
||||
24
install.sh
24
install.sh
|
|
@ -1,20 +1,20 @@
|
|||
#!/bin/sh
|
||||
tput clear
|
||||
tput civis
|
||||
echo " ____ ____ ______ __ __ __ "
|
||||
echo " / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ "
|
||||
echo " / __ / / / /\__ \| | /| / / __ / __/ ___/ __ \ "
|
||||
echo " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / "
|
||||
echo " /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ "
|
||||
echo " German BOS Information Script "
|
||||
echo " by Bastian Schroll "
|
||||
echo " ____ ____ ______ __ __ __ "
|
||||
echo " / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ "
|
||||
echo " / __ / / / /\__ \| | /| / / __ / __/ ___/ __ \ "
|
||||
echo " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / "
|
||||
echo " /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ "
|
||||
echo " German BOS Information Script "
|
||||
echo " by Bastian Schroll "
|
||||
echo ""
|
||||
echo "This may take a several minutes... Don't panic!"
|
||||
echo ""
|
||||
echo "Caution, script don't install a Webserver with PHP and MySQL"
|
||||
echo "So you have to make up manually if you want to use MySQL support"
|
||||
|
||||
mkdir -p ~/boswatch/install
|
||||
mkdir -p ~/boswatch/install
|
||||
|
||||
tput cup 13 15
|
||||
echo "[ 1/10] [#---------]"
|
||||
|
|
@ -32,7 +32,7 @@ tput cup 13 15
|
|||
echo "[ 3/10] [###-------]"
|
||||
tput cup 15 5
|
||||
echo "-> download rtl_fm......................"
|
||||
cd ~/boswatch/install
|
||||
cd ~/boswatch/install
|
||||
git clone git://git.osmocom.org/rtl-sdr.git >> ~/boswatch/install/setup_log.txt 2>&1
|
||||
cd rtl-sdr/
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ tput cup 13 15
|
|||
echo "[ 5/10] [#####-----]"
|
||||
tput cup 15 5
|
||||
echo "-> download multimon-ng................"
|
||||
cd ~/boswatch/install
|
||||
cd ~/boswatch/install
|
||||
git clone https://github.com/EliasOenal/multimonNG.git >> ~/boswatch/install/setup_log.txt 2>&1
|
||||
cd multimonNG/
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ tput cup 13 15
|
|||
echo "[ 7/10] [#######---]"
|
||||
tput cup 15 5
|
||||
echo "-> download MySQL Connector for Python."
|
||||
cd ~/boswatch/install
|
||||
cd ~/boswatch/install
|
||||
wget "http://dev.mysql.com/get/Downloads/Connector-Python/mysql-connector-python-1.0.9.tar.gz/from/http://cdn.mysql.com/" -O mysql-connector.tar >> ~/boswatch/install/setup_log.txt 2>&1
|
||||
tar xfv mysql-connector.tar >> ~/boswatch/install/setup_log.txt 2>&1
|
||||
cd mysql-connector-python*
|
||||
|
|
@ -97,4 +97,4 @@ echo "# BOSWatch - blacklist the DVB drivers to avoid conflict with the SDR driv
|
|||
|
||||
tput cup 17 1
|
||||
echo "BOSWatch are now installed in ~/boswatch/"
|
||||
echo "Install ready!"
|
||||
echo "Install ready!"
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
###########################################################################
|
||||
# Use this as a simple Plugin Loading Tool to test your own Coded Plugins #
|
||||
###########################################################################
|
||||
|
||||
import logging
|
||||
|
||||
import ConfigParser #for parse the config file
|
||||
import os #for log mkdir
|
||||
import time #timestamp for doublealarm
|
||||
|
||||
from includes import globals # Global variables
|
||||
from includes import pluginLoader
|
||||
from includes import alarmHandler
|
||||
from includes import filter
|
||||
|
||||
#create new logger
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
#set log string format
|
||||
formatter = logging.Formatter('%(asctime)s - %(module)-12s [%(levelname)-8s] %(message)s', '%d.%m.%Y %H:%M:%S')
|
||||
|
||||
#create a display loger
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.DEBUG) #log level >= info
|
||||
ch.setFormatter(formatter)
|
||||
logger.addHandler(ch)
|
||||
|
||||
#https://docs.python.org/2/howto/logging.html#logging-basic-tutorial
|
||||
#log levels
|
||||
#----------
|
||||
#debug - debug messages only for log
|
||||
#info - information for normal display
|
||||
#warning
|
||||
#error - normal error - program goes further
|
||||
#exception - error with exception message in log
|
||||
#critical - critical error, program exit
|
||||
|
||||
globals.script_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
try:
|
||||
logging.debug("reading config file")
|
||||
globals.config = ConfigParser.ConfigParser()
|
||||
globals.config.read(globals.script_path+"/config/config.ini")
|
||||
for key,val in globals.config.items("Plugins"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
|
||||
|
||||
pluginLoader.loadPlugins()
|
||||
|
||||
filter.loadFilters()
|
||||
|
||||
|
||||
# ----- Test Data ----- #
|
||||
#typ = "FMS"
|
||||
#data = {"fms":"12345678", "status":"2", "direction":"1", "tsi":"III"}
|
||||
|
||||
typ = "ZVEI"
|
||||
data = {"zvei":"25345"}
|
||||
|
||||
#typ = "POC"
|
||||
#data = {"ric":"1234567", "function":"1", "msg":"Hello World!, "bitrate":"1200"}
|
||||
|
||||
while True:
|
||||
try:
|
||||
time.sleep(1)
|
||||
|
||||
print ""
|
||||
alarmHandler.processAlarm(typ,"0",data)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.warning("Keyboard Interrupt")
|
||||
exit()
|
||||
except:
|
||||
logging.exception("unknown error")
|
||||
exit()
|
||||
|
|
@ -19,6 +19,25 @@ import base64 #for the HTTP request with User/Password
|
|||
|
||||
from includes import globals # Global variables
|
||||
|
||||
from includes.helper import configHandler
|
||||
|
||||
##
|
||||
#
|
||||
# onLoad (init) function of plugin
|
||||
# will be called one time by the pluginLoader on start
|
||||
#
|
||||
def onLoad():
|
||||
"""
|
||||
While loading the plugins by pluginLoader.loadPlugins()
|
||||
this onLoad() routine is called one time for initialize the plugin
|
||||
|
||||
@requires: nothing
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
# nothing to do for this plugin
|
||||
return
|
||||
|
||||
##
|
||||
#
|
||||
# do BosMon-Request
|
||||
|
|
@ -33,9 +52,9 @@ def bosMonRequest(httprequest, params, headers):
|
|||
@param params: Contains the parameter for transfer to BosMon.
|
||||
@type headers: map
|
||||
@param headers: The headers argument should be a mapping of extra HTTP headers to send with the request.
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if httprequest.request failed
|
||||
@exception: Exception if HTTP-Request failed
|
||||
"""
|
||||
try:
|
||||
#
|
||||
|
|
@ -43,9 +62,11 @@ def bosMonRequest(httprequest, params, headers):
|
|||
#
|
||||
httprequest.request("POST", "/telegramin/"+globals.config.get("BosMon", "bosmon_channel")+"/input.xml", params, headers)
|
||||
except:
|
||||
logging.exception("request to BosMon failed")
|
||||
else:
|
||||
#
|
||||
logging.error("request to BosMon failed")
|
||||
logging.debug("request to BosMon failed", exc_info=True)
|
||||
raise
|
||||
else:
|
||||
#
|
||||
# check HTTP-Response
|
||||
#
|
||||
httpresponse = httprequest.getresponse()
|
||||
|
|
@ -63,7 +84,7 @@ def run(typ,freq,data):
|
|||
"""
|
||||
This function is the implementation of the BosMon-Plugin.
|
||||
It will send the data to an BosMon-Instance via http
|
||||
|
||||
|
||||
The configuration for the BosMon-Connection is set in the config.ini.
|
||||
If an user is set, the HTTP-Request is authenticatet.
|
||||
|
||||
|
|
@ -75,101 +96,104 @@ def run(typ,freq,data):
|
|||
@keyword freq: frequency is not used in this plugin
|
||||
|
||||
@requires: BosMon-Configuration has to be set in the config.ini
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if ConfigParser failed
|
||||
@exception: Exception if initialize header and connect to BosMon-Server failed
|
||||
@exception: Exception if urlencoding the params failed
|
||||
"""
|
||||
try:
|
||||
#
|
||||
# ConfigParser
|
||||
#
|
||||
logging.debug("reading config file")
|
||||
try:
|
||||
for key,val in globals.config.items("BosMon"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
if configHandler.checkConfig("BosMon"): #read and debug the config
|
||||
|
||||
try:
|
||||
#
|
||||
# Initialize header an connect to BosMon-Server
|
||||
#
|
||||
headers = {}
|
||||
headers['Content-type'] = "application/x-www-form-urlencoded"
|
||||
headers['Accept'] = "text/plain"
|
||||
# if an user is set in the config.ini we will use HTTP-Authorization
|
||||
if globals.config.get("BosMon", "bosmon_user"):
|
||||
# generate b64encoded autorization-token for HTTP-request
|
||||
headers['Authorization'] = "Basic {0}".format(base64.b64encode("{0}:{1}".format(globals.config.get("BosMon", "bosmon_user"), globals.config.get("BosMon", "bosmon_password"))))
|
||||
logging.debug("connect to BosMon")
|
||||
# open connection to BosMon-Server
|
||||
httprequest = httplib.HTTPConnection(globals.config.get("BosMon", "bosmon_server"), globals.config.get("BosMon", "bosmon_port"))
|
||||
# debug-level to shell (0=no debug|1)
|
||||
httprequest.set_debuglevel(0)
|
||||
except:
|
||||
logging.exception("cannot connect to BosMon")
|
||||
try:
|
||||
#
|
||||
# Initialize header an connect to BosMon-Server
|
||||
#
|
||||
headers = {}
|
||||
headers['Content-type'] = "application/x-www-form-urlencoded"
|
||||
headers['Accept'] = "text/plain"
|
||||
# if an user is set in the config.ini we will use HTTP-Authorization
|
||||
if globals.config.get("BosMon", "bosmon_user"):
|
||||
# generate b64encoded autorization-token for HTTP-request
|
||||
headers['Authorization'] = "Basic {0}".format(base64.b64encode("{0}:{1}".format(globals.config.get("BosMon", "bosmon_user"), globals.config.get("BosMon", "bosmon_password"))))
|
||||
logging.debug("connect to BosMon")
|
||||
# open connection to BosMon-Server
|
||||
httprequest = httplib.HTTPConnection(globals.config.get("BosMon", "bosmon_server"), globals.config.get("BosMon", "bosmon_port"), timeout=5)
|
||||
# debug-level to shell (0=no debug|1)
|
||||
httprequest.set_debuglevel(0)
|
||||
except:
|
||||
logging.error("cannot connect to BosMon")
|
||||
logging.debug("cannot connect to BosMon", exc_info=True)
|
||||
# Without connection, plugin couldn't work
|
||||
return
|
||||
|
||||
else:
|
||||
#
|
||||
# Format given data-structure to compatible BosMon string
|
||||
#
|
||||
if typ == "FMS":
|
||||
logging.debug("Start FMS to BosMon")
|
||||
try:
|
||||
# BosMon-Telegramin expected assembly group, direction and tsi in one field
|
||||
# structure (binary as hex in base10):
|
||||
# Byte 1: assembly group; Byte 2: Direction; Byte 3+4: tactic short info
|
||||
info = 0
|
||||
# assembly group:
|
||||
info = info + 1 # + b0001 (Assumption: is in every time 1 (no output from multimon-ng))
|
||||
# direction:
|
||||
if data["direction"] == "1":
|
||||
info = info + 2 # + b0010
|
||||
# tsi:
|
||||
if "IV" in data["tsi"]:
|
||||
info = info + 12 # + b1100
|
||||
elif "III" in data["tsi"]:
|
||||
info = info + 8 # + b1000
|
||||
elif "II" in data["tsi"]:
|
||||
info = info + 4 # + b0100
|
||||
# "I" is nothing to do + b0000
|
||||
|
||||
params = urllib.urlencode({'type':'fms', 'address':data["fms"], 'status':data["status"], 'info':info, 'flags':'0'})
|
||||
logging.debug(" - Params: %s", params)
|
||||
# dispatch the BosMon-request
|
||||
bosMonRequest(httprequest, params, headers)
|
||||
except:
|
||||
logging.exception("FMS to BosMon failed")
|
||||
|
||||
elif typ == "ZVEI":
|
||||
logging.debug("Start ZVEI to BosMon")
|
||||
try:
|
||||
params = urllib.urlencode({'type':'zvei', 'address':data["zvei"], 'flags':'0'})
|
||||
logging.debug(" - Params: %s", params)
|
||||
# dispatch the BosMon-request
|
||||
bosMonRequest(httprequest, params, headers)
|
||||
except:
|
||||
logging.exception("ZVEI to BosMon failed")
|
||||
|
||||
elif typ == "POC":
|
||||
logging.debug("Start POC to BosMon")
|
||||
try:
|
||||
# BosMon-Telegramin expected "a-d" as RIC-sub/function
|
||||
params = urllib.urlencode({'type':'pocsag', 'address':data["ric"], 'flags':'0', 'function':data["functionChar"], 'message':data["msg"]})
|
||||
logging.debug(" - Params: %s", params)
|
||||
# dispatch the BosMon-request
|
||||
bosMonRequest(httprequest, params, headers)
|
||||
except:
|
||||
logging.exception("POC to BosMon failed")
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
#
|
||||
# Format given data-structure to compatible BosMon string
|
||||
#
|
||||
if typ == "FMS":
|
||||
logging.debug("Start FMS to BosMon")
|
||||
try:
|
||||
# BosMon-Telegramin expected assembly group, direction and tsi in one field
|
||||
# structure (binary as hex in base10):
|
||||
# Byte 1: assembly group; Byte 2: Direction; Byte 3+4: tactic short info
|
||||
info = 0
|
||||
# assembly group:
|
||||
info = info + 1 # + b0001 (Assumption: is in every time 1 (no output from multimon-ng))
|
||||
# direction:
|
||||
if data["direction"] == "1":
|
||||
info = info + 2 # + b0010
|
||||
# tsi:
|
||||
if "IV" in data["tsi"]:
|
||||
info = info + 12 # + b1100
|
||||
elif "III" in data["tsi"]:
|
||||
info = info + 8 # + b1000
|
||||
elif "II" in data["tsi"]:
|
||||
info = info + 4 # + b0100
|
||||
# "I" is nothing to do + b0000
|
||||
|
||||
params = urllib.urlencode({'type':'fms', 'address':data["fms"], 'status':data["status"], 'info':info, 'flags':'0'})
|
||||
logging.debug(" - Params: %s", params)
|
||||
# dispatch the BosMon-request
|
||||
bosMonRequest(httprequest, params, headers)
|
||||
except:
|
||||
logging.error("FMS to BosMon failed")
|
||||
logging.debug("FMS to BosMon failed", exc_info=True)
|
||||
return
|
||||
|
||||
elif typ == "ZVEI":
|
||||
logging.debug("Start ZVEI to BosMon")
|
||||
try:
|
||||
params = urllib.urlencode({'type':'zvei', 'address':data["zvei"], 'flags':'0'})
|
||||
logging.debug(" - Params: %s", params)
|
||||
# dispatch the BosMon-request
|
||||
bosMonRequest(httprequest, params, headers)
|
||||
except:
|
||||
logging.error("ZVEI to BosMon failed")
|
||||
logging.debug("ZVEI to BosMon failed", exc_info=True)
|
||||
return
|
||||
|
||||
elif typ == "POC":
|
||||
logging.debug("Start POC to BosMon")
|
||||
try:
|
||||
# BosMon-Telegramin expected "a-d" as RIC-sub/function
|
||||
params = urllib.urlencode({'type':'pocsag', 'address':data["ric"], 'flags':'0', 'function':data["functionChar"], 'message':data["msg"]})
|
||||
logging.debug(" - Params: %s", params)
|
||||
# dispatch the BosMon-request
|
||||
bosMonRequest(httprequest, params, headers)
|
||||
except:
|
||||
logging.error("POC to BosMon failed")
|
||||
logging.debug("POC to BosMon failed", exc_info=True)
|
||||
return
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
||||
finally:
|
||||
logging.debug("close BosMon-Connection")
|
||||
try:
|
||||
httprequest.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
finally:
|
||||
logging.debug("close BosMon-Connection")
|
||||
httprequest.close()
|
||||
|
||||
except:
|
||||
logging.exception("")
|
||||
# something very mysterious
|
||||
logging.error("unknown error")
|
||||
logging.debug("unknown error", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,25 @@ import mysql.connector
|
|||
|
||||
from includes import globals # Global variables
|
||||
|
||||
from includes.helper import configHandler
|
||||
|
||||
##
|
||||
#
|
||||
# onLoad (init) function of plugin
|
||||
# will be called one time by the pluginLoader on start
|
||||
#
|
||||
def onLoad():
|
||||
"""
|
||||
While loading the plugins by pluginLoader.loadPlugins()
|
||||
this onLoad() routine is called one time for initialize the plugin
|
||||
|
||||
@requires: nothing
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
# nothing to do for this plugin
|
||||
return
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
|
|
@ -28,7 +47,7 @@ def run(typ,freq,data):
|
|||
"""
|
||||
This function is the implementation of the MySQL-Plugin.
|
||||
It will store the data to an MySQL database
|
||||
|
||||
|
||||
The configuration for the MySQL-Connection is set in the config.ini.
|
||||
For DB- and tablestructure see boswatch.sql
|
||||
|
||||
|
|
@ -41,59 +60,53 @@ def run(typ,freq,data):
|
|||
|
||||
@requires: MySQL-Configuration has to be set in the config.ini
|
||||
@requires: Created Database/Tables, see boswatch.sql
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if ConfigParser failed
|
||||
@exception: Exception if connect to MySQL failed
|
||||
@exception: Exception if executing the sql-statement is failed
|
||||
"""
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
try:
|
||||
#ConfigParser
|
||||
logging.debug("reading config file")
|
||||
try:
|
||||
for key,val in globals.config.items("MySQL"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
|
||||
try:
|
||||
#
|
||||
# Connect to MySQL
|
||||
#
|
||||
logging.debug("connect to MySQL")
|
||||
connection = mysql.connector.connect(host = globals.config.get("MySQL","dbserver"), user = globals.config.get("MySQL","dbuser"), passwd = globals.config.get("MySQL","dbpassword"), db = globals.config.get("MySQL","database"))
|
||||
cursor = connection.cursor()
|
||||
except:
|
||||
logging.exception("cannot connect to MySQL")
|
||||
else:
|
||||
if configHandler.checkConfig("MySQL"): #read and debug the config
|
||||
|
||||
try:
|
||||
#
|
||||
# Connect to MySQL
|
||||
#
|
||||
# Create and execute SQL-statement
|
||||
#
|
||||
logging.debug("Insert %s", typ)
|
||||
|
||||
if typ == "FMS":
|
||||
#data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "tsi":fms_tsi}
|
||||
cursor.execute("INSERT INTO "+globals.config.get("MySQL","tableFMS")+" (time,fms,status,direction,tsi) VALUES (NOW(),%s,%s,%s,%s)",(data["fms"],data["status"],data["direction"],data["tsi"]))
|
||||
|
||||
elif typ == "ZVEI":
|
||||
#data = {"zvei":zvei_id}
|
||||
#Don't use %s here (bug in mysql-lib with one parameter)
|
||||
cursor.execute("INSERT INTO "+globals.config.get("MySQL","tableZVEI")+" (time,zvei) VALUES (NOW(),"+(data["zvei"])+")")
|
||||
|
||||
elif typ == "POC":
|
||||
#data = {"ric":poc_id, "function":poc_sub, "msg":poc_text}
|
||||
cursor.execute("INSERT INTO "+globals.config.get("MySQL","tablePOC")+" (time,ric,funktion,text) VALUES (NOW(),%s,%s,%s)",(data["ric"],data["function"],data["msg"]))
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
logging.debug("connect to MySQL")
|
||||
connection = mysql.connector.connect(host = globals.config.get("MySQL","dbserver"), user = globals.config.get("MySQL","dbuser"), passwd = globals.config.get("MySQL","dbpassword"), db = globals.config.get("MySQL","database"))
|
||||
cursor = connection.cursor()
|
||||
except:
|
||||
logging.exception("cannot Insert %s", typ)
|
||||
|
||||
finally:
|
||||
logging.debug("close MySQL")
|
||||
cursor.close()
|
||||
connection.close() #Close connection in every case
|
||||
logging.error("cannot connect to MySQL")
|
||||
logging.debug("cannot connect to MySQL", exc_info=True)
|
||||
else: # Without connection, plugin couldn't work
|
||||
try:
|
||||
#
|
||||
# Create and execute SQL-statement
|
||||
#
|
||||
logging.debug("Insert %s", typ)
|
||||
|
||||
if typ == "FMS":
|
||||
cursor.execute("INSERT INTO "+globals.config.get("MySQL","tableFMS")+" (time,fms,status,direction,directionText,tsi,description) VALUES (NOW(),%s,%s,%s,%s,%s,%s)",(data["fms"],data["status"],data["direction"],data["directionText"],data["tsi"],data["description"]))
|
||||
|
||||
elif typ == "ZVEI":
|
||||
cursor.execute("INSERT INTO "+globals.config.get("MySQL","tableZVEI")+" (time,zvei,description) VALUES (NOW(),%s,%s)",(data["zvei"],data["description"]))
|
||||
|
||||
elif typ == "POC":
|
||||
cursor.execute("INSERT INTO "+globals.config.get("MySQL","tablePOC")+" (time,ric,funktion,funktionChar,msg,bitrate,description) VALUES (NOW(),%s,%s,%s,%s,%s,%s)",(data["ric"],data["function"],data["functionChar"],data["msg"],data["bitrate"],data["description"]))
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
except:
|
||||
logging.error("cannot Insert %s", typ)
|
||||
logging.debug("cannot Insert %s", typ, exc_info=True)
|
||||
return
|
||||
|
||||
finally:
|
||||
logging.debug("close MySQL")
|
||||
try:
|
||||
cursor.close()
|
||||
connection.close() #Close connection in every case
|
||||
except:
|
||||
pass
|
||||
|
||||
except:
|
||||
logging.exception("unknown error")
|
||||
logging.error("unknown error")
|
||||
logging.debug("unknown error", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
-- MySQL Database Structure for the BOSWatch MySQL Plugin
|
||||
-- @author: Bastian Schroll
|
||||
|
||||
-- phpMyAdmin SQL Dump
|
||||
-- version 3.4.11.1deb2+deb7u1
|
||||
-- http://www.phpmyadmin.net
|
||||
|
|
@ -32,7 +35,9 @@ CREATE TABLE IF NOT EXISTS `bos_fms` (
|
|||
`fms` varchar(8) NOT NULL,
|
||||
`status` varchar(1) NOT NULL,
|
||||
`direction` varchar(1) NOT NULL,
|
||||
`directionText` text(10) NOT NULL,
|
||||
`tsi` varchar(3) NOT NULL,
|
||||
`description` text NOT NULL,
|
||||
PRIMARY KEY (`ID`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
|
||||
|
||||
|
|
@ -47,7 +52,10 @@ CREATE TABLE IF NOT EXISTS `bos_pocsag` (
|
|||
`time` datetime NOT NULL,
|
||||
`ric` varchar(7) NOT NULL DEFAULT '0',
|
||||
`funktion` int(1) NOT NULL,
|
||||
`text` text NOT NULL,
|
||||
`funktionChar` text(1) NOT NULL,
|
||||
`msg` text NOT NULL,
|
||||
`bitrate` int(4) NOT NULL,
|
||||
`description` text NOT NULL,
|
||||
KEY `ID` (`ID`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
|
||||
|
||||
|
|
@ -61,6 +69,7 @@ CREATE TABLE IF NOT EXISTS `bos_zvei` (
|
|||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`time` datetime NOT NULL,
|
||||
`zvei` varchar(5) NOT NULL DEFAULT '0',
|
||||
`description` text NOT NULL,
|
||||
PRIMARY KEY (`ID`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
|
||||
|
||||
191
plugins/README.md
Normal file
191
plugins/README.md
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
## How to Code your own plugin:
|
||||
|
||||
More information and a little Tutorial coming soon!
|
||||
|
||||
## 1. Plugin template
|
||||
#### 1.1 General
|
||||
You can find a little template plugin file in `plugins/template/template.py` But you can also take a look in all other plugins.
|
||||
|
||||
A plugin must be in an seperate folder with the same name of the .py file
|
||||
|
||||
#### 1.2 Plugin Init `.onLoad()`
|
||||
This `.onLoad()` routine is called one time for initialize the plugin
|
||||
|
||||
#### 1.3 Plugin call `.run()`
|
||||
This `.run()` routine is called every time an alarm comes in
|
||||
|
||||
Here are the information from BOSWatch available. See section `5. Process the data from BOSWatch`
|
||||
|
||||
|
||||
## 2. Use Global Logging
|
||||
#### 2.1 Init and Use
|
||||
First you must import the logging module
|
||||
```python
|
||||
import logging # Global logger
|
||||
```
|
||||
Now you can send log messages with:
|
||||
|
||||
```python
|
||||
logging.LOGLEVEL("MESSAGE")
|
||||
```
|
||||
You must replace the word `LOGLEVEL` with one if the following `debug`, `info`, `warning` or `error`
|
||||
|
||||
To use the right loglevel see next section `2.2 Choose right Loglevel`
|
||||
|
||||
#### 2.2 Choose right Loglevel
|
||||
`debug`
|
||||
all messages to find errors and for the internal program flow.
|
||||
|
||||
`info`
|
||||
messages that serve only to inform the user.
|
||||
|
||||
`warning`
|
||||
Warnings are notes and technical errors. Never leads to terminate BOSWatch.
|
||||
|
||||
`error`
|
||||
An error that does not necessarily lead to end of BOSWatch, but an administrator intervention required.
|
||||
|
||||
`critical`
|
||||
errors leading to the end of boswatch immediate - **in plugins not allowed** (Plugin cannot crash the entire program)
|
||||
|
||||
|
||||
## 3. Use config file
|
||||
#### 3.1 Own configuration in config.ini
|
||||
First you must set a new Section in `config.ini`
|
||||
A section is between brackets. Its recommended to give the section the same name as the plugin. `[SECTION_NAME]`
|
||||
|
||||
Now you can an set a unlimited number of options with its own value in these format: `OPTION = VALUE`.
|
||||
|
||||
Here is the sample from the template plugin:
|
||||
```python
|
||||
[template]
|
||||
test1 = testString
|
||||
test2 = 123456
|
||||
```
|
||||
|
||||
#### 3.2 Read data from config.ini
|
||||
To read yout configuration data you must import the `globals.py` where the global config-object is located:
|
||||
```python
|
||||
from includes import globals # Global variables
|
||||
```
|
||||
|
||||
Now you can get your configuration data with:
|
||||
```python
|
||||
VALUE = globals.config.get("SECTION", "OPTION") #Gets any value
|
||||
```
|
||||
or better, use this:
|
||||
```python
|
||||
VALUE = globals.config.getint("SECTION", "OPTION") #Value must be an Integer
|
||||
VALUE = globals.config.getfloat("SECTION", "OPTION") #Value must be an Float
|
||||
VALUE = globals.config.getboolean("SECTION", "OPTION") #Value must be an Boolean
|
||||
```
|
||||
|
||||
|
||||
## 4. Global helper functions
|
||||
#### 4.1 configHandler.py
|
||||
First you must include the helper file
|
||||
```python
|
||||
from includes.helper import configHandler
|
||||
```
|
||||
##### 4.1.1 `.checkConfig(section)`
|
||||
This function read all options from a config section and prints it to the debug log. The return value is `true`, also the section var is empty. In case of error a `false` is returned and error printed to log.
|
||||
```python
|
||||
if configHandler.checkConfig("template"): #check config file
|
||||
########## User Plugin CODE ##########
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
#### 4.2 timeHandler.py
|
||||
First you must include the helper file
|
||||
```python
|
||||
from includes.helper import timeHandler
|
||||
```
|
||||
##### 4.2.1 `.curtime(format)`
|
||||
```python
|
||||
timeHandler.curtime() # returns a formated datetime string
|
||||
```
|
||||
you can give the function an format string. See https://docs.python.org/2/library/time.html#time.strftime
|
||||
|
||||
default (without format parameter) the function returns a date time with this format `%d.%m.%Y %H:%M:%S`
|
||||
##### 4.2.2 `.getDate()`
|
||||
```python
|
||||
timeHandler.getDate() # returns the current date in format `%d.%m.%Y`
|
||||
```
|
||||
##### 4.2.3 `.getTime()`
|
||||
```python
|
||||
timeHandler.getTime() # returns the current time in format `%H:%M:%S`
|
||||
```
|
||||
##### 4.2.4 `.getTimestamp()`
|
||||
```python
|
||||
timeHandler.getTimestamp() # returns the current linux timestamp
|
||||
```
|
||||
|
||||
#### 4.3 wildcardHandler.py
|
||||
First you must include the helper file
|
||||
```python
|
||||
from includes.helper import wildcardHandler
|
||||
```
|
||||
##### 4.3.1 `.replaceWildcards(text,data)`
|
||||
```python
|
||||
wildcardHandler.replaceWildcards(text,data) # replace all standard wildcards
|
||||
```
|
||||
replace all the standard wildcards in the given text
|
||||
the function needs the data[ ] var
|
||||
|
||||
defined wildcards:
|
||||
|
||||
**General:**
|
||||
- `%TIME%` = Time (by script)
|
||||
- `%DATE%` = Date (by script)
|
||||
- `%DESCR%` = Description from csv-file
|
||||
|
||||
**FMS:**
|
||||
- `%FMS%` = FMS Code
|
||||
- `%STATUS%` = FMS Status
|
||||
- `%DIR%` = Direction of the telegram (0/1)
|
||||
- `%DIRT%` = Direction of the telegram (Text-String)
|
||||
- `%TSI%` = Tactical Short Information (I-IV)
|
||||
|
||||
**ZVEI:**
|
||||
- `%ZVEI%` = ZVEI 5-tone Code
|
||||
|
||||
**POCSAG:**
|
||||
- `%RIC%` = Pocsag RIC
|
||||
- `%FUNC%` = Pocsac function/Subric (1-4)
|
||||
- `%FUNCCHAR%` = Pocsac function/Subric als character (a-d)
|
||||
- `%MSG%` = Message of the Pocsag telegram
|
||||
- `%BITRATE%` = Bitrate of the Pocsag telegram
|
||||
|
||||
## 5. Process the data from BOSWatch
|
||||
Three parameters are passed during the alarm to the .run() method
|
||||
|
||||
#### 5.1 typ
|
||||
Thats the function of the alarm. Possible values are `FMS`, `ZVEI` or `POC`
|
||||
|
||||
#### 5.2 freq
|
||||
The reception frequency of the tuner in Hz
|
||||
|
||||
#### 5.3 data[ ]
|
||||
You can get an information with `data["INFO"]`
|
||||
In the data map are the folowing informations:
|
||||
|
||||
**ZVEI:**
|
||||
- zvei
|
||||
- description
|
||||
|
||||
**FMS:**
|
||||
- fms
|
||||
- status
|
||||
- direction
|
||||
- directionText
|
||||
- tsi
|
||||
- description
|
||||
|
||||
**POCSAG:**
|
||||
- ric
|
||||
- function
|
||||
- functionChar
|
||||
- msg
|
||||
- bitrate
|
||||
- description
|
||||
|
|
@ -19,12 +19,25 @@ from email.utils import make_msgid # need for confirm to RFC2822 standard
|
|||
|
||||
from includes import globals # Global variables
|
||||
|
||||
from includes.helper import timeHandler # helper function
|
||||
from includes.helper import configHandler
|
||||
|
||||
##
|
||||
#
|
||||
# Private helper function for a printable Timestamp
|
||||
# onLoad (init) function of plugin
|
||||
# will be called one time by the pluginLoader on start
|
||||
#
|
||||
def curtime():
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
def onLoad():
|
||||
"""
|
||||
While loading the plugins by pluginLoader.loadPlugins()
|
||||
this onLoad() routine is called one time for initialize the plugin
|
||||
|
||||
@requires: nothing
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
# nothing to do for this plugin
|
||||
return
|
||||
|
||||
##
|
||||
#
|
||||
|
|
@ -40,11 +53,11 @@ def doSendmail(server, subject, mailtext):
|
|||
@param subject: Subject for the eMail
|
||||
@type mailtext: string
|
||||
@param mailtext: Mailtext for the eMail
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if smtp.sendmail failed
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
msg = MIMEText(mailtext)
|
||||
msg['From'] = globals.config.get("eMail", "from")
|
||||
msg['To'] = globals.config.get("eMail", "to")
|
||||
|
|
@ -52,9 +65,11 @@ def doSendmail(server, subject, mailtext):
|
|||
msg['Date'] = formatdate()
|
||||
msg['Message-Id'] = make_msgid()
|
||||
msg['Priority'] = globals.config.get("eMail", "priority")
|
||||
server.sendmail(globals.config.get("eMail", "from"), globals.config.get("eMail", "to"), msg.as_string())
|
||||
server.sendmail(globals.config.get("eMail", "from"), globals.config.get("eMail", "to").split(), msg.as_string())
|
||||
except:
|
||||
logging.exception("send eMail failed")
|
||||
logging.error("send eMail failed")
|
||||
logging.debug("send eMail failed", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
##
|
||||
|
|
@ -66,7 +81,7 @@ def run(typ,freq,data):
|
|||
"""
|
||||
This function is the implementation of the eMail-Plugin.
|
||||
It will send the data via eMail (SMTP)
|
||||
|
||||
|
||||
The configuration for the eMail-Connection is set in the config.ini.
|
||||
If an user is set, the HTTP-Request is authenticatet.
|
||||
|
||||
|
|
@ -78,113 +93,115 @@ def run(typ,freq,data):
|
|||
@keyword freq: frequency of the SDR Stick
|
||||
|
||||
@requires: eMail-Configuration has to be set in the config.ini
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if ConfigParser failed
|
||||
@exception: Exception if connect to SMTP-Server failed
|
||||
@exception: Exception if sending the eMail failed
|
||||
"""
|
||||
try:
|
||||
#
|
||||
# ConfigParser
|
||||
#
|
||||
logging.debug("reading config file")
|
||||
try:
|
||||
for key,val in globals.config.items("eMail"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
if configHandler.checkConfig("eMail"): #read and debug the config
|
||||
|
||||
try:
|
||||
#
|
||||
# connect to SMTP-Server
|
||||
#
|
||||
server = smtplib.SMTP(globals.config.get("eMail", "smtp_server"), globals.config.get("eMail", "smtp_port"))
|
||||
# debug-level to shell (0=no debug|1)
|
||||
server.set_debuglevel(0)
|
||||
|
||||
# if tls is enabled, starttls
|
||||
if globals.config.get("eMail", "tls"):
|
||||
server.starttls()
|
||||
|
||||
# if user is given, login
|
||||
if globals.config.get("eMail", "user"):
|
||||
server.login(globals.config.get("eMail", "user"), globals.config.get("eMail", "password"))
|
||||
|
||||
except:
|
||||
logging.exception("cannot connect to eMail")
|
||||
try:
|
||||
#
|
||||
# connect to SMTP-Server
|
||||
#
|
||||
server = smtplib.SMTP(globals.config.get("eMail", "smtp_server"), globals.config.get("eMail", "smtp_port"))
|
||||
# debug-level to shell (0=no debug|1)
|
||||
server.set_debuglevel(0)
|
||||
|
||||
else:
|
||||
# if tls is enabled, starttls
|
||||
if globals.config.get("eMail", "tls"):
|
||||
server.starttls()
|
||||
|
||||
if typ == "FMS":
|
||||
logging.debug("Start FMS to eMail")
|
||||
try:
|
||||
# read subject-structure from config.ini
|
||||
subject = globals.config.get("eMail", "fms_subject")
|
||||
subject = subject.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards
|
||||
subject = subject.replace("%DIR%", data["direction"]).replace("%DIRT%", data["directionText"]) #replace Wildcards
|
||||
subject = subject.replace("%TSI%", data["tsi"]) #replace Wildcards
|
||||
subject = subject.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
subject = subject.replace("%TIME%", curtime()) # replace Wildcards
|
||||
# read mailtext-structure from config.ini
|
||||
mailtext = globals.config.get("eMail", "fms_message")
|
||||
mailtext = mailtext.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%DIR%", data["direction"]).replace("%DIRT%", data["directionText"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%TSI%", data["tsi"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards
|
||||
# send eMail
|
||||
doSendmail(server, subject, mailtext)
|
||||
except:
|
||||
logging.exception("FMS to eMail failed")
|
||||
# if user is given, login
|
||||
if globals.config.get("eMail", "user"):
|
||||
server.login(globals.config.get("eMail", "user"), globals.config.get("eMail", "password"))
|
||||
|
||||
elif typ == "ZVEI":
|
||||
logging.debug("Start ZVEI to eMail")
|
||||
try:
|
||||
# read subject-structure from config.ini
|
||||
subject = globals.config.get("eMail", "zvei_subject")
|
||||
subject = subject.replace("%ZVEI%", data["zvei"]) #replace Wildcards
|
||||
subject = subject.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
subject = subject.replace("%TIME%", curtime()) # replace Wildcards
|
||||
# read mailtext-structure from config.ini
|
||||
mailtext = globals.config.get("eMail", "zvei_message")
|
||||
mailtext = mailtext.replace("%ZVEI%", data["zvei"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards
|
||||
# send eMail
|
||||
doSendmail(server, subject, mailtext)
|
||||
except:
|
||||
logging.exception("ZVEI to eMail failed")
|
||||
except:
|
||||
logging.error("cannot connect to eMail")
|
||||
logging.debug("cannot connect to eMail", exc_info=True)
|
||||
# Without connection, plugin couldn't work
|
||||
return
|
||||
|
||||
elif typ == "POC":
|
||||
logging.debug("Start POC to eMail")
|
||||
try:
|
||||
# read subject-structure from config.ini
|
||||
subject = globals.config.get("eMail", "poc_subject")
|
||||
subject = subject.replace("%RIC%", data["ric"]) #replace Wildcards
|
||||
subject = subject.replace("%FUNC%", data["function"]).replace("%FUNCCHAR%", data["functionChar"]) #replace Wildcards
|
||||
subject = subject.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards
|
||||
subject = subject.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
subject = subject.replace("%TIME%", curtime()) # replace Wildcards
|
||||
# read mailtext-structure from config.ini
|
||||
mailtext = globals.config.get("eMail", "poc_message")
|
||||
mailtext = mailtext.replace("%RIC%", data["ric"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%FUNC%", data["function"]).replace("%FUNCCHAR%", data["functionChar"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards
|
||||
mailtext = mailtext.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
mailtext = mailtext.replace("%TIME%", curtime()) # replace Wildcards
|
||||
# send eMail
|
||||
doSendmail(server, subject, mailtext)
|
||||
except:
|
||||
logging.exception("POC to eMail failed")
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
||||
finally:
|
||||
logging.debug("close eMail-Connection")
|
||||
server.quit()
|
||||
|
||||
if typ == "FMS":
|
||||
logging.debug("Start FMS to eMail")
|
||||
try:
|
||||
# read subject-structure from config.ini
|
||||
subject = globals.config.get("eMail", "fms_subject")
|
||||
subject = subject.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards
|
||||
subject = subject.replace("%DIR%", data["direction"]).replace("%DIRT%", data["directionText"]) #replace Wildcards
|
||||
subject = subject.replace("%TSI%", data["tsi"]) #replace Wildcards
|
||||
subject = subject.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
subject = subject.replace("%TIME%", timeHandler.curtime("H:M:S")).replace("%DATE%", timeHandler.curtime("Y-m-d")) # replace Wildcards
|
||||
# read mailtext-structure from config.ini
|
||||
mailtext = globals.config.get("eMail", "fms_message")
|
||||
mailtext = mailtext.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%DIR%", data["direction"]).replace("%DIRT%", data["directionText"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%TSI%", data["tsi"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
mailtext = mailtext.replace("%TIME%", timeHandler.curtime("H:M:S")).replace("%DATE%", timeHandler.curtime("Y-m-d")) # replace Wildcards
|
||||
# send eMail
|
||||
doSendmail(server, subject, mailtext)
|
||||
except:
|
||||
logging.error("%s to eMail failed", typ)
|
||||
logging.debug("%s to eMail failed", typ, exc_info=True)
|
||||
return
|
||||
|
||||
elif typ == "ZVEI":
|
||||
logging.debug("Start ZVEI to eMail")
|
||||
try:
|
||||
# read subject-structure from config.ini
|
||||
subject = globals.config.get("eMail", "zvei_subject")
|
||||
subject = subject.replace("%ZVEI%", data["zvei"]) #replace Wildcards
|
||||
subject = subject.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
subject = subject.replace("%TIME%", timeHandler.curtime("H:M:S")).replace("%DATE%", timeHandler.curtime("Y-m-d")) # replace Wildcards
|
||||
# read mailtext-structure from config.ini
|
||||
mailtext = globals.config.get("eMail", "zvei_message")
|
||||
mailtext = mailtext.replace("%ZVEI%", data["zvei"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
mailtext = mailtext.replace("%TIME%", timeHandler.curtime("H:M:S")).replace("%DATE%", timeHandler.curtime("Y-m-d")) # replace Wildcards
|
||||
# send eMail
|
||||
doSendmail(server, subject, mailtext)
|
||||
except:
|
||||
logging.error("%s to eMail failed", typ)
|
||||
logging.debug("%s to eMail failed", typ, exc_info=True)
|
||||
return
|
||||
|
||||
elif typ == "POC":
|
||||
logging.debug("Start POC to eMail")
|
||||
try:
|
||||
# read subject-structure from config.ini
|
||||
subject = globals.config.get("eMail", "poc_subject")
|
||||
subject = subject.replace("%RIC%", data["ric"]) #replace Wildcards
|
||||
subject = subject.replace("%FUNC%", data["function"]).replace("%FUNCCHAR%", data["functionChar"]) #replace Wildcards
|
||||
subject = subject.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards
|
||||
subject = subject.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
subject = subject.replace("%TIME%", timeHandler.curtime("H:M:S")).replace("%DATE%", timeHandler.curtime("Y-m-d")) # replace Wildcards
|
||||
# read mailtext-structure from config.ini
|
||||
mailtext = globals.config.get("eMail", "poc_message")
|
||||
mailtext = mailtext.replace("%RIC%", data["ric"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%FUNC%", data["function"]).replace("%FUNCCHAR%", data["functionChar"]) #replace Wildcards
|
||||
mailtext = mailtext.replace("%MSG%", data["msg"]).replace("%BITRATE%", str(data["bitrate"])) #replace Wildcards
|
||||
mailtext = mailtext.replace("%DESCR%", data["description"]) # replace Wildcards
|
||||
mailtext = mailtext.replace("%TIME%", timeHandler.curtime("H:M:S")).replace("%DATE%", timeHandler.curtime("Y-m-d")) # replace Wildcards
|
||||
# send eMail
|
||||
doSendmail(server, subject, mailtext)
|
||||
except:
|
||||
logging.error("%s to eMail failed", typ)
|
||||
logging.debug("%s to eMail failed", typ, exc_info=True)
|
||||
return
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
||||
finally:
|
||||
logging.debug("close eMail-Connection")
|
||||
try:
|
||||
server.quit()
|
||||
except:
|
||||
pass
|
||||
|
||||
except:
|
||||
logging.exception("")
|
||||
# something very mysterious
|
||||
logging.error("unknown error")
|
||||
logging.debug("unknown error", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
"""
|
||||
firEmergency-Plugin to dispatch ZVEI- and POCSAG - messages to firEmergency
|
||||
|
||||
firEmergency configuration:
|
||||
- set input to "FMS32" at Port 5555
|
||||
|
||||
@autor: Smith-fms
|
||||
|
||||
@requires: firEmergency-Configuration has to be set in the config.ini
|
||||
|
|
@ -14,6 +17,26 @@ import socket
|
|||
|
||||
from includes import globals # Global variables
|
||||
|
||||
from includes.helper import configHandler
|
||||
|
||||
###
|
||||
#
|
||||
# onLoad (init) function of plugin
|
||||
# will be called one time by the pluginLoader on start
|
||||
#
|
||||
def onLoad():
|
||||
"""
|
||||
While loading the plugins by pluginLoader.loadPlugins()
|
||||
this onLoad() routine is called one time for initialize the plugin
|
||||
|
||||
@requires: nothing
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
# nothing to do for this plugin
|
||||
return
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# Main function of firEmergency-plugin
|
||||
|
|
@ -23,7 +46,7 @@ def run(typ,freq,data):
|
|||
"""
|
||||
This function is the implementation of the firEmergency-Plugin.
|
||||
It will send the data to an firEmergency-Instance.
|
||||
|
||||
|
||||
The configuration for the firEmergency-Connection is set in the config.ini.
|
||||
|
||||
@type typ: string (ZVEI|POC)
|
||||
|
|
@ -34,60 +57,64 @@ def run(typ,freq,data):
|
|||
@keyword freq: frequency is not used in this plugin
|
||||
|
||||
@requires: firEmergency-Configuration has to be set in the config.ini
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if ConfigParser failed
|
||||
@exception: Exception ifconnect to firEmergency failed
|
||||
@exception: Exception if sending the data failed
|
||||
"""
|
||||
try:
|
||||
#
|
||||
#ConfigParser
|
||||
#
|
||||
logging.debug("reading config file")
|
||||
try:
|
||||
for key,val in globals.config.items("firEmergency"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
if configHandler.checkConfig("firEmergency"): #read and debug the config
|
||||
|
||||
try:
|
||||
#
|
||||
# connect to firEmergency
|
||||
#
|
||||
firSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
firSocket.connect((globals.config.get("firEmergency", "firserver"), globals.config.getint("firEmergency", "firport")))
|
||||
except:
|
||||
logging.exception("cannot connect to firEmergency")
|
||||
else:
|
||||
#
|
||||
# Format given data-structure to xml-string for firEmergency
|
||||
#
|
||||
if typ == "FMS":
|
||||
logging.debug("FMS not supported by firEmgency")
|
||||
|
||||
elif typ == "ZVEI":
|
||||
logging.debug("ZVEI to firEmergency")
|
||||
try:
|
||||
firXML = "<event>\n<address>"+data["zvei"]+"</address>\n<message>"+data["zvei"]+" alarmiert.</message>\n</event>\n"
|
||||
firSocket.send(firXML)
|
||||
except:
|
||||
logging.exception("ZVEI to firEmergency failed")
|
||||
|
||||
elif typ == "POC":
|
||||
logging.debug("POC to firEmergency")
|
||||
try:
|
||||
firXML = "<event>\n<address>"+data["ric"]+"</address>\n<message>"+data["msg"]+"</message>\n</event>\n"
|
||||
firSocket.send(firXML)
|
||||
except:
|
||||
logging.exception("POC to firEmergency failed")
|
||||
try:
|
||||
#
|
||||
# connect to firEmergency
|
||||
#
|
||||
firSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
firSocket.connect((globals.config.get("firEmergency", "firserver"), globals.config.getint("firEmergency", "firport")))
|
||||
except:
|
||||
logging.error("cannot connect to firEmergency")
|
||||
logging.debug("cannot connect to firEmergency", exc_info=True)
|
||||
# Without connection, plugin couldn't work
|
||||
return
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
#
|
||||
# Format given data-structure to xml-string for firEmergency
|
||||
#
|
||||
if typ == "FMS":
|
||||
logging.debug("FMS not supported by firEmgency")
|
||||
|
||||
finally:
|
||||
logging.debug("close firEmergency-Connection")
|
||||
firSocket.close()
|
||||
elif typ == "ZVEI":
|
||||
logging.debug("ZVEI to firEmergency")
|
||||
try:
|
||||
firXML = "<event>\n<address>"+data["zvei"]+"</address>\n<description>"+data["description"]+"</description>\n<message>"+data["zvei"]+" alarmiert.</message>\n</event>\n"
|
||||
firSocket.send(firXML)
|
||||
except:
|
||||
logging.error("%s to firEmergency failed", typ)
|
||||
logging.debug("%s to firEmergency failed", typ, exc_info=True)
|
||||
# Without connection, plugin couldn't work
|
||||
return
|
||||
|
||||
elif typ == "POC":
|
||||
logging.debug("POC to firEmergency")
|
||||
try:
|
||||
# !!! Subric+"XX" because of an Issuse in firEmergency !!!
|
||||
firXML = "<event>\n<address>"+data["ric"]+"</address>\n<status>"+data["function"]+"XX</status>\n<description>"+data["description"]+"</description>\n<message>"+data["msg"]+"</message>\n</event>\n"
|
||||
firSocket.send(firXML)
|
||||
except:
|
||||
logging.error("%s to firEmergency failed", typ)
|
||||
logging.debug("%s to firEmergency failed", typ, exc_info=True)
|
||||
# Without connection, plugin couldn't work
|
||||
return
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
||||
finally:
|
||||
logging.debug("close firEmergency-Connection")
|
||||
try:
|
||||
firSocket.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
except:
|
||||
logging.exception("unknown error")
|
||||
logging.error("unknown error")
|
||||
logging.debug("unknown error", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -9,12 +9,34 @@ httpRequest-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages to an URL
|
|||
@requires: httpRequest-Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import time
|
||||
import logging # Global logger
|
||||
import httplib #for the HTTP request
|
||||
from urlparse import urlparse #for split the URL into url and path
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
from includes.helper import timeHandler
|
||||
from includes.helper import wildcardHandler
|
||||
from includes.helper import configHandler
|
||||
|
||||
##
|
||||
#
|
||||
# onLoad (init) function of plugin
|
||||
# will be called one time by the pluginLoader on start
|
||||
#
|
||||
def onLoad():
|
||||
"""
|
||||
While loading the plugins by pluginLoader.loadPlugins()
|
||||
this onLoad() routine is called one time for initialize the plugin
|
||||
|
||||
@requires: nothing
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
# nothing to do for this plugin
|
||||
return
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
|
|
@ -34,68 +56,66 @@ def run(typ,freq,data):
|
|||
@keyword freq: frequency of the SDR Stick
|
||||
|
||||
@requires: httpRequest-Configuration has to be set in the config.ini
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if ConfigParser failed
|
||||
@exception: Exception if http Request failed
|
||||
@exception: Exception if http Response failed
|
||||
"""
|
||||
try:
|
||||
#
|
||||
# ConfigParser
|
||||
#
|
||||
logging.debug("reading config file")
|
||||
try:
|
||||
for key,val in globals.config.items("httpRequest"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
|
||||
try:
|
||||
#
|
||||
# Create URL
|
||||
#
|
||||
logging.debug("send %s HTTP request", typ)
|
||||
|
||||
if typ == "FMS":
|
||||
url = globals.config.get("httpRequest", "fms_url") #Get URL
|
||||
url = url.replace("%FMS%", data["fms"]).replace("%STATUS%", data["status"]) #replace Wildcards in URL
|
||||
url = url.replace("%DIR%", data["direction"]).replace("%TSI%", data["tsi"]) #replace Wildcards in URL
|
||||
elif typ == "ZVEI":
|
||||
url = globals.config.get("httpRequest", "zvei_url") #Get URL
|
||||
url = url.replace("%ZVEI%", data["zvei"]) #replace Wildcards in URL
|
||||
elif typ == "POC":
|
||||
url = globals.config.get("httpRequest", "poc_url") #Get URL
|
||||
url = url.replace("%RIC%", data["ric"]).replace("%FUNC%", data["function"]) #replace Wildcards in URL
|
||||
url = url.replace("%MSG%", data["msg"]).replace("%BITRATE%", data["bitrate"]) #replace Wildcards in URL
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
||||
#
|
||||
# HTTP-Request
|
||||
#
|
||||
url = urlparse(url) #split URL into path and querry
|
||||
httprequest = httplib.HTTPConnection(url[2]) #connect to URL Path
|
||||
httprequest.request("GET", url[5]) #send URL Querry per GET
|
||||
|
||||
except:
|
||||
logging.exception("cannot send HTTP request")
|
||||
else:
|
||||
if configHandler.checkConfig("httpRequest"): #read and debug the config
|
||||
|
||||
try:
|
||||
#
|
||||
# check HTTP-Response
|
||||
#
|
||||
httpresponse = httprequest.getresponse()
|
||||
if str(httpresponse.status) == "200": #Check HTTP Response an print a Log or Error
|
||||
logging.debug("HTTP response: %s - %s" , str(httpresponse.status), str(httpresponse.reason))
|
||||
# Create URL
|
||||
#
|
||||
if typ == "FMS":
|
||||
url = globals.config.get("httpRequest", "fms_url") #Get URL
|
||||
url = wildcardHandler.replaceWildcards(url, data) # replace wildcards with helper function
|
||||
elif typ == "ZVEI":
|
||||
url = globals.config.get("httpRequest", "zvei_url") #Get URL
|
||||
url = wildcardHandler.replaceWildcards(url, data) # replace wildcards with helper function
|
||||
elif typ == "POC":
|
||||
url = globals.config.get("httpRequest", "poc_url") #Get URL
|
||||
url = wildcardHandler.replaceWildcards(url, data) # replace wildcards with helper function
|
||||
|
||||
else:
|
||||
logging.warning("HTTP response: %s - %s" , str(httpresponse.status), str(httpresponse.reason))
|
||||
except: #otherwise
|
||||
logging.exception("cannot get HTTP response")
|
||||
|
||||
finally:
|
||||
logging.debug("close HTTP-Connection")
|
||||
httprequest.close()
|
||||
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
return
|
||||
|
||||
|
||||
#
|
||||
# HTTP-Request
|
||||
#
|
||||
logging.debug("send %s HTTP request", typ)
|
||||
url = urlparse(url) #split URL into path and querry
|
||||
httprequest = httplib.HTTPConnection(url[2]) #connect to URL Path
|
||||
httprequest.request("GET", url[5]) #send URL Querry per GET
|
||||
|
||||
except:
|
||||
logging.error("cannot send HTTP request")
|
||||
logging.debug("cannot send HTTP request", exc_info=True)
|
||||
return
|
||||
|
||||
else:
|
||||
try:
|
||||
#
|
||||
# check HTTP-Response
|
||||
#
|
||||
httpresponse = httprequest.getresponse()
|
||||
if str(httpresponse.status) == "200": #Check HTTP Response an print a Log or Error
|
||||
logging.debug("HTTP response: %s - %s" , str(httpresponse.status), str(httpresponse.reason))
|
||||
else:
|
||||
logging.warning("HTTP response: %s - %s" , str(httpresponse.status), str(httpresponse.reason))
|
||||
except: #otherwise
|
||||
logging.error("cannot get HTTP response")
|
||||
logging.debug("cannot get HTTP response", exc_info=True)
|
||||
return
|
||||
|
||||
finally:
|
||||
logging.debug("close HTTP-Connection")
|
||||
try:
|
||||
httprequest.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
except:
|
||||
logging.exception("unknown error")
|
||||
logging.error("unknown error")
|
||||
logging.debug("unknown error", exc_info=True)
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
Handover to Plugin:
|
||||
typ = [FMS|ZVEI|POC]
|
||||
freq = [Freq in Hz]
|
||||
data = {"KEY1":"VALUE1","KEY2":"VALUE2"}
|
||||
|
||||
|
||||
The following informations are included in the var "data".
|
||||
They can be used by their Index Names: data['OPTION']
|
||||
|
||||
ZVEI:
|
||||
- zvei
|
||||
- description
|
||||
|
||||
FMS:
|
||||
- fms
|
||||
- status
|
||||
- direction
|
||||
- directionText
|
||||
- tsi
|
||||
- description
|
||||
|
||||
POCSAG:
|
||||
- ric
|
||||
- function
|
||||
- functionChar
|
||||
- msg
|
||||
- bitrate
|
||||
- description
|
||||
|
||||
|
||||
Global Objects:
|
||||
|
||||
1.)
|
||||
import logging # Global logger
|
||||
Message into Log: logging.LOGLEVEL("MESSAGE")
|
||||
Loglevel: debug|info|warning|error|exception|critical
|
||||
|
||||
2.)
|
||||
import globals # Global variables
|
||||
reads Data from the config.ini
|
||||
VALUE = globals.config.get("SECTION", "OPTION")
|
||||
111
plugins/jsonSocket/jsonSocket.py
Normal file
111
plugins/jsonSocket/jsonSocket.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
jsonSocket-Plugin to dispatch FMS-, ZVEI- and POCSAG-messages via UDP/TCP
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: jsonSocket-Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
import socket # for connection
|
||||
import json # for data-transfer
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
from includes.helper import configHandler
|
||||
|
||||
##
|
||||
#
|
||||
# onLoad (init) function of plugin
|
||||
# will be called one time by the pluginLoader on start
|
||||
#
|
||||
def onLoad():
|
||||
"""
|
||||
While loading the plugins by pluginLoader.loadPlugins()
|
||||
this onLoad() routine is called one time for initialize the plugin
|
||||
|
||||
@requires: nothing
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
# nothing to do for this plugin
|
||||
return
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# Main function of jsonSocket-plugin
|
||||
# will be called by the alarmHandler
|
||||
#
|
||||
def run(typ,freq,data):
|
||||
"""
|
||||
This function is the implementation of the jsonSocket-Plugin.
|
||||
It will send the data via UDP/TCP
|
||||
|
||||
The configuration for the Connection is set in the config.ini.
|
||||
|
||||
@type typ: string (FMS|ZVEI|POC)
|
||||
@param typ: Typ of the dataset for sending via UDP/TCP
|
||||
@type data: map of data (structure see interface.txt)
|
||||
@param data: Contains the parameter for dispatch to UDP.
|
||||
@type freq: string
|
||||
@keyword freq: frequency of the SDR Stick
|
||||
|
||||
@requires: jsonSocket-Configuration has to be set in the config.ini
|
||||
|
||||
@return: nothing
|
||||
"""
|
||||
try:
|
||||
if configHandler.checkConfig("jsonSocket"): #read and debug the config
|
||||
|
||||
try:
|
||||
#
|
||||
# initialize to socket-Server
|
||||
#
|
||||
# SOCK_DGRAM is the socket type to use for UDP sockets
|
||||
# SOCK_STREAM is the socket type to use for TCP sockets
|
||||
if globals.config.get("jsonSocket", "protocol") == "TCP":
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect((globals.config.get("jsonSocket", "server"), globals.config.getint("jsonSocket", "port")))
|
||||
else:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
|
||||
except:
|
||||
logging.error("cannot initialize %s-socket", globals.config.get("jsonSocket", "protocol"))
|
||||
logging.debug("cannot initialize %s-socket", globals.config.get("jsonSocket", "protocol"), exc_info=True)
|
||||
# Without connection, plugin couldn't work
|
||||
return
|
||||
|
||||
else:
|
||||
# toDo is equals for all types, so only check if typ is supported
|
||||
supportedTypes = ["FMS", "ZVEI", "POC"]
|
||||
if typ in supportedTypes:
|
||||
logging.debug("Start %s to %s", typ, globals.config.get("jsonSocket", "protocol"))
|
||||
try:
|
||||
# dump data to json-string
|
||||
sendData = json.dumps(data)
|
||||
# send data
|
||||
sock.sendto(sendData, (globals.config.get("jsonSocket", "server"), globals.config.getint("jsonSocket", "port")))
|
||||
except:
|
||||
logging.error("%s to %s failed", typ, globals.config.get("jsonSocket", "protocol"))
|
||||
logging.debug("%s to %s failed", typ, globals.config.get("jsonSocket", "protocol"), exc_info=True)
|
||||
return
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
||||
finally:
|
||||
logging.debug("close %s-Connection", globals.config.get("jsonSocket", "protocol"))
|
||||
try:
|
||||
sock.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
except:
|
||||
# something very mysterious
|
||||
logging.error("unknown error")
|
||||
logging.debug("unknown error", exc_info=True)
|
||||
|
|
@ -1,50 +1,91 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
#########
|
||||
# USAGE
|
||||
#
|
||||
# Config
|
||||
# ======
|
||||
# to read a option from config File
|
||||
# VALUE = globals.config.get("SECTION", "OPTION")
|
||||
#
|
||||
# Data from boswatch.py
|
||||
# =====================
|
||||
# use data["KEY"] for Alarm Data from boswatch.py
|
||||
# for usable KEYs in different Functions (FMS|ZVEI|POC) see interface.txt
|
||||
#
|
||||
# LOG Messages
|
||||
# ============
|
||||
# send Log Messages with logging.LOGLEVEL("MESSAGE")
|
||||
# usable Loglevels debug|info|warning|error|exception|critical
|
||||
# if you use .exception in Try:Exception: Construct, it logs the Python EX.message too
|
||||
"""
|
||||
template plugin to show the function and usage of plugins
|
||||
feel free to edit to yout own plugin
|
||||
please edit theese desciption, the @author-Tag and the @requires-Tag
|
||||
For more information take a look into the other plugins
|
||||
|
||||
@author: Jens Herrmann
|
||||
@author: Bastian Schroll
|
||||
|
||||
@requires: none
|
||||
"""
|
||||
|
||||
#
|
||||
# Imports
|
||||
#
|
||||
import logging # Global logger
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
# Helper function, uncomment to use
|
||||
#from includes.helper import timeHandler
|
||||
#from includes.helper import wildcardHandler
|
||||
from includes.helper import configHandler
|
||||
|
||||
def run(typ,freq,data):
|
||||
##
|
||||
#
|
||||
# onLoad (init) function of plugin
|
||||
# will be called one time by the pluginLoader on start
|
||||
#
|
||||
def onLoad():
|
||||
"""
|
||||
While loading the plugins by pluginLoader.loadPlugins()
|
||||
this onLoad() routine is called one time for initialize the plugin
|
||||
|
||||
@requires: nothing
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if init has an fatal error so that the plugin couldn't work
|
||||
|
||||
"""
|
||||
try:
|
||||
#ConfigParser
|
||||
logging.debug("reading config file")
|
||||
try:
|
||||
for key,val in globals.config.items("template"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
|
||||
########## User Plugin CODE ##########
|
||||
if typ == "FMS":
|
||||
logging.warning("%s not supported", typ)
|
||||
elif typ == "ZVEI":
|
||||
logging.warning("%s not supported", typ)
|
||||
elif typ == "POC":
|
||||
logging.warning("%s not supported", typ)
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
########## User Plugin CODE ##########
|
||||
|
||||
########## User onLoad CODE ##########
|
||||
pass
|
||||
########## User onLoad CODE ##########
|
||||
except:
|
||||
logging.exception("unknown error")
|
||||
logging.error("unknown error")
|
||||
logging.debug("unknown error", exc_info=True)
|
||||
raise
|
||||
|
||||
##
|
||||
#
|
||||
# Main function of plugin
|
||||
# will be called by the alarmHandler
|
||||
#
|
||||
def run(typ,freq,data):
|
||||
"""
|
||||
This function is the implementation of the Plugin.
|
||||
|
||||
If necessary the configuration hast to be set in the config.ini.
|
||||
|
||||
@type typ: string (FMS|ZVEI|POC)
|
||||
@param typ: Typ of the dataset
|
||||
@type data: map of data (structure see interface.txt)
|
||||
@param data: Contains the parameter for dispatch
|
||||
@type freq: string
|
||||
@keyword freq: frequency of the SDR Stick
|
||||
|
||||
@requires: If necessary the configuration hast to be set in the config.ini.
|
||||
|
||||
@return: nothing
|
||||
@exception: nothing, make sure this function will never thrown an exception
|
||||
"""
|
||||
try:
|
||||
if configHandler.checkConfig("template"): #read and debug the config (let empty if no config used)
|
||||
|
||||
########## User Plugin CODE ##########
|
||||
if typ == "FMS":
|
||||
logging.warning("%s not supported", typ)
|
||||
elif typ == "ZVEI":
|
||||
logging.warning("%s not supported", typ)
|
||||
elif typ == "POC":
|
||||
logging.warning("%s not supported", typ)
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
########## User Plugin CODE ##########
|
||||
|
||||
except:
|
||||
logging.error("unknown error")
|
||||
logging.debug("unknown error", exc_info=True)
|
||||
|
|
|
|||
148
serverExamples/jsonSocketServer.py
Normal file
148
serverExamples/jsonSocketServer.py
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
#
|
||||
|
||||
"""
|
||||
jsonSocketServer
|
||||
This is a small example of an jsonSocketServer for receive alarm-messages from BOSWatch.
|
||||
The jsonSocketServer controlls an pibrella-bord in case of received POCSAG-RIC
|
||||
|
||||
Implemented functions:
|
||||
- asynchronous service for alarm-sound
|
||||
- green LED if jsonSocketServer is running
|
||||
- green LED is blinking if Dau-Test-RIC was received
|
||||
- yellow LED is blinking if our RICs is reveived with functioncode "a"
|
||||
- red LED is blinking in case of an alarm (our RICs with functioncode "b")
|
||||
- siren will run with the pack
|
||||
- press Pibrella button to stop alarm and reset the LEDs
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
BOSWatch: https://github.com/Schrolli91/BOSWatch
|
||||
Pibrella: https://github.com/pimoroni/pibrella
|
||||
"""
|
||||
|
||||
# no IP for server necessary
|
||||
IP = ""
|
||||
# listen on port
|
||||
PORT = 8112
|
||||
|
||||
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
import socket # for udp-socket
|
||||
import pibrella # for pi-board
|
||||
import json # for data
|
||||
|
||||
|
||||
#
|
||||
# Eventhandler for button
|
||||
# will stop the alarm and reset the LEDs
|
||||
#
|
||||
def button_pressed(pin):
|
||||
global siren_stopped
|
||||
import pibrella
|
||||
pibrella.light.off()
|
||||
pibrella.light.green.on()
|
||||
siren_stopped = True
|
||||
# load Eventhandler
|
||||
pibrella.button.pressed(button_pressed)
|
||||
|
||||
#
|
||||
# Siren-control
|
||||
#
|
||||
|
||||
# normally we have no alarm, siren-control-var is True
|
||||
siren_stopped = True
|
||||
|
||||
# asynchronous siren:
|
||||
def siren():
|
||||
import time
|
||||
if siren_stopped == True:
|
||||
pibrella.buzzer.stop()
|
||||
return True
|
||||
for x in xrange(-30,30,2):
|
||||
pibrella.buzzer.note(x)
|
||||
time.sleep(0.01)
|
||||
for x in reversed(xrange(-30,30,2)):
|
||||
pibrella.buzzer.note(x)
|
||||
time.sleep(0.01)
|
||||
# start asynchronous siren
|
||||
pibrella.async_start('siren',siren)
|
||||
|
||||
|
||||
#
|
||||
# Main Program
|
||||
#
|
||||
try:
|
||||
# Logging
|
||||
myLogger = logging.getLogger()
|
||||
myLogger.setLevel(logging.DEBUG)
|
||||
formatter = logging.Formatter('%(asctime)s [%(levelname)-8s] %(message)s', '%d.%m.%Y %H:%M:%S')
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.DEBUG)
|
||||
ch.setFormatter(formatter)
|
||||
myLogger.addHandler(ch)
|
||||
|
||||
# Start TCP socket:
|
||||
logging.debug("Start jsonSocketServer")
|
||||
sock = socket.socket()
|
||||
sock.bind((IP,PORT))
|
||||
sock.listen(2)
|
||||
logging.info("jsonSocketServer runs")
|
||||
pibrella.light.green.on()
|
||||
|
||||
# our Alarm-RICs:
|
||||
ric_alarm = [12345677, 12345676, 12345675]
|
||||
|
||||
while True:
|
||||
# accept connections from outside
|
||||
(clientsocket, address) = sock.accept()
|
||||
logging.debug("connected client: %s", address)
|
||||
|
||||
# receive message as json string
|
||||
json_string = clientsocket.recv( 4096 ) # buffer size is 4096 bytes
|
||||
try:
|
||||
# parse json
|
||||
parsed_json = json.loads(json_string)
|
||||
logging.debug("parsed message: %s", parsed_json)
|
||||
except ValueError:
|
||||
# parsing error is foolish, but we don't have to exit
|
||||
logging.warning("No JSON object could be decoded: %s", json_string)
|
||||
pass
|
||||
else:
|
||||
# DAU-Test-RIC received
|
||||
if parsed_json['ric'] == "1234567":
|
||||
logging.debug("POCSAG is alive")
|
||||
pibrella.light.green.blink(1, 1)
|
||||
|
||||
elif int(parsed_json['ric']) in ric_alarm:
|
||||
logging.debug("We have do to something")
|
||||
if parsed_json['functionChar'] == "a":
|
||||
logging.info("-> Probealarm: %", parsed_json['ric'])
|
||||
pibrella.light.yellow.blink(1, 1)
|
||||
elif parsed_json['functionChar'] == "b":
|
||||
logging.info("-> Alarm: %", parsed_json['ric'])
|
||||
pibrella.light.red.blink(1, 1)
|
||||
# change variable to False to start the siren
|
||||
siren_stopped = False
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.warning("Keyboard Interrupt")
|
||||
except:
|
||||
logging.exception("unknown error")
|
||||
finally:
|
||||
try:
|
||||
logging.debug("socketServer shuting down")
|
||||
sock.close()
|
||||
logging.debug("socket closed")
|
||||
logging.debug("exiting socketServer")
|
||||
except:
|
||||
logging.warning("failed in clean-up routine")
|
||||
finally:
|
||||
logging.debug("close Logging")
|
||||
logging.info("socketServer exit()")
|
||||
logging.shutdown()
|
||||
ch.close()
|
||||
exit(0)
|
||||
28
service/README.md
Normal file
28
service/README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
### Start BOSWatch as a daemon
|
||||
|
||||
##### Changing the init script
|
||||
|
||||
Lines 14 and 15 define where to find the Python script.
|
||||
In this case the script expects that there is a folder `/usr/local/bin/BOSWatch` and that the script is inside there.
|
||||
|
||||
Line 23 sets what user to run the script as. Using a root-user is necessary for BOSWatch.
|
||||
|
||||
Line 19 sets the parameters for BOSWatch, use the same as starting BOSWatch from the shell.
|
||||
We recommend to use "-u" and "-q" when you want to run BOSWatch as a daemon.
|
||||
- "-u": You will find the logfiles in `/var/log/BOSWatch`
|
||||
- "-q": Shows no information. Only logfiles
|
||||
|
||||
##### Using the init script
|
||||
|
||||
To actually use this script, put BOSWatch where you want (recommend `/usr/local/bin/BOSWatch`)
|
||||
and make sure it is executable (e.g. `sudo chmod 755 boswatch.py`).
|
||||
Edit the init script accordingly. Copy it into /etc/init.d using e.g. `sudo cp boswatch.sh /etc/init.d`.
|
||||
Make sure the script is executable (chmod again) and make sure that it has UNIX line-endings.
|
||||
|
||||
At this point you should be able to start BOSWatchcd ~/srt using the command `sudo /etc/init.d/boswatch.sh start`,
|
||||
check its status with the `sudo /etc/init.d/boswatch.sh status` argument and stop it with `sudo /etc/init.d/boswatch.sh stop`.
|
||||
|
||||
To make the Raspberry Pi use your init script at the right time, one more step is required:
|
||||
Running the command `sudo update-rc.d boswatch.sh defaults`.
|
||||
This command adds in symbolic links to the /etc/rc.x directories so that the init script is run at the default times.
|
||||
You can see these links if you do `ls -l /etc/rc?.d/*boswatch.sh`
|
||||
62
service/boswatch.sh
Executable file
62
service/boswatch.sh
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
#!/bin/sh
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: BOSWatch
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Python Service to receive and decode German BOS Information with rtl_fm and multimon-NG
|
||||
# Description: Python Service to receive and decode German BOS Information with rtl_fm and multimon-NG
|
||||
### END INIT INFO
|
||||
|
||||
# Change the next 3 lines to suit where you install your script and what you want to call it
|
||||
DIR=/usr/local/bin/BOSWatch
|
||||
DAEMON=$DIR/boswatch.py
|
||||
DAEMON_NAME=boswatch
|
||||
|
||||
# Add any command line options for your daemon here
|
||||
DAEMON_OPTS="-f xxx -a yyy -s zz -u -q"
|
||||
|
||||
# This next line determines what user the script runs as.
|
||||
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
|
||||
DAEMON_USER=root
|
||||
|
||||
# The process ID of the script when it runs is stored here:
|
||||
PIDFILE=/var/run/$DAEMON_NAME.pid
|
||||
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
do_start () {
|
||||
log_daemon_msg "Starting system $DAEMON_NAME daemon"
|
||||
start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS
|
||||
log_end_msg $?
|
||||
}
|
||||
do_stop () {
|
||||
log_daemon_msg "Stopping system $DAEMON_NAME daemon"
|
||||
start-stop-daemon --stop --pidfile $PIDFILE --retry 10
|
||||
log_end_msg $?
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
|
||||
start|stop)
|
||||
do_${1}
|
||||
;;
|
||||
|
||||
restart|reload|force-reload)
|
||||
do_stop
|
||||
do_start
|
||||
;;
|
||||
|
||||
status)
|
||||
status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
exit 0
|
||||
7
testdata/rt_fm errors.txt
vendored
Normal file
7
testdata/rt_fm errors.txt
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
Error Messages from RTL_FM
|
||||
|
||||
fprintf(stderr, "Signal caught, exiting!\n");
|
||||
fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dongle.dev_index);
|
||||
fprintf(stderr, "Failed to open %s\n", output.filename);
|
||||
fprintf(stderr, "\nUser cancel, exiting...\n");
|
||||
fprintf(stderr, "\nLibrary error %d, exiting...\n", r);
|
||||
120
testdata/testdata.txt
vendored
Normal file
120
testdata/testdata.txt
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
# Testdata for the BOSWatch Test Mode function
|
||||
# Data in Multimon-NG Raw Format
|
||||
# Data is alternately passed to the decoder to simulate an used Radio-Frequency
|
||||
|
||||
#
|
||||
# POCSAG
|
||||
# ------
|
||||
#
|
||||
# The following settings in config.ini are expected for POCSAG
|
||||
#
|
||||
# [BOSWatch]
|
||||
# useDescription = 1
|
||||
# doubleFilter_ignore_entries = 10
|
||||
# doubleFilter_check_msg = 1
|
||||
#
|
||||
# [POC]
|
||||
# deny_ric = 7777777
|
||||
# filter_range_start = 0000005
|
||||
# filter_range_end = 8999999
|
||||
# idDescribed = 1
|
||||
#
|
||||
|
||||
# bitrate
|
||||
POCSAG512: Address: 1000512 Function: 1 Alpha: BOSWatch-Test ÖÄÜß: okay
|
||||
POCSAG1200: Address: 1001200 Function: 1 Alpha: BOSWatch-Test: okay
|
||||
POCSAG2400: Address: 1002400 Function: 1 Alpha: BOSWatch-Test: okay
|
||||
|
||||
# function-code
|
||||
POCSAG512: Address: 1000000 Function: 0 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 1000001 Function: 1 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 1000002 Function: 2 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 1000003 Function: 3 Alpha: BOSWatch-Test: okay
|
||||
|
||||
# german special sign
|
||||
POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test ÖÄÜß: okay
|
||||
POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test öäü: okay
|
||||
|
||||
# witch csv
|
||||
POCSAG512: Address: 1234567 Function: 1 Alpha: BOSWatch-Test: with csv
|
||||
|
||||
# without csv
|
||||
POCSAG1200: Address: 2345678 Function: 2 Alpha: BOSWatch-Test: without csv
|
||||
POCSAG2400: Address: 3456789 Function: 3 Alpha: BOSWatch-Test: without csv
|
||||
|
||||
# OHNE TEXT????
|
||||
POCSAG1200: Address: 1100000 Function: 0
|
||||
POCSAG1200: Address: 1100000 Function: 1
|
||||
POCSAG1200: Address: 1100000 Function: 2
|
||||
POCSAG1200: Address: 1100000 Function: 3
|
||||
|
||||
# duplicate with same and other msg
|
||||
POCSAG1200: Address: 2000001 Function: 2 Alpha: BOSWatch-Test: second is a duplicate
|
||||
POCSAG1200: Address: 2000001 Function: 2 Alpha: BOSWatch-Test: second is a duplicate
|
||||
POCSAG1200: Address: 2000001 Function: 2 Alpha: BOSWatch-Testing: okay
|
||||
|
||||
# duplicate in different order
|
||||
POCSAG1200: Address: 2100000 Function: 2
|
||||
POCSAG1200: Address: 2100001 Function: 2
|
||||
POCSAG1200: Address: 2100002 Function: 2
|
||||
POCSAG1200: Address: 2100000 Function: 2
|
||||
POCSAG1200: Address: 2100001 Function: 2
|
||||
POCSAG1200: Address: 2100002 Function: 2
|
||||
POCSAG1200: Address: 2100000 Function: 2 Alpha: BOSWatch-Test: second is a duplicate
|
||||
POCSAG1200: Address: 2100001 Function: 2 Alpha: BOSWatch-Test: second is a duplicate
|
||||
POCSAG1200: Address: 2100002 Function: 2 Alpha: BOSWatch-Test: second is a duplicate
|
||||
POCSAG1200: Address: 2100000 Function: 2 Alpha: BOSWatch-Test: second is a duplicate
|
||||
POCSAG1200: Address: 2100001 Function: 2 Alpha: BOSWatch-Test: second is a duplicate
|
||||
POCSAG1200: Address: 2100002 Function: 2 Alpha: BOSWatch-Test: second is a duplicate
|
||||
|
||||
# invalid
|
||||
POCSAG512: Address: 3 Function: 0 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 33 Function: 0 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 333 Function: 0 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 3333 Function: 0 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 33333 Function: 0 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 333333 Function: 0 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 3333333 Function: 0 Alpha: BOSWatch-Test: okay
|
||||
POCSAG512: Address: 333333F Function: 0 Alpha: BOSWatch-Test: invalid
|
||||
POCSAG512: Address: 333333F Function: 1 Alpha: BOSWatch-Test: invalid
|
||||
POCSAG512: Address: 3333333 Function: 4 Alpha: BOSWatch-Test: invalid
|
||||
|
||||
# denied
|
||||
POCSAG1200: Address: 7777777 Function: 1 Alpha: BOSWatch-Test: denied
|
||||
|
||||
# out of filter Range
|
||||
POCSAG1200: Address: 0000004 Function: 1 Alpha: BOSWatch-Test: out of filter start
|
||||
POCSAG1200: Address: 9000000 Function: 1 Alpha: BOSWatch-Test: out of filter end
|
||||
|
||||
# regEx-Filter?
|
||||
|
||||
|
||||
#
|
||||
# FMS
|
||||
# ---
|
||||
#
|
||||
FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=I (ohneNA,ohneSIGNAL)) CRC correct
|
||||
FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=I (ohneNA,ohneSIGNAL)) CRC correct
|
||||
FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=II (ohneNA,mit SIGNAL)) CRC correct
|
||||
FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=III(mit NA,ohneSIGNAL)) CRC correct
|
||||
FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=IV (mit NA,mit SIGNAL)) CRC correct
|
||||
|
||||
|
||||
#
|
||||
# ZVEI
|
||||
# ----
|
||||
#
|
||||
|
||||
#with csv description
|
||||
ZVEI2: 12345
|
||||
#without csv description
|
||||
ZVEI2: 56789
|
||||
#duplicate
|
||||
ZVEI2: 56789
|
||||
#with repeat Tone
|
||||
ZVEI2: 1F2F3
|
||||
#in case of invalid id
|
||||
ZVEI2: 135
|
||||
#in case of a double-tone for siren n-'D's are sended
|
||||
ZVEI2: DDD
|
||||
ZVEI2: DDDDD
|
||||
|
|
@ -9,4 +9,4 @@ $tableFMS = "bos_fms";
|
|||
$tableZVEI = "bos_zvei";
|
||||
$tablePOC = "bos_pocsag";
|
||||
|
||||
?>
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -17,12 +17,12 @@ $db = new Database($dbhost, $dbuser, $dbpassword, $database, 1); //Show Error =
|
|||
|
||||
<div style="text-align: center; width: 1250px; margin: 0px auto;">
|
||||
|
||||
<img src="gfx/logo.png" alt="BOSWatch"><br>
|
||||
<a href="index.php?overview">[Übersicht]</a> - <a href="index.php?parser">[Parser]</a>
|
||||
|
||||
<br><br>
|
||||
<img src="gfx/logo.png" alt="BOSWatch"><br>
|
||||
<a href="index.php?overview">[<EFBFBD>bersicht]</a> - <a href="index.php?parser">[Parser]</a>
|
||||
|
||||
<br><br>
|
||||
<?php
|
||||
|
||||
|
||||
if(isset($_GET['overview']))
|
||||
{
|
||||
include("tpl/content.overview.php");
|
||||
|
|
@ -38,9 +38,9 @@ $db = new Database($dbhost, $dbuser, $dbpassword, $database, 1); //Show Error =
|
|||
include("tpl/content.overview.php");
|
||||
include("tpl/template.overview.php");
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
?>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ Simple Database Class (C) by Bastian Schroll
|
|||
|
||||
/**
|
||||
* Database::__construct()
|
||||
*
|
||||
*
|
||||
* Stellt eine Verbung mit der MySQL Datenbank fest
|
||||
*
|
||||
*
|
||||
* @param mixed $host Hostname des Datenbank Server
|
||||
* @param mixed $user Username des Datenbank Nutzers
|
||||
* @param mixed $password Passwort des Datenbank Nutzers
|
||||
|
|
@ -40,10 +40,10 @@ Simple Database Class (C) by Bastian Schroll
|
|||
|
||||
/**
|
||||
* Database::query()
|
||||
*
|
||||
* Führt einen MySQL Query aus
|
||||
*
|
||||
* @param mixed $query Auszuführender Query
|
||||
*
|
||||
* F<EFBFBD>hrt einen MySQL Query aus
|
||||
*
|
||||
* @param mixed $query Auszuf<EFBFBD>hrender Query
|
||||
* @return Result-Handler/FALSE
|
||||
*/
|
||||
function query($query)
|
||||
|
|
@ -59,11 +59,11 @@ Simple Database Class (C) by Bastian Schroll
|
|||
|
||||
/**
|
||||
* Database::fetchAssoc()
|
||||
*
|
||||
* Liefert alle gefundnen Datensätze als Assoc
|
||||
*
|
||||
*
|
||||
* Liefert alle gefundnen Datens<EFBFBD>tze als Assoc
|
||||
*
|
||||
* @param mixed $result Externer Result-Handler
|
||||
* @return gefundene Datensätze als Assoc
|
||||
* @return gefundene Datens<EFBFBD>tze als Assoc
|
||||
*/
|
||||
function fetchAssoc($result = null)
|
||||
{
|
||||
|
|
@ -78,11 +78,11 @@ Simple Database Class (C) by Bastian Schroll
|
|||
|
||||
/**
|
||||
* Database::count()
|
||||
*
|
||||
* Zählt alle gefundenen Datensätze
|
||||
*
|
||||
*
|
||||
* Z<EFBFBD>hlt alle gefundenen Datens<EFBFBD>tze
|
||||
*
|
||||
* @param mixed $result Externer Result-Handler
|
||||
* @return Anzahl gefundener Datensätze
|
||||
* @return Anzahl gefundener Datens<EFBFBD>tze
|
||||
*/
|
||||
function count($result = null)
|
||||
{
|
||||
|
|
@ -97,9 +97,9 @@ Simple Database Class (C) by Bastian Schroll
|
|||
|
||||
/**
|
||||
* Database::closeConnection()
|
||||
*
|
||||
* Schließt die bestehende MySQL Verbindung
|
||||
*
|
||||
*
|
||||
* Schlie<EFBFBD>t die bestehende MySQL Verbindung
|
||||
*
|
||||
* @return TRUE/FALSE
|
||||
*/
|
||||
function closeConnection()
|
||||
|
|
@ -114,9 +114,9 @@ Simple Database Class (C) by Bastian Schroll
|
|||
|
||||
/**
|
||||
* Database::error()
|
||||
*
|
||||
*
|
||||
* Gibt eine Interne Fehlermeldung aus
|
||||
*
|
||||
*
|
||||
* @param mixed $error_msg Text der Fehlermeldung
|
||||
* @param mixed $sql_err MySQL Fehlermeldung per mysql_error()
|
||||
* @return NULL
|
||||
|
|
@ -131,4 +131,4 @@ Simple Database Class (C) by Bastian Schroll
|
|||
}
|
||||
}
|
||||
|
||||
} ?>
|
||||
} ?>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
$Rows[] = $daten;
|
||||
}
|
||||
$tpl['fms'] = $Rows;
|
||||
|
||||
|
||||
//read ZVEI
|
||||
$db->query("SELECT id, time, zvei FROM ".$tableZVEI." ORDER BY id DESC LIMIT 50");
|
||||
$Rows = array();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<?php
|
||||
|
||||
?>
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Last alarms for FMS and ZVEI (max. 50)<br><br>
|
||||
|
||||
|
||||
<div style="float: left; width: 800px;">
|
||||
<b>Last FMS alarms</b>
|
||||
<table border="1" style="width: 800px;">
|
||||
|
|
@ -11,13 +11,13 @@ Last alarms for FMS and ZVEI (max. 50)<br><br>
|
|||
<td>Richt.</td>
|
||||
<td>TKI</td>
|
||||
</tr>
|
||||
<?php
|
||||
<?php
|
||||
foreach ($tpl['fms'] as $fms)
|
||||
{
|
||||
|
||||
|
||||
$time = strtotime($fms['time']);
|
||||
$time = date("d.m.Y H:i:s", $time);
|
||||
|
||||
|
||||
echo "<tr>";
|
||||
echo "<td>". $fms['id'] . "</td>";
|
||||
echo "<td>". $time . "</td>";
|
||||
|
|
@ -39,13 +39,13 @@ Last alarms for FMS and ZVEI (max. 50)<br><br>
|
|||
<td>Funktion</td>
|
||||
<td>Text</td>
|
||||
</tr>
|
||||
<?php
|
||||
<?php
|
||||
foreach ($tpl['poc'] as $poc)
|
||||
{
|
||||
|
||||
|
||||
$time = strtotime($poc['time']);
|
||||
$time = date("d.m.Y H:i:s", $time);
|
||||
|
||||
|
||||
echo "<tr>";
|
||||
echo "<td>". $poc['id'] . "</td>";
|
||||
echo "<td>". $time . "</td>";
|
||||
|
|
@ -57,7 +57,7 @@ Last alarms for FMS and ZVEI (max. 50)<br><br>
|
|||
?>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div style="float: right; width: 400px;">
|
||||
<b>Last ZVEI alarms</b>
|
||||
<table border="1" style="width: 400px;">
|
||||
|
|
@ -66,13 +66,13 @@ Last alarms for FMS and ZVEI (max. 50)<br><br>
|
|||
<td>Datum - Zeit</td>
|
||||
<td>Schleife</td>
|
||||
</tr>
|
||||
<?php
|
||||
<?php
|
||||
foreach ($tpl['zvei'] as $zvei)
|
||||
{
|
||||
|
||||
|
||||
$time = strtotime($zvei['time']);
|
||||
$time = date("d.m.Y H:i:s", $time);
|
||||
|
||||
|
||||
echo "<tr>";
|
||||
echo "<td>". $zvei['id'] . "</td>";
|
||||
echo "<td>". $time . "</td>";
|
||||
|
|
|
|||
Loading…
Reference in a new issue