diff --git a/boswatch/router/__init__.py b/boswatch/router/__init__.py new file mode 100644 index 0000000..836e3e8 --- /dev/null +++ b/boswatch/router/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- diff --git a/boswatch/router/route.py b/boswatch/router/route.py new file mode 100644 index 0000000..fb581bc --- /dev/null +++ b/boswatch/router/route.py @@ -0,0 +1,38 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""! + ____ ____ ______ __ __ __ _____ + / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ / + / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ < + / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ / +/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/ + German BOS Information Script + by Bastian Schroll + +@file: route.py +@date: 04.03.2019 +@author: Bastian Schroll +@description: Class for a single BOSWatch packet router route point +""" + + +class Route: + """!Class for single routing points""" + def __init__(self, name, callback): + """!Create a instance of an route point + + @param name: name of the route point + @param callback: instance of the callback function + """ + self._name = name + self._callback = callback + + @property + def name(self): + """!Property to get the route point name""" + return self._name + + @property + def callback(self): + """!Porperty to get the callback function instance""" + return self._callback diff --git a/boswatch/router/router.py b/boswatch/router/router.py new file mode 100644 index 0000000..599e9b2 --- /dev/null +++ b/boswatch/router/router.py @@ -0,0 +1,72 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""! + ____ ____ ______ __ __ __ _____ + / __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ / + / __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ < + / /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ / +/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/ + German BOS Information Script + by Bastian Schroll + +@file: router.py +@date: 01.03.2019 +@author: Bastian Schroll +@description: Class for the BOSWatch packet router +""" +import logging +import copy + +logging.debug("- %s loaded", __name__) + + +class Router: + """!Class for the Router""" + def __init__(self, name): + """!Create a new router + + @param name: name of the router""" + self._name = name + self._routeList = [] + logging.debug("[%s] new router", self._name) + + def addRoute(self, route): + """!Adds a route point to the router + + @param route: instance of the Route class + """ + logging.debug("[%s] add route: %s", self._name, route.name) + self._routeList.append(route) + + def runRouter(self, bwPacket): + """!Run the router + + @param bwPacket: instance of Packet class + @return a instance of Packet class + """ + logging.debug("[%s] started", self._name) + for routeObject in self._routeList: + logging.debug("[%s] -> run route: %s", self._name, routeObject) + bwPacket_tmp = routeObject.callback(copy.deepcopy(bwPacket)) # copy bwPacket to prevent edit the original + + if bwPacket_tmp is None: # returning None doesnt change the bwPacket + continue + + if bwPacket_tmp is False: # returning False stops the router immediately + logging.debug("[%s] stopped", self._name) + break + + bwPacket = bwPacket_tmp + logging.debug("[%s] <- bwPacket returned: %s", self._name, bwPacket) + logging.debug("[%s] ended", self._name) + return bwPacket + + @property + def name(self): + """!Property to get the name of the router""" + return self._name + + @property + def routeList(self): + """!Property to get a list of all route points of this router""" + return self._routeList diff --git a/boswatch/router.py b/boswatch/router/routerManager.py similarity index 65% rename from boswatch/router.py rename to boswatch/router/routerManager.py index 13d4714..6e608d6 100644 --- a/boswatch/router.py +++ b/boswatch/router/routerManager.py @@ -9,81 +9,41 @@ German BOS Information Script by Bastian Schroll -@file: router.py -@date: 01.03.2019 +@file: routerManager.py +@date: 04.03.2019 @author: Bastian Schroll -@description: Class for the BOSWatch packet router +@description: Class for the BOSWatch packet router manager class """ + # todo think about implement threading for routers and the plugin calls (THREAD SAFETY!!!) import logging -import copy import importlib from boswatch.configYaml import ConfigYAML +from boswatch.router.router import Router +from boswatch.router.route import Route logging.debug("- %s loaded", __name__) -class _Route: - def __init__(self, name, callback): - self._name = name - self._callback = callback - - @property - def name(self): - return self._name - - @property - def callback(self): - return self._callback - - -class _Router: - def __init__(self, name): - self._name = name - self._routeList = [] - logging.debug("[%s] new router", self._name) - - def addRoute(self, route): - logging.debug("[%s] add route: %s", self._name, route.name) - self._routeList.append(route) - - def runRouter(self, bwPacket): - logging.debug("[%s] started", self._name) - for routeObject in self._routeList: - logging.debug("[%s] -> run route: %s", self._name, routeObject) - bwPacket_tmp = routeObject.callback(copy.deepcopy(bwPacket)) # copy bwPacket to prevent edit the original - - if bwPacket_tmp is None: # returning None doesnt change the bwPacket - continue - - if bwPacket_tmp is False: # returning False stops the router immediately - logging.debug("[%s] stopped", self._name) - break - - bwPacket = bwPacket_tmp - logging.debug("[%s] <- bwPacket returned: %s", self._name, bwPacket) - logging.debug("[%s] ended", self._name) - return bwPacket - - @property - def name(self): - return self._name - - @property - def routeList(self): - return self._routeList - - class RouterManager: + """!Class to manage all routers""" def __init__(self): + """!Create new router""" self._routerDict = {} def __del__(self): + """!Destroy the internal routerDict + All routers and route point instances will be destroyed too + Also destroys all instances from modules or plugins""" # destroy all routers (also destroys all instances of modules/plugins) del self._routerDict # if there is an error, router list would be empty (see tmp variable) def buildRouter(self, config): + """!Initialize Routers from given config file + + @param config: instance of ConfigYaml class + @return True or False""" self._routerDict = {} # all routers and instances of modules/plugins would be destroyed routerDict_tmp = {} logging.debug("build routers") @@ -97,7 +57,7 @@ class RouterManager: if router.get("name") in self._routerDict: logging.error("duplicated router name: %s", router.get("name")) return False - routerDict_tmp[router.get("name")] = _Router(router.get("name")) + routerDict_tmp[router.get("name")] = Router(router.get("name")) for router in config.get("router"): routerName = router.get("name") @@ -115,15 +75,15 @@ class RouterManager: if routeType == "plugin": importedFile = importlib.import_module(routeType + "." + routeName) loadedClass = importedFile.BoswatchPlugin(routeConfig) - routerDict_tmp[routerName].addRoute(_Route(routeName, loadedClass._run)) + routerDict_tmp[routerName].addRoute(Route(routeName, loadedClass._run)) elif routeType == "module": importedFile = importlib.import_module(routeType + "." + routeName) loadedClass = importedFile.BoswatchModule(routeConfig) - routerDict_tmp[routerName].addRoute(_Route(routeName, loadedClass._run)) + routerDict_tmp[routerName].addRoute(Route(routeName, loadedClass._run)) elif routeType == "router": - routerDict_tmp[routerName].addRoute(_Route(routeName, routerDict_tmp[routeName].runRouter)) + routerDict_tmp[routerName].addRoute(Route(routeName, routerDict_tmp[routeName].runRouter)) else: logging.error("unknown type '%s' in %s", routeType, route) @@ -139,6 +99,10 @@ class RouterManager: return True def runRouter(self, routerRunList, bwPacket): + """!Run given Routers + + @param routerRunList: string or list of router names in string form + @param bwPacket: instance of Packet class""" if type(routerRunList) is str: # convert single string name to list routerRunList = [routerRunList] @@ -147,6 +111,7 @@ class RouterManager: self._routerDict[routerName].runRouter(bwPacket) def _showRouterRoute(self): + """!Show the routes of all routers""" for name, routerObject in self._routerDict.items(): logging.debug("Route for %s", name) counter = 0 diff --git a/bw_client.py b/bw_client.py index c867744..58cc0e5 100644 --- a/bw_client.py +++ b/bw_client.py @@ -97,13 +97,17 @@ try: bwClient = TCPClient() if bwClient.connect(ip, port): + testFile = open(paths.TEST_PATH + "testdata.list", "r") + while 1: - for i in range(0, 5): # todo implement real data receive - time.sleep(1) - print("Alarm Nr #" + str(i)) + for testData in testFile: - bwPacket = Decoder.decode("ZVEI1: 12345") + if (len(testData.rstrip(' \t\n\r')) == 0) or ("#" in testData[0]): + continue + + logging.debug("Test: %s", testData) + bwPacket = Decoder.decode(testData) if bwPacket: bwPacket.printInfo() diff --git a/bw_server.py b/bw_server.py index 57f4db1..4980b4f 100644 --- a/bw_server.py +++ b/bw_server.py @@ -43,7 +43,7 @@ from boswatch.network.server import TCPServer from boswatch.packet import Packet from boswatch.utils import header from boswatch.network.broadcast import BroadcastServer -from boswatch.router import RouterManager +from boswatch.router.routerManager import RouterManager header.logoToLog() @@ -82,6 +82,7 @@ try: while 1: if incomingQueue.empty(): # pause only when no data time.sleep(0.1) # reduce cpu load (wait 100ms) + # in worst case a packet have to wait 100ms until it will be processed else: data = incomingQueue.get() diff --git a/test/testdata.list b/test/testdata.list new file mode 100644 index 0000000..f4090b2 --- /dev/null +++ b/test/testdata.list @@ -0,0 +1,136 @@ +# Testdata for the BOSWatch Test Mode function +# Data in Multimon-NG Raw Format +# Data is alternately passed to the decoder to simulate an used Radio-Frequency + +# +# POCSAG +# ------ +# +# The following settings in config.ini are expected for POCSAG +# +# [BOSWatch] +# useDescription = 1 +# doubleFilter_ignore_entries = 10 +# doubleFilter_check_msg = 1 +# +# [POC] +# deny_ric = 7777777 +# filter_range_start = 0000005 +# filter_range_end = 8999999 +# idDescribed = 1 +# + +# bitrate +POCSAG512: Address: 1000512 Function: 1 Alpha: BOSWatch-Test ÖÄÜß: okay +POCSAG1200: Address: 1001200 Function: 1 Alpha: BOSWatch-Test: okay +POCSAG2400: Address: 1002400 Function: 1 Alpha: BOSWatch-Test: okay + +# function-code +POCSAG512: Address: 1000000 Function: 0 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 1000001 Function: 1 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 1000002 Function: 2 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 1000003 Function: 3 Alpha: BOSWatch-Test: okay + +# german special sign +POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test ÖÄÜß: okay +POCSAG512: Address: 1200001 Function: 1 Alpha: BOSWatch-Test öäü: okay + +# with csv +POCSAG512: Address: 1234567 Function: 1 Alpha: BOSWatch-Test: with csv + +# without csv +POCSAG1200: Address: 2345678 Function: 2 Alpha: BOSWatch-Test: without csv +POCSAG2400: Address: 3456789 Function: 3 Alpha: BOSWatch-Test: without csv + +# OHNE TEXT???? +POCSAG1200: Address: 1100000 Function: 0 +POCSAG1200: Address: 1100000 Function: 1 +POCSAG1200: Address: 1100000 Function: 2 +POCSAG1200: Address: 1100000 Function: 3 + +# duplicate with same and other msg +POCSAG1200: Address: 2000001 Function: 2 Alpha: BOSWatch-Test: second is a duplicate +POCSAG1200: Address: 2000001 Function: 2 Alpha: BOSWatch-Test: second is a duplicate +POCSAG1200: Address: 2000001 Function: 2 Alpha: BOSWatch-Testing: okay + +# duplicate in different order +POCSAG1200: Address: 2100000 Function: 2 +POCSAG1200: Address: 2100001 Function: 2 +POCSAG1200: Address: 2100002 Function: 2 +POCSAG1200: Address: 2100000 Function: 2 +POCSAG1200: Address: 2100001 Function: 2 +POCSAG1200: Address: 2100002 Function: 2 +POCSAG1200: Address: 2100000 Function: 2 Alpha: BOSWatch-Test: second is a duplicate +POCSAG1200: Address: 2100001 Function: 2 Alpha: BOSWatch-Test: second is a duplicate +POCSAG1200: Address: 2100002 Function: 2 Alpha: BOSWatch-Test: second is a duplicate +POCSAG1200: Address: 2100000 Function: 2 Alpha: BOSWatch-Test: second is a duplicate +POCSAG1200: Address: 2100001 Function: 2 Alpha: BOSWatch-Test: second is a duplicate +POCSAG1200: Address: 2100002 Function: 2 Alpha: BOSWatch-Test: second is a duplicate + +# invalid +POCSAG512: Address: 3 Function: 0 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 33 Function: 0 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 333 Function: 0 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 3333 Function: 0 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 33333 Function: 0 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 333333 Function: 0 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 3333333 Function: 0 Alpha: BOSWatch-Test: okay +POCSAG512: Address: 333333F Function: 0 Alpha: BOSWatch-Test: invalid +POCSAG512: Address: 333333F Function: 1 Alpha: BOSWatch-Test: invalid +POCSAG512: Address: 3333333 Function: 4 Alpha: BOSWatch-Test: invalid + +# denied +POCSAG1200: Address: 7777777 Function: 1 Alpha: BOSWatch-Test: denied + +# out of filter Range +POCSAG1200: Address: 0000004 Function: 1 Alpha: BOSWatch-Test: out of filter start +POCSAG1200: Address: 9000000 Function: 1 Alpha: BOSWatch-Test: out of filter end + +#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? + + +# +# FMS +# --- +# +FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=I (ohneNA,ohneSIGNAL)) CRC correct +FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=I (ohneNA,ohneSIGNAL)) CRC correct +FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=II (ohneNA,mit SIGNAL)) CRC correct +FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 1=LST->FZG 2=III(mit NA,ohneSIGNAL)) CRC correct +FMS: 43f314170000 (9=Rotkreuz 3=Bayern 1 Ort 0x25=037FZG 7141Status 3=Einsatz Ab 0=FZG->LST 2=IV (mit NA,mit SIGNAL)) CRC correct + + +# +# ZVEI +# ---- +# + +#with csv description +ZVEI1: 12345 +#without csv description +ZVEI1: 56789 +#duplicate +ZVEI1: 56789 +#with repeat Tone +ZVEI1: 1F2E3 +#in case of invalid id +ZVEI1: 135 +#in case of a double-tone for siren n-'D's are sended +# ZVEI1: DDD +# ZVEI1: DDDDD