Merge branch 'develop'

This commit is contained in:
Schrolli 2015-06-11 08:03:21 +02:00
commit dead180506
26 changed files with 1549 additions and 390 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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
View 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
View 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
View 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.

View file

@ -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")

View file

@ -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")

View file

@ -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)

View file

@ -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)

View file

@ -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
View 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

View file

@ -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:

View file

@ -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 = {}

View file

@ -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"])

View file

@ -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")

View file

@ -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"}

View file

@ -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)

View file

@ -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
View 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("")

View 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")

View file

@ -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")

View file

@ -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")

View file

@ -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;
?>

View file

@ -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>