diff --git a/.gitignore b/.gitignore index 7617a015..3b70e903 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,9 @@ *.log config.ini log/ + +\.project + +\.pydevproject + +\.settings/ diff --git a/.travis.yml b/.travis.yml index d1103beb..00827395 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ sudo: required branches: only: - master - - beta - develop before_script: diff --git a/CHANGELOG.md b/CHANGELOG.md index 63467147..447246f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # Changelog +### __[2.3]__ - 22.12.2017 +##### Added +- zuschaltbare POCSAG Multicast-Alarm Funktionalität [#307](https://github.com/Schrolli91/BOSWatch/pull/307) +- Flag in Config um nur letzte Net Ident oder gesamte Historie zu speichern [#317](https://github.com/Schrolli91/BOSWatch/pull/317) +##### Removed +- Beta Branch aus Readme, Installer und Travis-CI entfernt [#324](https://github.com/Schrolli91/BOSWatch/pull/324) +##### Fixed +- Bug in httpRequest Plugin (data Field wurde überschrieben) [#337](https://github.com/Schrolli91/BOSWatch/pull/337) +- Kommentar für FirEmergency Einstellung angepasst [#338](https://github.com/Schrolli91/BOSWatch/pull/338) + + ### __[v2.2.2]__ - 21.10.2017 ##### Added - Installations Script für Services [#316](https://github.com/Schrolli91/BOSWatch/pull/316) diff --git a/README.md b/README.md index fa97e5e3..c9b570b9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ |Branch|Code Qualität|CI-Build| |---|---|---| |master|[![Codacy Badge](https://img.shields.io/codacy/grade/d512976554354a199555bd34ed179bb1/master.svg)](https://www.codacy.com/app/Schrolli91/BOSWatch/dashboard?bid=3763821)|[![Build Status](https://travis-ci.org/Schrolli91/BOSWatch.svg?branch=master)](https://travis-ci.org/Schrolli91/BOSWatch)| -|beta|[![Codacy Badge](https://img.shields.io/codacy/grade/d512976554354a199555bd34ed179bb1/beta.svg)](https://www.codacy.com/app/Schrolli91/BOSWatch/dashboard?bid=4213030)|[![Build Status](https://travis-ci.org/Schrolli91/BOSWatch.svg?branch=beta)](https://travis-ci.org/Schrolli91/BOSWatch)| |develop|[![Codacy Badge](https://img.shields.io/codacy/grade/d512976554354a199555bd34ed179bb1/develop.svg)](https://www.codacy.com/app/Schrolli91/BOSWatch/dashboard?bid=3763820)|[![Build Status](https://travis-ci.org/Schrolli91/BOSWatch.svg?branch=develop)](https://travis-ci.org/Schrolli91/BOSWatch)| @@ -52,6 +51,7 @@ unless you are developer you can use the develop-Branch - may be unstable! - Ready for use BOSWatch as daemon - possibility to start plugins asynchron - NMA Error Handler +- multicastAlarm for transmission optimized networks ### Plugins diff --git a/boswatch.py b/boswatch.py index df1e5e6d..2ad194d0 100755 --- a/boswatch.py +++ b/boswatch.py @@ -220,6 +220,8 @@ # if given loglevel is debug: if globalVars.config.getint("BOSWatch","loglevel") == 10: configHandler.checkConfig("BOSWatch") + configHandler.checkConfig("multicastAlarm") + configHandler.checkConfig("Filters") configHandler.checkConfig("FMS") configHandler.checkConfig("ZVEI") configHandler.checkConfig("POC") diff --git a/citest/config.httpRequest.ini b/citest/config.httpRequest.ini index bfa08ec2..665a66e0 100644 --- a/citest/config.httpRequest.ini +++ b/citest/config.httpRequest.ini @@ -114,6 +114,26 @@ ricd = Unwetter # Usually sent periodically, separated by comma netIdent_ric = 0174760, 1398098 + +[multicastAlarm] +# Configure multicastAlarm if your POCSAG network uses an optimized transmission scheme for alarms with more than one RIC (often found in Swissphone networks). +# In this optimized transmission scheme, a POCSAG telegram with each RIC that needs to be alarmed will be send in a sequence. These telegrams are send without a text message. This sequence is directly followed by a telegram with a specific RIC and the text message that belongs to the sequnece send right before. +# A POCSAG pager (DME) can be configured to start an acoustic alarm if a specific RIC without text has been received. If afterwards the specific RIC with the text message will be received, the pager will show the message in it's display. +# multicastAlarm enables BOSwatch to process the all received RICs joined with the text message. +# +# enable multicastAlarm (0 - off | 1 - on) +multicastAlarm = 0 + +# time limit for alarms that do not belong to the multicastAlarm sequence in seconds +multicastAlarm_ignore_time = 15 + +# multicastAlarm delimiter RIC (usually used as a starting point for a alarm sequence) (can be empty) +multicastAlarm_delimiter_ric = + +# multicastAlarm RIC that is used to send the text message +multicastAlarm_ric = + + [Filters] # RegEX Filter Configuration # http://www.regexr.com/ - RegEX Test Tool an Documentation diff --git a/citest/config.mysql.ini b/citest/config.mysql.ini index 7766f77c..317d3d91 100644 --- a/citest/config.mysql.ini +++ b/citest/config.mysql.ini @@ -114,6 +114,26 @@ ricd = Unwetter # Usually sent periodically, separated by comma netIdent_ric = 0174760, 1398098 + +[multicastAlarm] +# Configure multicastAlarm if your POCSAG network uses an optimized transmission scheme for alarms with more than one RIC (often found in Swissphone networks). +# In this optimized transmission scheme, a POCSAG telegram with each RIC that needs to be alarmed will be send in a sequence. These telegrams are send without a text message. This sequence is directly followed by a telegram with a specific RIC and the text message that belongs to the sequnece send right before. +# A POCSAG pager (DME) can be configured to start an acoustic alarm if a specific RIC without text has been received. If afterwards the specific RIC with the text message will be received, the pager will show the message in it's display. +# multicastAlarm enables BOSwatch to process the all received RICs joined with the text message. +# +# enable multicastAlarm (0 - off | 1 - on) +multicastAlarm = 0 + +# time limit for alarms that do not belong to the multicastAlarm sequence in seconds +multicastAlarm_ignore_time = 15 + +# multicastAlarm delimiter RIC (usually used as a starting point for a alarm sequence) (can be empty) +multicastAlarm_delimiter_ric = + +# multicastAlarm RIC that is used to send the text message +multicastAlarm_ric = + + [Filters] # RegEX Filter Configuration # http://www.regexr.com/ - RegEX Test Tool an Documentation diff --git a/citest/testdata.txt b/citest/testdata.txt index e4ac22e5..4f1d5d11 100644 --- a/citest/testdata.txt +++ b/citest/testdata.txt @@ -35,7 +35,7 @@ POCSAG512: Address: 1000003 Function: 3 Alpha: BOSWatch-Test: okay POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test ÖÄÜß: okay POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test öäü: okay -# witch csv +# with csv POCSAG512: Address: 1234567 Function: 1 Alpha: BOSWatch-Test: with csv # without csv @@ -86,6 +86,22 @@ POCSAG1200: Address: 7777777 Function: 1 Alpha: BOSWatch-Test: denied POCSAG1200: Address: 0000004 Function: 1 Alpha: BOSWatch-Test: out of filter start POCSAG1200: Address: 9000000 Function: 1 Alpha: BOSWatch-Test: out of filter end +#Probealram +POCSAG1200: Address: 0871004 Function: 1 Alpha: Dies ist ein Probealarm! +## Multicast Alarm +POCSAG1200: Address: 0871002 Function: 0 Alpha: +POCSAG1200: Address: 0860001 Function: 0 +POCSAG1200: Address: 0860002 Function: 0 +POCSAG1200: Address: 0860003 Function: 0 +POCSAG1200: Address: 0860004 Function: 0 +POCSAG1200: Address: 0860005 Function: 0 +POCSAG1200: Address: 0860006 Function: 0 +POCSAG1200: Address: 0860007 Function: 0 +POCSAG1200: Address: 0860008 Function: 0 +POCSAG1200: Address: 0860009 Function: 0 +POCSAG1200: Address: 0860010 Function: 0 +POCSAG1200: Address: 0871003 Function: 0 Alpha: B2 Feuer Gebäude Pers in Gefahr. bla bla bla + # regEx-Filter? diff --git a/config/config.template.ini b/config/config.template.ini index 7a959f27..bbb6e95f 100644 --- a/config/config.template.ini +++ b/config/config.template.ini @@ -128,6 +128,27 @@ ricd = Unwetter # RIC for net identification # Usually sent periodically, separated by comma netIdent_ric = 0174760, 1398098 +# you can hold one entry per netIdent_ric [0] or the whole history [1] +netIdent_history = 0 + + +[multicastAlarm] +# Configure multicastAlarm if your POCSAG network uses an optimized transmission scheme for alarms with more than one RIC (often found in Swissphone networks). +# In this optimized transmission scheme, a POCSAG telegram with each RIC that needs to be alarmed will be send in a sequence. These telegrams are send without a text message. This sequence is directly followed by a telegram with a specific RIC and the text message that belongs to the sequnece send right before. +# A POCSAG pager (DME) can be configured to start an acoustic alarm if a specific RIC without text has been received. If afterwards the specific RIC with the text message will be received, the pager will show the message in it's display. +# multicastAlarm enables BOSwatch to process the all received RICs joined with the text message. +# +# enable multicastAlarm (0 - off | 1 - on) +multicastAlarm = 0 + +# time limit for alarms that do not belong to the multicastAlarm sequence in seconds +multicastAlarm_ignore_time = 15 + +# multicastAlarm delimiter RIC (usually used as a starting point for a alarm sequence) (can be empty) +multicastAlarm_delimiter_ric = + +# multicastAlarm RIC that is used to send the text message +multicastAlarm_ric = [Filters] diff --git a/includes/decoders/poc.py b/includes/decoders/poc.py index b0198f09..da863186 100644 --- a/includes/decoders/poc.py +++ b/includes/decoders/poc.py @@ -117,23 +117,40 @@ def decode(freq, decoded): if re.search("[0-9]{7}", poc_id) and re.search("[1-4]{1}", poc_sub): #if POC is valid if isAllowed(poc_id): + # check for double alarm if doubleFilter.checkID("POC", poc_id+poc_sub, poc_text): - logging.info("POCSAG%s: %s %s %s ", bitrate, poc_id, poc_sub, poc_text) data = {"ric":poc_id, "function":poc_sub, "msg":poc_text, "bitrate":bitrate, "description":poc_id} # Add function as character a-d to dataset data["functionChar"] = data["function"].replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d") + + logging.info("POCSAG%s: %s %s %s ", data["bitrate"], data["ric"], data["function"], data["msg"]) + # If enabled, look up description if globalVars.config.getint("POC", "idDescribed"): from includes import descriptionList - data["description"] = descriptionList.getDescription("POC", poc_id+data["functionChar"]) - # processing the alarm - try: - from includes import alarmHandler - alarmHandler.processAlarmHandler("POC", freq, data) - except: - logging.error("processing alarm failed") - logging.debug("processing alarm failed", exc_info=True) + data["description"] = descriptionList.getDescription("POC", data["ric"]+data["functionChar"]) + + # multicastAlarm processing if enabled and a message without text or delimiter RIC or netIdent_ric received + if globalVars.config.getint("multicastAlarm", "multicastAlarm") and data["ric"] != globalVars.config.get("POC", "netIdent_ric") and (data["msg"] == "" or data["ric"] in globalVars.config.get("multicastAlarm", "multicastAlarm_delimiter_ric")): + logging.debug(" - multicastAlarm without msg") + from includes import multicastAlarm + multicastAlarm.newEntrymultiList(data) + + # multicastAlarm processing if enabled and alarm message has been received + elif globalVars.config.getint("multicastAlarm", "multicastAlarm") and data["msg"] != "" and data["ric"] in globalVars.config.get("multicastAlarm", "multicastAlarm_ric"): + logging.debug(" - multicastAlarm with message") + from includes import multicastAlarm + multicastAlarm.multicastAlarmExec(freq, data) + + else: + # processing the alarm + try: + from includes import alarmHandler + alarmHandler.processAlarmHandler("POC", freq, data) + except: + logging.error("processing alarm failed") + logging.debug("processing alarm failed", exc_info=True) # in every time save old data for double alarm doubleFilter.newEntry(poc_id+poc_sub, poc_text) else: diff --git a/includes/globalVars.py b/includes/globalVars.py index a6dcd604..23f9377c 100644 --- a/includes/globalVars.py +++ b/includes/globalVars.py @@ -9,9 +9,9 @@ """ # version info -versionNr = "2.2.2" +versionNr = "2.3" branch = "master" -buildDate = "21.10.2017" +buildDate = "22/12/2017" # Global variables diff --git a/includes/multicastAlarm.py b/includes/multicastAlarm.py new file mode 100644 index 00000000..40a83443 --- /dev/null +++ b/includes/multicastAlarm.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +""" +multicastAlarm is the function to enable BOSwatch to work in networks that optimise the transmission of POCSAG telegrams + +@author: Fabian Kessler + +@requires: Configuration has to be set in the config.ini +""" + +import logging # Global logger +import time # timestamp for multicastAlarm + +from includes import globalVars # Global variables + +# +# ListStructure [0..n] = (Data, TimeStamp) +# +multiList = [] + +def newEntrymultiList(data): + """ + add entry to multi alarm list and remove old entries + + @return: nothing + """ + global multiList + timestamp = int(time.time()) + # multicastAlarm processing if enabled and delimiter RIC has been received + if data['ric'] == globalVars.config.get("multicastAlarm", "multicastAlarm_delimiter_ric"): + multiList = [] + logging.debug("delimiter RIC received - buffer cleared") + else: + multiList.append([data, timestamp]) + logging.debug("Added %s to multiList", data['ric']) + # check for old entries in multiList + for (xData, xTimestamp) in multiList[:]: + if xTimestamp < timestamp-globalVars.config.getint("multicastAlarm", "multicastAlarm_ignore_time"): + multiList.remove([xData, xTimestamp]) + logging.debug("RIC %s removed - %s sec. older than current timestamp", xData['ric'], xTimestamp-timestamp) + + +def multicastAlarmExec(freq, data): + """ + call alarmHandler for every entry in multiList + + @return: nothing + """ + logging.debug("data before update from multiList: %s", data) + for (xData, _) in multiList: + #update data with values multiList + data['ric'] = xData['ric'] + data['function'] = xData['function'] + data['functionChar'] = xData['functionChar'] + data['description'] = xData['description'] + logging.debug("data after update from multiList: %s", data) + try: + from includes import alarmHandler + alarmHandler.processAlarmHandler("POC", freq, data) + except: + logging.error("processing alarm failed") + logging.debug("processing alarm failed", exc_info=True) diff --git a/install.sh b/install.sh index 3fc81409..5470b243 100644 --- a/install.sh +++ b/install.sh @@ -78,7 +78,6 @@ for (( i=1; i<=$#; i=$i+2 )); do -b|--branch) case $arg2 in dev|develop) echo " !!! WARNING: you are using the DEV BRANCH !!! "; branch=dev ;; - beta) echo " !!! WARNING: you are using the BETA BRANCH !!! "; branch=beta ;; *) branch=master ;; esac ;; diff --git a/plugins/MySQL/MySQL.py b/plugins/MySQL/MySQL.py index e9d3d7e9..3d4db163 100644 --- a/plugins/MySQL/MySQL.py +++ b/plugins/MySQL/MySQL.py @@ -21,23 +21,23 @@ from includes.helper import configHandler def isSignal(poc_id): - """ - @type poc_id: string - @param poc_id: POCSAG Ric + """ + @type poc_id: string + @param poc_id: POCSAG Ric - @requires: Configuration has to be set in the config.ini + @requires: Configuration has to be set in the config.ini - @return: True if the Ric is Signal, other False - @exception: none - """ - # If RIC is Signal return True, else False - if globalVars.config.get("POC", "netIdent_ric"): - if poc_id in globalVars.config.get("POC", "netIdent_ric"): - logging.info("RIC %s is net ident", poc_id) - return True - else: - logging.info("RIC %s is no net ident", poc_id) - return False + @return: True if the Ric is Signal, other False + @exception: none + """ + # If RIC is Signal return True, else False + if globalVars.config.get("POC", "netIdent_ric"): + if poc_id in globalVars.config.get("POC", "netIdent_ric"): + logging.info("RIC %s is net ident", poc_id) + return True + else: + logging.info("RIC %s is no net ident", poc_id) + return False ## @@ -111,11 +111,14 @@ def run(typ,freq,data): elif typ == "POC": if isSignal(data["ric"]): - cursor.execute("UPDATE "+globalVars.config.get("MySQL","tableSIG")+" SET time = NOW() WHERE ric = '"+data["ric"]+"';") - if cursor.rowcount == 0: + if globalVars.config.getint("POC","netIdent_histry"): cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableSIG")+" (time,ric) VALUES (NOW(), '"+data["ric"]+"');") + else: + cursor.execute("UPDATE "+globalVars.config.get("MySQL","tableSIG")+" SET time = NOW() WHERE ric = '"+data["ric"]+"';") + if cursor.rowcount == 0: + cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableSIG")+" (time,ric) VALUES (NOW(), '"+data["ric"]+"');") else: - cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tablePOC")+" (time, ric, function, functionChar, msg, bitrate, description) VALUES (FROM_UNIXTIME(%s),%s,%s,%s,%s,%s,%s)", (data["timestamp"], data["ric"], data["function"], data["functionChar"], data["msg"], data["bitrate"], data["description"])) + cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tablePOC")+" (time, ric, function, functionChar, msg, bitrate, description) VALUES (FROM_UNIXTIME(%s),%s,%s,%s,%s,%s,%s)", (data["timestamp"], data["ric"], data["function"], data["functionChar"], data["msg"], data["bitrate"], data["description"])) else: logging.warning("Invalid Typ: %s", typ) diff --git a/plugins/firEmergency/firEmergency.py b/plugins/firEmergency/firEmergency.py index a724cd23..89a325c5 100644 --- a/plugins/firEmergency/firEmergency.py +++ b/plugins/firEmergency/firEmergency.py @@ -5,7 +5,7 @@ firEmergency-Plugin to dispatch ZVEI- and POCSAG - messages to firEmergency firEmergency configuration: -- set input to "FMS32" at Port 5555 +- set input to "Standartschnittstelle" at Port 5555 @autor: Smith-fms diff --git a/plugins/httpRequest/httpRequest.py b/plugins/httpRequest/httpRequest.py index 56e35a61..1622a8bb 100644 --- a/plugins/httpRequest/httpRequest.py +++ b/plugins/httpRequest/httpRequest.py @@ -68,11 +68,13 @@ def run(typ,freq,data): try: # - # Replace special characters in data Strings for URL + # Make a copy of the data field to not overwrite the data in it + # Replace special characters in dataCopy Strings for URL # - for key in data: - if isinstance(data[key], basestring): - data[key] = urllib.quote(data[key]) + dataCopy = dict(data) + for key in dataCopy: + if isinstance(dataCopy[key], basestring): + dataCopy[key] = urllib.quote(dataCopy[key]) # # Get URLs # @@ -90,7 +92,7 @@ def run(typ,freq,data): # replace wildcards # for (i, url) in enumerate(urls): - urls[i] = wildcardHandler.replaceWildcards(urls[i].strip(), data) + urls[i] = wildcardHandler.replaceWildcards(urls[i].strip(), dataCopy) # # HTTP-Request #