mirror of
https://github.com/Schrolli91/BOSWatch.git
synced 2025-12-06 07:42:03 +01:00
Merge branch 'develop'
This commit is contained in:
commit
dead180506
89
README.md
89
README.md
|
|
@ -12,54 +12,75 @@ The software was developed using the Multimon-NG code, a function in the real op
|
|||
unless you are developer you can use the develop-Branch - may be unstable!
|
||||
|
||||
### Features
|
||||
##### Implemented Features:
|
||||
- FMS, ZVEI and POCSAG512/1200/2400 decoding and Displaying
|
||||
- Plugin support for easy Functions extension
|
||||
##### Implemented features:
|
||||
- FMS, ZVEI and POCSAG512/1200/2400 decoding and displaying
|
||||
- Plugin support for easy functional extension
|
||||
- Filtering double alarms with adjustable time
|
||||
- Filtering Range of POCSAG RIC´s
|
||||
- Filtering Data for each Typ/Plugin combination
|
||||
- All configurations in seperate config File
|
||||
- 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)
|
||||
- Logfiles for better Troubleshooting
|
||||
- verbose/quiet Mode for more/none information
|
||||
- Logfiles for better troubleshooting
|
||||
- verbose/quiet mode for more/none information
|
||||
|
||||
##### Features for the Future:
|
||||
- more Plugins
|
||||
##### Features for the future:
|
||||
- more plugins
|
||||
|
||||
|
||||
###Plugins
|
||||
##### Implemented Plugins:
|
||||
- MySQL (insert Data into MySQL Database [FMS|ZVEI|POC])
|
||||
- BosMon (send Data to BosMon Server [ZVEI|POC])
|
||||
- httpRequest (send a request to an URL [FMS|ZVEI|POC])
|
||||
##### 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])
|
||||
|
||||
- for more Information to the plugins see `config.ini`
|
||||
|
||||
##### Plugins for the Future:
|
||||
- E-mail Notification
|
||||
- Other Ideas per Issues
|
||||
- Ideas per Issues please
|
||||
|
||||
|
||||
### Configuration
|
||||
##### boswatch.py
|
||||
Take a look into the Folder /config/
|
||||
Take a look into the folder /config/
|
||||
Rename `config.template.ini` to `config.ini`
|
||||
In the Section `[BOSWatch]` you can set double_alarm_time etc.
|
||||
In the Section `[Plugins]` you can activate or deactivate the Plugins
|
||||
For each Plugin that requires configurations a own Section with his Name is available
|
||||
For each plugin that requires configurations, a own Section with his name is available
|
||||
|
||||
For the other Functions see "Usage" below.
|
||||
For the other functions see "Usage" below.
|
||||
|
||||
##### Web Frontend
|
||||
Put the Files in Folder /wwww/ into your local Webserver Folder (/var/www/).
|
||||
Now you must edit the "config.php" with your Userdata to your local Database.
|
||||
For the Parsing Functions take a look into the parser.php
|
||||
##### 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
|
||||
|
||||
only ZVEI to all plugins with 25### at 85.5MHz
|
||||
testfilter = ZVEI;zvei;*;85500000;25[0-9]{3}
|
||||
|
||||
only POCSAG to MySQL with the text "ALARM:" in the message
|
||||
pocTest = POC;msg;MySQL;*;ALARM:
|
||||
|
||||
##### Web frontend
|
||||
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
|
||||
|
||||
|
||||
### Usage
|
||||
`sudo python boswatch.py -f 85.235M -a FMS ZVEI`
|
||||
Starts boswatch at Frequency 85.235 MHz with the Demodulation Functions FMS and ZVEI.
|
||||
Starts boswatch at frequency 85.235 MHz with the demodulation functions FMS and ZVEI.
|
||||
Parameter -f/--freq and -a/--demod are required!
|
||||
|
||||
Help to all usable Parameters with `sudo python boswatch.py -h`
|
||||
Help to all usable parameters with `sudo python boswatch.py -h`
|
||||
|
||||
```
|
||||
usage: boswatch.py [-h] -f FREQ [-d DEVICE] [-e ERROR] -a
|
||||
|
|
@ -70,28 +91,28 @@ 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)
|
||||
-e ERROR, --error ERROR Frequency-Error of your Device in PPM
|
||||
-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
|
||||
-v, --verbose Shows more Information
|
||||
-q, --quiet Shows no Information. Only Logfiles
|
||||
Demodulation functions
|
||||
-s SQUELCH, --squelch SQUELCH level of squelch
|
||||
-v, --verbose Shows more information
|
||||
-q, --quiet Shows no information. Only logfiles
|
||||
```
|
||||
|
||||
|
||||
### Installation
|
||||
You can easy install BOSWatch with the install.sh Script.
|
||||
- Download the install.sh in any Folder you want.
|
||||
- Download the install.sh in any folder you want.
|
||||
- Make it executeable `sudo chmod +x install.sh`
|
||||
- And use the script `sudo sh install.sh`
|
||||
|
||||
Now the script downloads and compile all needed data.
|
||||
At the end you can find BOSWatch in `~/boswatch/`
|
||||
Rename `config.template.ini` to `config.ini` and configure
|
||||
In case of an Error during the Installation, check the Logfile in `~/boswatch/install/setup_log.txt`
|
||||
In case of an error during the installation, check the logfile in `~/boswatch/install/setup_log.txt`
|
||||
|
||||
Caution, script don't install a Webserver with PHP and MySQL.
|
||||
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.
|
||||
|
||||
|
||||
|
|
@ -99,4 +120,4 @@ So you have to make up manually if you want to use MySQL support.
|
|||
- RTL_SDR (rtl_fm)
|
||||
- Multimon-NG
|
||||
- Python Support
|
||||
- MySQL Connector for Python
|
||||
- MySQL Connector for Python (for MySQL-plugin)
|
||||
|
|
|
|||
380
boswatch.py
380
boswatch.py
|
|
@ -1,14 +1,20 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
#
|
||||
"""
|
||||
BOSWatch
|
||||
Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG
|
||||
Through a simple plugin system, data can easily be transferred to other applications
|
||||
For more Information see the README.md
|
||||
|
||||
##### Info #####
|
||||
# BOSWatch
|
||||
# Autor: Bastian Schroll
|
||||
# Python Script to receive and decode German BOS Information with rtl_fm and multimon-NG
|
||||
# For more Information see the README.md
|
||||
##### Info #####
|
||||
@author: Bastian Schroll
|
||||
@author: Jens Herrmann
|
||||
|
||||
GitHUB: https://github.com/Schrolli91/BOSWatch
|
||||
"""
|
||||
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
import argparse #for parse the args
|
||||
import ConfigParser #for parse the config file
|
||||
|
|
@ -18,43 +24,117 @@ import subprocess
|
|||
|
||||
from includes import globals # Global variables
|
||||
|
||||
# Programm
|
||||
##
|
||||
#
|
||||
# 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()")
|
||||
|
||||
|
||||
#
|
||||
# ArgParser
|
||||
#
|
||||
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",
|
||||
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)
|
||||
parser.add_argument("-d", "--device", help="Device you want to use (Check with rtl_test)", type=int, default=0)
|
||||
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("-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()
|
||||
except SystemExit:
|
||||
# -h or --help called, exit right now
|
||||
exit(0)
|
||||
except:
|
||||
print "cannot parsing the arguments"
|
||||
|
||||
|
||||
#
|
||||
# Main Programm
|
||||
#
|
||||
try:
|
||||
try:
|
||||
#create logger
|
||||
# initialization
|
||||
try:
|
||||
#
|
||||
# Script-pathes
|
||||
#
|
||||
globals.script_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
#
|
||||
# If necessary create Log-Path
|
||||
#
|
||||
if not os.path.exists(globals.script_path+"/log/"):
|
||||
os.mkdir(globals.script_path+"/log/")
|
||||
|
||||
#create new logger
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.DEBUG)
|
||||
#
|
||||
# Create new myLogger...
|
||||
#
|
||||
myLogger = logging.getLogger()
|
||||
myLogger.setLevel(logging.DEBUG)
|
||||
#set log string format
|
||||
formatter = logging.Formatter('%(asctime)s - %(module)-12s [%(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 = logging.FileHandler(globals.script_path+"/log/boswatch.log", "w")
|
||||
fh.setLevel(logging.DEBUG) #log level >= Debug
|
||||
fh = MyTimedRotatingFileHandler(globals.script_path+"/log/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.setFormatter(formatter)
|
||||
logger.addHandler(fh)
|
||||
myLogger.addHandler(fh)
|
||||
#create a display logger
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.INFO) #log level >= info
|
||||
#log level for display >= info
|
||||
#will be changed later after parsing args
|
||||
ch.setLevel(logging.INFO)
|
||||
ch.setFormatter(formatter)
|
||||
logger.addHandler(ch)
|
||||
myLogger.addHandler(ch)
|
||||
except:
|
||||
logging.exception("cannot create logger")
|
||||
else:
|
||||
# initialization of the logging was fine, continue...
|
||||
|
||||
try:
|
||||
#clear log
|
||||
bos_log = open(globals.script_path+"/log/boswatch.log", "w")
|
||||
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")
|
||||
bos_log.write("")
|
||||
rtl_log.write("")
|
||||
mon_log.write("")
|
||||
bos_log.close()
|
||||
rtl_log.close()
|
||||
mon_log.close()
|
||||
logging.debug("BOSWatch has started")
|
||||
|
|
@ -62,129 +142,146 @@ try:
|
|||
except:
|
||||
logging.exception("cannot clear Logfiles")
|
||||
|
||||
try:
|
||||
#parse args
|
||||
logging.debug("parse args")
|
||||
#With -h or --help you get the Args help
|
||||
#ArgsParser
|
||||
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 this Folder")
|
||||
#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)
|
||||
parser.add_argument("-d", "--device", help="Device you want to use (Check with rtl_test)", type=int, default=0)
|
||||
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("-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()
|
||||
except:
|
||||
logging.error("cannot parse args")
|
||||
else:
|
||||
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)
|
||||
|
||||
try:
|
||||
#display/log args
|
||||
logging.debug(" - Frequency: %s", 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: P")
|
||||
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)
|
||||
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")
|
||||
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
|
||||
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("BOSWatch"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
try:
|
||||
#
|
||||
# Read config.ini
|
||||
#
|
||||
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("BOSWatch"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
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 read config file")
|
||||
else:
|
||||
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:
|
||||
#set the loglevel of the file handler
|
||||
logging.debug("set loglevel of fileHandler")
|
||||
fh.setLevel(globals.config.getint("BOSWatch","loglevel"))
|
||||
#
|
||||
# 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 set loglevel of fileHandler")
|
||||
|
||||
#load plugins
|
||||
from includes import pluginLoader
|
||||
pluginLoader.loadPlugins()
|
||||
|
||||
#load filters
|
||||
from includes import filter
|
||||
filter.getFilters()
|
||||
|
||||
try:
|
||||
#start rtl_fm
|
||||
logging.debug("starting rtl_fm")
|
||||
rtl_fm = subprocess.Popen("rtl_fm -d "+str(args.device)+" -f "+str(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:
|
||||
logging.exception("cannot start multimon-ng")
|
||||
else:
|
||||
# multimon-ng 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:
|
||||
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
|
||||
|
||||
logging.debug("start decoding")
|
||||
# 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)
|
||||
|
||||
while True:
|
||||
#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'
|
||||
decoded = str(multimon_ng.stdout.readline()) #Get line data from multimon stdout
|
||||
|
||||
#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=III(mit NA,ohneSIGNAL)) CRC correct\n'"
|
||||
#decoded = "POCSAG1200: Address: 1234567 Function: 1 Alpha: Hello World"
|
||||
#time.sleep(1)
|
||||
|
||||
from includes import decoder
|
||||
decoder.decode(args.freq, decoded)
|
||||
from includes import decoder
|
||||
decoder.decode(freqToHz(args.freq), decoded)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logging.warning("Keyboard Interrupt")
|
||||
|
|
@ -200,6 +297,11 @@ finally:
|
|||
logging.debug("exiting BOSWatch")
|
||||
except:
|
||||
logging.warning("failed in clean-up routine")
|
||||
finally:
|
||||
logging.info("BOSWatch exit()")
|
||||
exit(0)
|
||||
finally:
|
||||
# Close Logging
|
||||
logging.debug("close Logging")
|
||||
logging.info("BOSWatch exit()")
|
||||
logging.shutdown()
|
||||
fh.close()
|
||||
ch.close()
|
||||
exit(0)
|
||||
|
|
@ -3,82 +3,202 @@
|
|||
########################
|
||||
|
||||
[BOSWatch]
|
||||
#set loglevel for logfile
|
||||
#10 = debug
|
||||
#20 = info
|
||||
#30 = warning
|
||||
#40 = error
|
||||
#50 = critical
|
||||
# set loglevel for logfile
|
||||
# 10 = debug
|
||||
# 20 = info
|
||||
# 30 = warning
|
||||
# 40 = error
|
||||
# 50 = critical
|
||||
loglevel = 10
|
||||
|
||||
#time to ignore same alarm in a row (sek)
|
||||
fms_double_ignore_time = 5
|
||||
# BOSWatch use a rotating logfile
|
||||
# Rotating is at midnight
|
||||
# You can set the backupCount here
|
||||
# backupCount = 7 (keeps logfiles for a week)
|
||||
backupCount = 7
|
||||
|
||||
#time to ignore same alarm in a row (sek)
|
||||
zvei_double_ignore_time = 5
|
||||
# Using RegEx-Filter (0|1)
|
||||
# Filter-configuration in section [Filters]
|
||||
useRegExFilter = 0
|
||||
|
||||
#time to ignore same alarm in a row (sek)
|
||||
poc_double_ignore_time = 10
|
||||
#start and end of the filter range
|
||||
poc_filter_range_start = 0000000
|
||||
poc_filter_range_end = 9999999
|
||||
# Using Description (0|1)
|
||||
# You have to be enabled it for every typ in the sections below too
|
||||
useDescription = 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)
|
||||
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)
|
||||
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 =
|
||||
|
||||
# Deny this RICs (empty: allow all, separator ",")
|
||||
# f.e.: deny_ric = 1234566,1234567,1234568
|
||||
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)
|
||||
idDescribed = 0
|
||||
|
||||
|
||||
[Filters]
|
||||
#No Filter for a Typ/Plugin Combination = all Data pass
|
||||
#INDIVIDUAL_NAME = TYP;PLUGIN;REGEX
|
||||
zvei_local_filter = ZVEI;template;25[0-9F]{3}
|
||||
# RegEX Filter Configuration
|
||||
# http://www.regexr.com/ - RegEX Test Tool an Documentation
|
||||
# No Filter for a Typ/Plugin Combination = all Data pass
|
||||
# INDIVIDUAL_NAME = TYP;DATAFIELD;PLUGIN;FREQUENZ;REGEX
|
||||
# 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}
|
||||
|
||||
# only POCSAG to MySQL with the text "ALARM:" in the Message
|
||||
#pocTest = POC;msg;MySQL;*;ALARM:
|
||||
|
||||
|
||||
[Plugins]
|
||||
#can take on or off the plugins (0|1)
|
||||
# can take on or off the plugins (0|1)
|
||||
MySQL = 0
|
||||
BosMon = 0
|
||||
httpRequest = 0
|
||||
eMail = 0
|
||||
BosMon = 0
|
||||
firEmergency = 0
|
||||
|
||||
# for developing template-module
|
||||
template = 0
|
||||
|
||||
|
||||
[MySQL]
|
||||
# MySQL configuration
|
||||
dbserver = localhost
|
||||
dbuser = root
|
||||
dbpassword = root
|
||||
database = boswatch
|
||||
|
||||
#tables in the database
|
||||
# tables in the database
|
||||
tableFMS = bos_fms
|
||||
tableZVEI = bos_zvei
|
||||
tablePOC = bos_pocsag
|
||||
|
||||
|
||||
[httpRequest]
|
||||
# URL without http://
|
||||
|
||||
# you can use the following wildcards in your URL as GET params:
|
||||
# http://en.wikipedia.org/wiki/Query_string
|
||||
|
||||
# %FMS% = FMS Code
|
||||
# %STATUS% = FMS Status
|
||||
# %DIR% = Direction of the telegram
|
||||
# %TSI% = Tactical Short Information
|
||||
#fms_url = www.google.de?code=%FMS%&stat=%STATUS%
|
||||
fms_url =
|
||||
|
||||
# %ZVEI% = ZVEI 5-tone Code
|
||||
#zvei_url = www.google.de?zvei=%ZVEI%
|
||||
zvei_url =
|
||||
|
||||
# %RIC% = Pocsag RIC
|
||||
# %FUNC% = Pocsac function/Subric
|
||||
# %MSG% = Message of the Pocsag telegram
|
||||
# %BITRATE% = Bitrate of the Pocsag telegram
|
||||
#poc_url = www.google.de?ric=%RIC%&subric=%FUNC%&msg=%MSG%
|
||||
poc_url =
|
||||
|
||||
|
||||
[eMail]
|
||||
# SMTP-Server
|
||||
smtp_server = localhost
|
||||
# Port of SMTP-Server (default:
|
||||
smtp_port =
|
||||
# use tls for connection (0|1)
|
||||
tls = 0
|
||||
# Use this, when SMTP-Server has restricted access
|
||||
user =
|
||||
password =
|
||||
|
||||
# Parameters for Alarm-Msg:
|
||||
# "to" could be more than one address, comma separated
|
||||
from = local@localhost
|
||||
to = user@irgendwo, user2@woanders
|
||||
|
||||
# Priority of the eMail:
|
||||
# normal|urgent|non-urgent
|
||||
priority = urgent
|
||||
|
||||
# %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)
|
||||
# %TIME% = Date/Time (by script)
|
||||
fms_subject = FMS: %FMS%
|
||||
fms_message = %TIME%: %FMS% - Status: %STATUS% - Direction: %DIRT% - TSI: %TSI%
|
||||
|
||||
# %ZVEI% = ZVEI 5-tone Code
|
||||
# %TIME% = Date/Time (by script)
|
||||
zvei_subject = Alarm: %ZVEI%
|
||||
zvei_message = %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)
|
||||
poc_subject = Alarm: %RIC%%FUNCCHAR%
|
||||
poc_message = %TIME%: %MSG%
|
||||
|
||||
|
||||
[BosMon]
|
||||
#Server as IP of DNS-Name (without http://)
|
||||
#actually no ssl supported
|
||||
# Server as IP of DNS-Name (without http://)
|
||||
# actually no ssl supported
|
||||
bosmon_server = 192.168.0.1
|
||||
bosmon_port = 80
|
||||
|
||||
#channel-name of typ "Web-Telegramm"
|
||||
# channel-name of typ "Web-Telegramm"
|
||||
bosmon_channel = channel
|
||||
|
||||
#Use this, when BosMon has restricted access
|
||||
# Use this, when BosMon has restricted access
|
||||
bosmon_user =
|
||||
bosmon_password =
|
||||
|
||||
|
||||
[httpRequest]
|
||||
#URL without http://
|
||||
fms_url = www.google.de
|
||||
zvei_url = www.google.de
|
||||
poc_url = www.google.de
|
||||
[firEmergency]
|
||||
# firEmergency configuration
|
||||
firserver = localhost
|
||||
firport = 9001
|
||||
|
||||
|
||||
#####################
|
||||
##### Not ready yet #
|
||||
#####################
|
||||
|
||||
|
||||
[template]
|
||||
data1 = test123
|
||||
data2 = test345
|
||||
data3 = test567
|
||||
data4 = test789
|
||||
test1 = testString
|
||||
test2 = 123456
|
||||
10
csv/fms.csv
Normal file
10
csv/fms.csv
Normal file
|
|
@ -0,0 +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"
|
||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
10
csv/poc.csv
Normal file
10
csv/poc.csv
Normal file
|
|
@ -0,0 +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"
|
||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
10
csv/zvei.csv
Normal file
10
csv/zvei.csv
Normal file
|
|
@ -0,0 +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"
|
||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
|
|
@ -1,16 +1,53 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
import logging
|
||||
"""
|
||||
Handler for the Filter and Plugins at an Alarm
|
||||
|
||||
@author: Bastian Schroll
|
||||
|
||||
@requires: none
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
|
||||
def processAlarm(typ,freq,data):
|
||||
logging.debug("[ ALARM ]")
|
||||
for pluginName, plugin in globals.pluginList.items():
|
||||
from includes import filter
|
||||
if filter.checkFilters(data,typ,pluginName):
|
||||
logging.debug("call Plugin: %s", pluginName)
|
||||
plugin.run(typ,freq,data)
|
||||
logging.debug("return from: %s", pluginName)
|
||||
logging.debug("[END ALARM]")
|
||||
"""
|
||||
Function to process Filters and Plugins at Alarm
|
||||
|
||||
@type typ: string (FMS|ZVEI|POC)
|
||||
@param typ: Typ of the dataset
|
||||
@type freq: string
|
||||
@param freq: frequency of the SDR Stick
|
||||
@type data: map of data (structure see interface.txt)
|
||||
@param data: Contains the parameter
|
||||
|
||||
@requires: active Plugins in pluginList
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if Alarm processing failed
|
||||
"""
|
||||
try:
|
||||
logging.debug("[ ALARM ]")
|
||||
#Go to all Plugins in pluginList
|
||||
for pluginName, plugin in globals.pluginList.items():
|
||||
|
||||
#if enabled use RegEx-Filter
|
||||
if globals.config.getint("BOSWatch","useRegExFilter"):
|
||||
from includes import filter
|
||||
if filter.checkFilters(typ,data,pluginName,freq):
|
||||
logging.debug("call Plugin: %s", pluginName)
|
||||
plugin.run(typ,freq,data)
|
||||
logging.debug("return from: %s", pluginName)
|
||||
|
||||
else: #RegEX Filter off - Call Plugin direct
|
||||
logging.debug("call Plugin: %s", pluginName)
|
||||
plugin.run(typ,freq,data)
|
||||
logging.debug("return from: %s", pluginName)
|
||||
|
||||
logging.debug("[END ALARM]")
|
||||
except:
|
||||
logging.exception("Error in Alarm processing")
|
||||
|
|
@ -1,27 +1,49 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Search for decode String and call the right decoder Funtion
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: none
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
def decode(freq, decoded):
|
||||
|
||||
#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)
|
||||
"""
|
||||
Search for decode String and call the right decoder Function
|
||||
|
||||
#ZVEI Decoder Section
|
||||
#check ZVEI: -> validate -> check double alarm -> log
|
||||
if "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
|
||||
if "POCSAG" in decoded:
|
||||
logging.debug("recieved POCSAG")
|
||||
from includes.decoders import poc
|
||||
poc.decode(freq, decoded)
|
||||
@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
|
||||
if "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
|
||||
if "POCSAG" in decoded:
|
||||
logging.debug("recieved POCSAG")
|
||||
from includes.decoders import poc
|
||||
poc.decode(freq, decoded)
|
||||
|
||||
except:
|
||||
logging.exception("cannot start decoder")
|
||||
|
|
@ -1,34 +1,64 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
FMS Decoder
|
||||
|
||||
@author: Bastian Schroll
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time #timestamp for doublealarm
|
||||
import re #Regex for validation
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
#FMS Decoder Function
|
||||
#validate -> check double alarm -> log
|
||||
##
|
||||
#
|
||||
# FMS Decoder Function
|
||||
# validate -> check double alarm -> log
|
||||
#
|
||||
def decode(freq, decoded):
|
||||
"""
|
||||
Export FMS Information from Multimon-NG RAW String and call alarmHandler.processAlarm()
|
||||
|
||||
@type freq: string
|
||||
@param freq: frequency of the SDR Stick
|
||||
@type decoded: string
|
||||
@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
|
||||
|
||||
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_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 re.search("[0-9a-f]{8}[0-9a-f]{1}[01]{1}", fms_id): #if FMS is valid
|
||||
if fms_id == globals.fms_id_old and timestamp < globals.fms_time_old + globals.config.getint("BOSWatch", "fms_double_ignore_time"): #check for double alarm
|
||||
if fms_id == globals.fms_id_old and timestamp < globals.fms_time_old + globals.config.getint("FMS", "double_ignore_time"): #check for double alarm
|
||||
logging.info("FMS double alarm: %s within %s second(s)", globals.fms_id_old, timestamp-globals.fms_time_old)
|
||||
globals.fms_time_old = timestamp #in case of double alarm, fms_double_ignore_time set new
|
||||
else:
|
||||
logging.info("FMS:%s Status:%s Richtung:%s TKI:%s", fms_id[0:8], fms_status, fms_direction, fms_tsi)
|
||||
data = {"fms":fms_id[0:8], "status":fms_status, "direction":fms_direction, "tsi":fms_tsi}
|
||||
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)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +1,94 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
POCSAG Decoder
|
||||
|
||||
@author: Bastian Schroll
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time #timestamp for doublealarm
|
||||
import re #Regex for validation
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
#POCSAG Decoder Function
|
||||
#validate -> check double alarm -> log
|
||||
##
|
||||
#
|
||||
# Simple Filter
|
||||
#
|
||||
def isAllowed(poc_id):
|
||||
"""
|
||||
Simple Filter Functions (Allowed, Denied and Range)
|
||||
|
||||
@type poc_id: string
|
||||
@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,
|
||||
# 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"):
|
||||
logging.debug("RIC %s is allowed", poc_id)
|
||||
return True
|
||||
else:
|
||||
logging.debug("RIC %s is not in the allowed list", poc_id)
|
||||
return False
|
||||
# 2.) If denied RIC, return False
|
||||
elif poc_id in globals.config.get("POC", "deny_ric"):
|
||||
logging.debug("RIC %s is denied by config.ini", poc_id)
|
||||
return False
|
||||
# 3.) Check Range, return False if outside def. range
|
||||
elif int(poc_id) < globals.config.getint("POC", "filter_range_start"):
|
||||
logging.debug("RIC %s out of filter range (start)", poc_id)
|
||||
return False
|
||||
elif int(poc_id) > globals.config.getint("POC", "filter_range_end"):
|
||||
logging.debug("RIC %s out of filter range (end)", poc_id)
|
||||
return False
|
||||
return True
|
||||
|
||||
##
|
||||
#
|
||||
# POCSAG Decoder Function
|
||||
# validate -> check double alarm -> log
|
||||
#
|
||||
def decode(freq, decoded):
|
||||
"""
|
||||
Export POCSAG Information from Multimon-NG RAW String and call alarmHandler.processAlarm()
|
||||
|
||||
@type freq: string
|
||||
@param freq: frequency of the SDR Stick
|
||||
@type decoded: string
|
||||
@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]
|
||||
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]
|
||||
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]
|
||||
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:
|
||||
|
|
@ -39,22 +102,28 @@ def decode(freq, decoded):
|
|||
poc_text = ""
|
||||
|
||||
if re.search("[0-9]{7}", poc_id): #if POC is valid
|
||||
if int(poc_id) >= globals.config.getint("BOSWatch", "poc_filter_range_start"):
|
||||
if int(poc_id) <= globals.config.getint("BOSWatch", "poc_filter_range_end"):
|
||||
if poc_id == globals.poc_id_old and timestamp < globals.poc_time_old + globals.config.getint("BOSWatch", "poc_double_ignore_time"): #check for double alarm
|
||||
logging.info("POCSAG%s double alarm: %s within %s second(s)", bitrate, globals.poc_id_old, timestamp-globals.poc_time_old)
|
||||
globals.poc_time_old = timestamp #in case of double alarm, poc_double_ignore_time set new
|
||||
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}
|
||||
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
|
||||
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 out of filter range (high)", bitrate, poc_id)
|
||||
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
|
||||
else:
|
||||
logging.info("POCSAG%s: %s out of filter range (low)", bitrate, poc_id)
|
||||
logging.info("POCSAG%s: %s is not allowed", bitrate, poc_id)
|
||||
else:
|
||||
logging.warning("No valid POCSAG%s RIC: %s", bitrate, poc_id)
|
||||
|
|
@ -1,29 +1,78 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
ZVEI Decoder
|
||||
|
||||
@author: Bastian Schroll
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time #timestamp for doublealarm
|
||||
import re #Regex for validation
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
#ZVEI Decoder Function
|
||||
#validate -> check double alarm -> log
|
||||
##
|
||||
#
|
||||
# ZVEI Decoder Function
|
||||
# validate -> check double alarm -> log
|
||||
#
|
||||
def decode(freq, decoded):
|
||||
"""
|
||||
Export ZVEI Information from Multimon-NG RAW String and call alarmHandler.processAlarm()
|
||||
|
||||
@type freq: string
|
||||
@param freq: frequency of the SDR Stick
|
||||
@type decoded: string
|
||||
@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
|
||||
if re.search("[0-9F]{5}", zvei_id): #if ZVEI is valid
|
||||
if zvei_id == globals.zvei_id_old and timestamp < globals.zvei_time_old + globals.config.getint("BOSWatch", "zvei_double_ignore_time"): #check for double alarm
|
||||
zvei_id = removeF(zvei_id) #resolve F
|
||||
if re.search("[0-9]{5}", zvei_id): #if ZVEI is valid
|
||||
if zvei_id == globals.zvei_id_old and timestamp < globals.zvei_time_old + globals.config.getint("ZVEI", "double_ignore_time"): #check for double alarm
|
||||
logging.info("ZVEI double alarm: %s within %s second(s)", globals.zvei_id_old, timestamp-globals.zvei_time_old)
|
||||
globals.zvei_time_old = timestamp #in case of double alarm, zvei_double_ignore_time set new
|
||||
else:
|
||||
logging.info("5-Ton: %s", zvei_id)
|
||||
data = {"zvei":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)
|
||||
|
||||
|
||||
def removeF(zvei):
|
||||
"""
|
||||
Resolve the F from the repeat Tone
|
||||
|
||||
@type zvei: string
|
||||
@param zvei: ZVEI Information
|
||||
|
||||
@return: ZVEI without F
|
||||
@exception: none
|
||||
"""
|
||||
if "F" in zvei:
|
||||
zvei_old = zvei
|
||||
for i in range(1, 5):
|
||||
if zvei[i] == "F":
|
||||
zvei = zvei.replace("F",zvei[i-1],1)
|
||||
logging.debug("resolve F: %s -> %s", zvei_old, zvei)
|
||||
return zvei
|
||||
102
includes/descriptionList.py
Normal file
102
includes/descriptionList.py
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
#!/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
|
||||
|
||||
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
|
||||
return resultStr
|
||||
|
|
@ -1,6 +1,14 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Functions for the RegEX Filter
|
||||
|
||||
@author: Bastian Schroll
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
import re #Regex for Filter Check
|
||||
|
|
@ -8,32 +16,58 @@ import re #Regex for Filter Check
|
|||
from includes import globals # Global variables
|
||||
|
||||
|
||||
def getFilters():
|
||||
logging.debug("reading config file")
|
||||
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")
|
||||
#For each entry in config.ini [Filters] Section
|
||||
for key,val in globals.config.items("Filters"):
|
||||
logging.debug(" - %s = %s", key, val)
|
||||
filter = val.split(";")
|
||||
globals.filterList.append({"name": key, "typ": filter[0], "plugin": filter[1], "regex": filter[2]})
|
||||
#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]})
|
||||
except:
|
||||
logging.exception("cannot read config file")
|
||||
|
||||
|
||||
def checkFilters(data,typ,plugin):
|
||||
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)
|
||||
@param data: Contains the parameter
|
||||
@type plugin: string
|
||||
@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", typ, plugin)
|
||||
|
||||
#extract the correct data for filtering
|
||||
if typ == "FMS": data = data["fms"]
|
||||
if typ == "ZVEI": data = data["zvei"]
|
||||
if typ == "POC": data = data["poc"]
|
||||
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:
|
||||
if i["typ"] == typ and i["plugin"] == plugin:
|
||||
#if Typ/Plugin/Freq combination is found
|
||||
if i["typ"] == typ and (i["plugin"] == plugin or i['plugin'] == "*") and (i["freq"] == freq or i['freq'] == "*"):
|
||||
foundFilter = True
|
||||
logging.debug("found Filter: %s = %s", i["name"], i["regex"])
|
||||
if re.search(i["regex"], data):
|
||||
#Check the RegEX
|
||||
if re.search(i["regex"], data[i["dataField"]]):
|
||||
logging.debug("Filter passed: %s", i["name"])
|
||||
return True
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Global variables
|
||||
|
||||
@author: Jens Herrmann
|
||||
@author: Bastian Schroll
|
||||
"""
|
||||
|
||||
#Global variables
|
||||
config = 0
|
||||
script_path = ""
|
||||
|
|
@ -19,4 +26,10 @@ poc_time_old = 0
|
|||
pluginList = {}
|
||||
|
||||
#filter
|
||||
filterList = []
|
||||
filterList = []
|
||||
|
||||
#idDescribing
|
||||
fmsDescribtionList = {}
|
||||
zveiDescribtionList = {}
|
||||
ricDescribtionList = {}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
Functions to Load and import the Plugins
|
||||
|
||||
@author: Bastian Schroll
|
||||
|
||||
@requires: Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
import imp
|
||||
import os
|
||||
|
|
@ -8,23 +16,40 @@ import os
|
|||
from includes import globals # Global variables
|
||||
|
||||
def loadPlugins():
|
||||
"""
|
||||
Load all Plugins into globals.pluginList
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if insert into globals.pluginList failed
|
||||
"""
|
||||
try:
|
||||
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
|
||||
except:
|
||||
logging.exception("cannot load Plugins")
|
||||
|
||||
|
||||
def getPlugins():
|
||||
"""
|
||||
get a Python Dict of all activeated Plugins
|
||||
|
||||
@return: Plugins as Python Dict
|
||||
@exception: Exception if Plugin search failed
|
||||
"""
|
||||
try:
|
||||
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)
|
||||
# plugins have to be a subdir with MainModule, if not skip
|
||||
|
||||
#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
|
||||
|
||||
|
|
@ -36,7 +61,7 @@ def getPlugins():
|
|||
logging.debug("Plugin [ENABLED ] %s", i)
|
||||
else:
|
||||
logging.debug("Plugin [DISABLED] %s ", i)
|
||||
except: #no entry for plugin found in config-file, skip
|
||||
except: #no entry for plugin found in config-file
|
||||
logging.warning("Plugin [NO CONF ] %s", i)
|
||||
except:
|
||||
logging.exception("Error during Plugin search")
|
||||
|
|
@ -45,6 +70,16 @@ def getPlugins():
|
|||
|
||||
|
||||
def loadPlugin(plugin):
|
||||
"""
|
||||
Imports a single Plugin
|
||||
|
||||
@type plugin: Plugin Data
|
||||
@param plugin: Contains the information to import a Plugin
|
||||
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if Plugin import failed
|
||||
"""
|
||||
try:
|
||||
logging.debug("load Plugin: %s", plugin["name"])
|
||||
return imp.load_module(plugin["name"], *plugin["info"])
|
||||
|
|
|
|||
|
|
@ -1,32 +1,52 @@
|
|||
#!/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):
|
||||
print " ____ ____ ______ __ __ __ "
|
||||
print " / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ b"
|
||||
print " / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ e"
|
||||
print " / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / t"
|
||||
print " /_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ a"
|
||||
print " German BOS Information Script "
|
||||
print " by Bastian Schroll "
|
||||
print ""
|
||||
"""
|
||||
Prints the Header to the Shell
|
||||
|
||||
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 ""
|
||||
@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")
|
||||
|
|
@ -53,7 +53,7 @@ except:
|
|||
|
||||
pluginLoader.loadPlugins()
|
||||
|
||||
filter.getFilters()
|
||||
filter.loadFilters()
|
||||
|
||||
|
||||
# ----- Test Data ----- #
|
||||
|
|
@ -61,7 +61,7 @@ filter.getFilters()
|
|||
#data = {"fms":"12345678", "status":"2", "direction":"1", "tsi":"III"}
|
||||
|
||||
typ = "ZVEI"
|
||||
data = {"zvei":"12345"}
|
||||
data = {"zvei":"25345"}
|
||||
|
||||
#typ = "POC"
|
||||
#data = {"ric":"1234567", "function":"1", "msg":"Hello World!, "bitrate":"1200"}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
BOSWatch-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages to BosMon
|
||||
|
||||
-U {The BosMon hompage<http://www.bosmon.de>}
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: BosMon-Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
import httplib #for the HTTP request
|
||||
|
|
@ -9,23 +19,72 @@ import base64 #for the HTTP request with User/Password
|
|||
|
||||
from includes import globals # Global variables
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# do BosMon-Request
|
||||
#
|
||||
def bosMonRequest(httprequest, params, headers):
|
||||
"""
|
||||
This local function dispatch the BosMon-Request
|
||||
|
||||
@type httprequest: HTTPConnection
|
||||
@param httprequest: An HTTPConnection-Object that represents an open connection to a BosMon-Instance
|
||||
@type params: string of urlencoded data
|
||||
@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
|
||||
"""
|
||||
try:
|
||||
#
|
||||
# BosMon/HTTP-Request
|
||||
#
|
||||
httprequest.request("POST", "/telegramin/"+globals.config.get("BosMon", "bosmon_channel")+"/input.xml", params, headers)
|
||||
except:
|
||||
logging.exception("request to BosMon failed")
|
||||
else:
|
||||
#
|
||||
# check HTTP-Response
|
||||
#
|
||||
httpresponse = httprequest.getresponse()
|
||||
if str(httpresponse.status) == "200": #Check HTTP Response an print a Log or Error
|
||||
logging.debug("BosMon response: %s - %s", str(httpresponse.status), str(httpresponse.reason))
|
||||
else:
|
||||
logging.warning("BosMon response: %s - %s", str(httpresponse.status), str(httpresponse.reason))
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# Main function of BosMon-plugin
|
||||
# will be called by the alarmHandler
|
||||
#
|
||||
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.
|
||||
|
||||
@type typ: string (FMS|ZVEI|POC)
|
||||
@param typ: Typ of the dataset for sending to BosMon
|
||||
@type data: map of data (structure see interface.txt)
|
||||
@param data: Contains the parameter for dispatch to BosMon.
|
||||
@type freq: string
|
||||
@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
|
||||
#
|
||||
# ConfigParser
|
||||
#
|
||||
logging.debug("reading config file")
|
||||
try:
|
||||
for key,val in globals.config.items("BosMon"):
|
||||
|
|
@ -34,51 +93,76 @@ def run(typ,freq,data):
|
|||
logging.exception("cannot read config file")
|
||||
|
||||
try:
|
||||
#Initialize header an connect to BosMon-Server
|
||||
#
|
||||
# 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)
|
||||
# debug-level to shell (0=no debug|1)
|
||||
httprequest.set_debuglevel(0)
|
||||
except:
|
||||
logging.exception("cannot connect to BosMon")
|
||||
|
||||
else:
|
||||
#
|
||||
# Format given data-structure to compatible BosMon string
|
||||
#
|
||||
if typ == "FMS":
|
||||
logging.warning("%s not supported", typ)
|
||||
#logging.debug("Start FMS to BosMon")
|
||||
#try:
|
||||
#BosMon-Telegramin expected assembly group, direction and tsi in one field
|
||||
#structure:
|
||||
#params = urllib.urlencode({'type':'fms', 'address':data["fms"], 'status':data["status"], 'info':'0', 'flags':'0'})
|
||||
#logging.debug(" - Params: %s", params)
|
||||
#bosMonRequest(httprequest, params, headers)
|
||||
#except:
|
||||
#logging.error("FMS to BosMon failed")
|
||||
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.error("ZVEI to BosMon failed")
|
||||
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
|
||||
data["function"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d")
|
||||
params = urllib.urlencode({'type':'pocsag', 'address':data["ric"], 'flags':'0', 'function':data["function"], 'message':data["msg"]})
|
||||
# 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.exception("POC to BosMon failed")
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
MySQL-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages to a MySQL database
|
||||
|
||||
@author: Jens Herrmann
|
||||
@author: Bastian Schroll
|
||||
|
||||
@requires: MySQL-Configuration has to be set in the config.ini
|
||||
@requires: Created Database/Tables, see boswatch.sql
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
import mysql
|
||||
|
|
@ -9,7 +19,35 @@ import mysql.connector
|
|||
from includes import globals # Global variables
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# Main function of MySQL-plugin
|
||||
# will be called by the alarmHandler
|
||||
#
|
||||
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
|
||||
|
||||
@type typ: string (FMS|ZVEI|POC)
|
||||
@param typ: Typ of the dataset for sending to BosMon
|
||||
@type data: map of data (structure see interface.txt)
|
||||
@param data: Contains the parameter for dispatch to BosMon.
|
||||
@type freq: string
|
||||
@keyword freq: frequency is not used in this plugin
|
||||
|
||||
@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
|
||||
"""
|
||||
|
||||
try:
|
||||
#ConfigParser
|
||||
logging.debug("reading config file")
|
||||
|
|
@ -20,14 +58,19 @@ def run(typ,freq,data):
|
|||
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:
|
||||
|
||||
try:
|
||||
#
|
||||
# Create and execute SQL-statement
|
||||
#
|
||||
logging.debug("Insert %s", typ)
|
||||
|
||||
if typ == "FMS":
|
||||
|
|
|
|||
190
plugins/eMail/eMail.py
Normal file
190
plugins/eMail/eMail.py
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
eMail-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages via eMail/SMTP
|
||||
|
||||
@author: Jens Herrmann
|
||||
|
||||
@requires: eMail-Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
|
||||
import time
|
||||
import smtplib #for the SMTP client
|
||||
from email.mime.text import MIMEText # Import the email modules we'll need
|
||||
from email.utils import formatdate # need for confirm to RFC2822 standard
|
||||
from email.utils import make_msgid # need for confirm to RFC2822 standard
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
##
|
||||
#
|
||||
# Private helper function for a printable Timestamp
|
||||
#
|
||||
def curtime():
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
##
|
||||
#
|
||||
# do send mail
|
||||
#
|
||||
def doSendmail(server, subject, mailtext):
|
||||
"""
|
||||
This local function send the eMail
|
||||
|
||||
@type server: SMTP
|
||||
@param server: An SMTP-Object that represents an open connection to an eMail-Server
|
||||
@type subject: string
|
||||
@param subject: Subject for the eMail
|
||||
@type mailtext: string
|
||||
@param mailtext: Mailtext for the eMail
|
||||
|
||||
@return: nothing
|
||||
@exception: Exception if smtp.sendmail failed
|
||||
"""
|
||||
try:
|
||||
msg = MIMEText(mailtext)
|
||||
msg['From'] = globals.config.get("eMail", "from")
|
||||
msg['To'] = globals.config.get("eMail", "to")
|
||||
msg['Subject'] = subject
|
||||
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())
|
||||
except:
|
||||
logging.exception("send eMail failed")
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# Main function of eMail-plugin
|
||||
# will be called by the alarmHandler
|
||||
#
|
||||
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.
|
||||
|
||||
@type typ: string (FMS|ZVEI|POC)
|
||||
@param typ: Typ of the dataset for sending via eMail
|
||||
@type data: map of data (structure see interface.txt)
|
||||
@param data: Contains the parameter for dispatch to eMail.
|
||||
@type freq: string
|
||||
@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")
|
||||
|
||||
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")
|
||||
|
||||
else:
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
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()
|
||||
|
||||
except:
|
||||
logging.exception("")
|
||||
93
plugins/firEmergency/firEmergency.py
Normal file
93
plugins/firEmergency/firEmergency.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: cp1252 -*-
|
||||
|
||||
"""
|
||||
firEmergency-Plugin to dispatch ZVEI- and POCSAG - messages to firEmergency
|
||||
|
||||
@autor: Smith-fms
|
||||
|
||||
@requires: firEmergency-Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
import logging # Global logger
|
||||
import socket
|
||||
|
||||
from includes import globals # Global variables
|
||||
|
||||
##
|
||||
#
|
||||
# Main function of firEmergency-plugin
|
||||
# will be called by the alarmHandler
|
||||
#
|
||||
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)
|
||||
@param typ: Typ of the dataset for sending to firEmergency
|
||||
@type data: map of data (structure see interface.txt)
|
||||
@param data: Contains the parameter for dispatch to firEmergency.
|
||||
@type freq: string
|
||||
@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")
|
||||
|
||||
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")
|
||||
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
||||
finally:
|
||||
logging.debug("close firEmergency-Connection")
|
||||
firSocket.close()
|
||||
|
||||
except:
|
||||
logging.exception("unknown error")
|
||||
|
|
@ -1,76 +1,101 @@
|
|||
#!/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
|
||||
"""
|
||||
httpRequest-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages to an URL
|
||||
|
||||
@author: Bastian Schroll
|
||||
|
||||
@requires: httpRequest-Configuration has to be set in the config.ini
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
|
||||
##
|
||||
#
|
||||
# Main function of HTTP-plugin
|
||||
# will be called by the alarmHandler
|
||||
#
|
||||
def run(typ,freq,data):
|
||||
"""
|
||||
This function is the implementation of the httpRequest-Plugin.
|
||||
It will send the data to an URL via http Request
|
||||
|
||||
@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
|
||||
@type freq: string
|
||||
@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
|
||||
#
|
||||
# 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")
|
||||
|
||||
########## User Plugin CODE ##########
|
||||
|
||||
try:
|
||||
#
|
||||
# Create URL
|
||||
#
|
||||
logging.debug("send %s HTTP request", typ)
|
||||
|
||||
if typ == "FMS":
|
||||
httprequest = httplib.HTTPConnection(globals.config.get("httpRequest", "fms_url"))
|
||||
httprequest.request("HEAD", "/")
|
||||
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":
|
||||
httprequest = httplib.HTTPConnection(globals.config.get("httpRequest", "zvei_url"))
|
||||
httprequest.request("HEAD", "/")
|
||||
url = globals.config.get("httpRequest", "zvei_url") #Get URL
|
||||
url = url.replace("%ZVEI%", data["zvei"]) #replace Wildcards in URL
|
||||
elif typ == "POC":
|
||||
httprequest = httplib.HTTPConnection(globals.config.get("httpRequest", "poc_url"))
|
||||
httprequest.request("HEAD", "/")
|
||||
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:
|
||||
|
||||
try:
|
||||
httpresponse = httprequest.getresponse()
|
||||
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 NameError: #if var httprequest does not exist
|
||||
logging.exception("no HTTP request been sended")
|
||||
except: #otherwise
|
||||
logging.exception("cannot get HTTP response")
|
||||
|
||||
finally:
|
||||
logging.debug("close HTTP-Connection")
|
||||
httprequest.close()
|
||||
########## User Plugin CODE ##########
|
||||
|
||||
|
||||
except:
|
||||
logging.exception("unknown error")
|
||||
|
|
@ -1,36 +1,41 @@
|
|||
Übergabe an Plugin:
|
||||
|
||||
typ = [FMS|ZVEI|POC]
|
||||
freq = [FREQ(Hz)]
|
||||
data = {"KEY1":"VALUE1","KEY2":"VALUE2"} als Python Dict
|
||||
Handover to Plugin:
|
||||
typ = [FMS|ZVEI|POC]
|
||||
freq = [Freq in Hz]
|
||||
data = {"KEY1":"VALUE1","KEY2":"VALUE2"}
|
||||
|
||||
|
||||
Follgende Daten sind bei der jeweiligen Alarm-Art enthalten
|
||||
und per data["OPTION"] abrufbar:
|
||||
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
|
||||
|
||||
|
||||
Es stehen folgende globale Objecte zur Verfügung:
|
||||
Global Objects:
|
||||
|
||||
1.) import logging # Global logger
|
||||
logging - Object
|
||||
Nachricht ins Log per: logging.LOGLEVEL("MESSAGE")
|
||||
Mögliche Loglevel: debug|info|warning|error|exception|critical
|
||||
1.)
|
||||
import logging # Global logger
|
||||
Message into Log: logging.LOGLEVEL("MESSAGE")
|
||||
Loglevel: debug|info|warning|error|exception|critical
|
||||
|
||||
2.) import globals # Global variables
|
||||
config - Object (typ: ConfigParser, stellt config.ini bereit)
|
||||
2.)
|
||||
import globals # Global variables
|
||||
reads Data from the config.ini
|
||||
VALUE = globals.config.get("SECTION", "OPTION")
|
||||
|
|
@ -16,4 +16,12 @@
|
|||
$Rows[] = $daten;
|
||||
}
|
||||
$tpl['zvei'] = $Rows;
|
||||
?>
|
||||
//read POCSAG
|
||||
$db->query("SELECT id, time, ric, funktion, text FROM ".$tablePOC." ORDER BY id DESC LIMIT 50");
|
||||
$Rows = array();
|
||||
while ($daten = $db->fetchAssoc())
|
||||
{
|
||||
$Rows[] = $daten;
|
||||
}
|
||||
$tpl['poc'] = $Rows;
|
||||
?>
|
||||
|
|
|
|||
|
|
@ -28,6 +28,33 @@ Last alarms for FMS and ZVEI (max. 50)<br><br>
|
|||
echo "</tr>";
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
<br>
|
||||
<b>Last POCSAG alarms</b>
|
||||
<table border="1" style="width: 800px;">
|
||||
<tr style="font-weight: bold;">
|
||||
<td>ID</td>
|
||||
<td>Datum - Zeit</td>
|
||||
<td>RIC</td>
|
||||
<td>Funktion</td>
|
||||
<td>Text</td>
|
||||
</tr>
|
||||
<?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>";
|
||||
echo "<td>". $poc['ric'] . "</td>";
|
||||
echo "<td>". $poc['funktion'] . "</td>";
|
||||
echo "<td>". $poc['text'] . "</td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
|
@ -54,4 +81,4 @@ Last alarms for FMS and ZVEI (max. 50)<br><br>
|
|||
}
|
||||
?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue