mirror of
https://github.com/BOSWatch/BW3-Core.git
synced 2026-01-09 02:00:16 +01:00
Merge branch 'develop' into update_check
This commit is contained in:
commit
be73d3c381
5
.github/workflows/run_pytest.yml
vendored
5
.github/workflows/run_pytest.yml
vendored
|
|
@ -25,3 +25,8 @@ jobs:
|
|||
- name: Test with pytest
|
||||
run: |
|
||||
pytest -c 'test/pytest.ini'
|
||||
- name: Save artifacts
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: test.log
|
||||
path: log/test.log
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -10,6 +10,7 @@
|
|||
\venv/
|
||||
|
||||
# generated files
|
||||
stats_*
|
||||
log/
|
||||
docu/docs/api/html
|
||||
docu/site/
|
||||
|
|
|
|||
31
Dockerfile
Normal file
31
Dockerfile
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
FROM alpine:3.10 AS build-base
|
||||
RUN apk add git make cmake g++ libusb-dev libpulse
|
||||
|
||||
FROM build-base AS rtl_fm
|
||||
ARG RTL_SDR_VERSION=0.6.0
|
||||
RUN git clone --depth 1 --branch ${RTL_SDR_VERSION} https://github.com/osmocom/rtl-sdr.git /opt/rtl_sdr
|
||||
WORKDIR /opt/rtl_sdr/build
|
||||
RUN cmake .. && make
|
||||
|
||||
FROM build-base AS multimon
|
||||
ARG MULTIMON_VERSION=1.1.8
|
||||
RUN git clone --depth 1 --branch ${MULTIMON_VERSION} https://github.com/EliasOenal/multimon-ng.git /opt/multimon
|
||||
WORKDIR /opt/multimon/build
|
||||
RUN cmake .. && make
|
||||
|
||||
FROM alpine:3.10 AS boswatch
|
||||
ARG BW_VERSION=develop
|
||||
RUN apk add git && \
|
||||
git clone --depth 1 --branch ${BW_VERSION} https://github.com/BOSWatch/BW3-Core.git /opt/boswatch
|
||||
|
||||
|
||||
FROM python:3.6-alpine AS runner
|
||||
LABEL maintainer="bastian@schroll-software.de"
|
||||
|
||||
# for RTL for MM
|
||||
RUN apk add libusb-dev libpulse && \
|
||||
pip3 install pyyaml
|
||||
|
||||
COPY --from=boswatch /opt/boswatch/ /opt/boswatch/
|
||||
COPY --from=multimon /opt/multimon/build/multimon-ng /opt/multimon/multimon-ng
|
||||
COPY --from=rtl_fm /opt/rtl_sdr/build/src/ /opt/rtl_sdr/build/src/
|
||||
|
|
@ -33,6 +33,10 @@ class ConfigYAML:
|
|||
else:
|
||||
yield item
|
||||
|
||||
def __len__(self):
|
||||
"""!returns the length of an config element"""
|
||||
return len(self._config)
|
||||
|
||||
def __str__(self):
|
||||
"""!Returns the string representation of the internal config dict"""
|
||||
return str(self._config)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ class Decoder:
|
|||
|
||||
@param data: data to decode
|
||||
@return bwPacket instance"""
|
||||
logging.debug("search decoder")
|
||||
data = str(data)
|
||||
if "FMS" in data:
|
||||
return FmsDecoder.decode(data)
|
||||
|
|
|
|||
|
|
@ -62,10 +62,9 @@ class FmsDecoder:
|
|||
bwPacket.set("directionText", directionText)
|
||||
bwPacket.set("tacticalInfo", tacticalInfo)
|
||||
|
||||
logging.debug(bwPacket)
|
||||
return bwPacket
|
||||
|
||||
logging.warning("no valid data")
|
||||
logging.warning("no valid FMS")
|
||||
return None
|
||||
logging.warning("CRC Error")
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -56,10 +56,9 @@ class PocsagDecoder:
|
|||
bwPacket.set("subricText", subricText)
|
||||
bwPacket.set("message", message)
|
||||
|
||||
logging.debug(bwPacket)
|
||||
return bwPacket
|
||||
|
||||
logging.warning("no valid data")
|
||||
logging.warning("no valid POCSAG")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -44,10 +44,9 @@ class ZveiDecoder:
|
|||
bwPacket.set("mode", "zvei")
|
||||
bwPacket.set("zvei", ZveiDecoder._solveDoubleTone(data[7:12]))
|
||||
|
||||
logging.debug(bwPacket)
|
||||
return bwPacket
|
||||
|
||||
logging.warning("no valid data")
|
||||
logging.warning("no valid ZVEI")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
2
boswatch/inputSource/__init__.py
Normal file
2
boswatch/inputSource/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
65
boswatch/inputSource/inputBase.py
Normal file
65
boswatch/inputSource/inputBase.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""!
|
||||
____ ____ ______ __ __ __ _____
|
||||
/ __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ /
|
||||
/ __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ <
|
||||
/ /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ /
|
||||
/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/
|
||||
German BOS Information Script
|
||||
by Bastian Schroll
|
||||
|
||||
@file: inoutSource.py
|
||||
@date: 28.10.2018
|
||||
@author: Bastian Schroll
|
||||
@description: Base class for boswatch input sources
|
||||
"""
|
||||
import time
|
||||
import logging
|
||||
import threading
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class InputBase(ABC):
|
||||
"""!Base class for handling inout sources"""
|
||||
|
||||
def __init__(self, inputQueue, inputConfig, decoderConfig):
|
||||
"""!Build a new InputSource class
|
||||
|
||||
@param inputQueue: Python queue object to store input data
|
||||
@param inputConfig: ConfigYaml object with the inoutSource config
|
||||
@param decoderConfig: ConfigYaml object with the decoder config"""
|
||||
self._inputThread = None
|
||||
self._isRunning = False
|
||||
self._inputQueue = inputQueue
|
||||
self._inputConfig = inputConfig
|
||||
self._decoderConfig = decoderConfig
|
||||
|
||||
def start(self):
|
||||
"""!Start the input source thread"""
|
||||
logging.debug("starting input thread")
|
||||
self._isRunning = True
|
||||
self._inputThread = threading.Thread(target=self._runThread, name="inputThread",
|
||||
args=(self._inputQueue, self._inputConfig, self._decoderConfig))
|
||||
self._inputThread.daemon = True
|
||||
self._inputThread.start()
|
||||
|
||||
@abstractmethod
|
||||
def _runThread(self, dataQueue, sdrConfig, decoderConfig):
|
||||
"""!Thread routine of the input source has to be inherit"""
|
||||
|
||||
def shutdown(self):
|
||||
"""!Stop the input source thread"""
|
||||
if self._isRunning:
|
||||
logging.debug("wait for stopping the input thread")
|
||||
self._isRunning = False
|
||||
self._inputThread.join()
|
||||
logging.debug("input thread stopped")
|
||||
|
||||
def addToQueue(self, data):
|
||||
"""!Adds alarm data to the queue for further processing during boswatch client"""
|
||||
self._inputQueue.put_nowait((data, time.time()))
|
||||
logging.debug("Add received data to queue")
|
||||
print(data)
|
||||
77
boswatch/inputSource/sdrInput.py
Normal file
77
boswatch/inputSource/sdrInput.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""!
|
||||
____ ____ ______ __ __ __ _____
|
||||
/ __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ /
|
||||
/ __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ <
|
||||
/ /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ /
|
||||
/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/
|
||||
German BOS Information Script
|
||||
by Bastian Schroll
|
||||
|
||||
@file: sdrInput.py
|
||||
@date: 28.10.2018
|
||||
@author: Bastian Schroll
|
||||
@description: Input source for sdr with rtl_fm
|
||||
"""
|
||||
import logging
|
||||
from boswatch.utils import paths
|
||||
from boswatch.processManager import ProcessManager
|
||||
from boswatch.inputSource.inputBase import InputBase
|
||||
|
||||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class SdrInput(InputBase):
|
||||
"""!Class for the sdr input source"""
|
||||
|
||||
def _runThread(self, dataQueue, sdrConfig, decoderConfig):
|
||||
sdrProc = None
|
||||
mmProc = None
|
||||
try:
|
||||
sdrProc = ProcessManager(str(sdrConfig.get("rtlPath", default="rtl_fm")))
|
||||
sdrProc.addArgument("-d " + str(sdrConfig.get("device", default="0"))) # device id
|
||||
sdrProc.addArgument("-f " + str(sdrConfig.get("frequency"))) # frequencies
|
||||
sdrProc.addArgument("-p " + str(sdrConfig.get("error", default="0"))) # frequency error in ppm
|
||||
sdrProc.addArgument("-l " + str(sdrConfig.get("squelch", default="1"))) # squelch
|
||||
sdrProc.addArgument("-g " + str(sdrConfig.get("gain", default="100"))) # gain
|
||||
sdrProc.addArgument("-M fm") # set mode to fm
|
||||
sdrProc.addArgument("-E DC") # set DC filter
|
||||
sdrProc.addArgument("-s 22050") # bit rate of audio stream
|
||||
sdrProc.setStderr(open(paths.LOG_PATH + "rtl_fm.log", "a"))
|
||||
sdrProc.start()
|
||||
|
||||
mmProc = ProcessManager(str(sdrConfig.get("mmPath", default="multimon-ng")), textMode=True)
|
||||
if decoderConfig.get("fms", default=0):
|
||||
mmProc.addArgument("-a FMSFSK")
|
||||
if decoderConfig.get("zvei", default=0):
|
||||
mmProc.addArgument("-a ZVEI1")
|
||||
if decoderConfig.get("poc512", default=0):
|
||||
mmProc.addArgument("-a POCSAG512")
|
||||
if decoderConfig.get("poc1200", default=0):
|
||||
mmProc.addArgument("-a POCSAG1200")
|
||||
if decoderConfig.get("poc2400", default=0):
|
||||
mmProc.addArgument("-a POCSAG2400")
|
||||
mmProc.addArgument("-f alpha")
|
||||
mmProc.addArgument("-t raw -")
|
||||
mmProc.setStdin(sdrProc.stdout)
|
||||
mmProc.setStderr(open(paths.LOG_PATH + "multimon-ng.log", "a"))
|
||||
mmProc.start()
|
||||
|
||||
logging.info("start decoding")
|
||||
while self._isRunning:
|
||||
if not sdrProc.isRunning:
|
||||
logging.warning("rtl_fm was down - try to restart")
|
||||
sdrProc.start()
|
||||
elif not mmProc.isRunning:
|
||||
logging.warning("multimon was down - try to restart")
|
||||
mmProc.start()
|
||||
elif sdrProc.isRunning and mmProc.isRunning:
|
||||
line = mmProc.readline()
|
||||
if line:
|
||||
self.addToQueue(line)
|
||||
except:
|
||||
logging.exception("error in sdr input routine")
|
||||
finally:
|
||||
mmProc.stop()
|
||||
sdrProc.stop()
|
||||
|
|
@ -57,6 +57,7 @@ class TCPClient:
|
|||
@return True or False"""
|
||||
try:
|
||||
if self.isConnected:
|
||||
self._sock.shutdown(socket.SHUT_RDWR)
|
||||
self._sock.close()
|
||||
logging.debug("disconnected")
|
||||
return True
|
||||
|
|
@ -72,29 +73,35 @@ class TCPClient:
|
|||
@param data: data to send to the server
|
||||
@return True or False"""
|
||||
try:
|
||||
logging.debug("transmitting: %s", data)
|
||||
header = str(len(data)).ljust(HEADERSIZE)
|
||||
self._sock.sendall(bytes(header + data, "utf-8"))
|
||||
logging.debug("transmitting:\n%s", data)
|
||||
data = data.encode("utf-8")
|
||||
header = str(len(data)).ljust(HEADERSIZE).encode("utf-8")
|
||||
self._sock.sendall(header + data)
|
||||
logging.debug("transmitted...")
|
||||
return True
|
||||
except socket.error as e:
|
||||
logging.error(e)
|
||||
return False
|
||||
|
||||
def receive(self):
|
||||
def receive(self, timeout=1):
|
||||
"""!Receive data from the server
|
||||
|
||||
@param timeout: to wait for incoming data in seconds
|
||||
@return received data"""
|
||||
try:
|
||||
read, _, _ = select.select([self._sock], [], [], 1)
|
||||
read, _, _ = select.select([self._sock], [], [], timeout)
|
||||
if not read: # check if there is something to read
|
||||
return False
|
||||
header = self._sock.recv(HEADERSIZE)
|
||||
|
||||
header = self._sock.recv(HEADERSIZE).decode("utf-8")
|
||||
if not len(header): # check if there data
|
||||
return False
|
||||
length = int(header.decode("utf-8").strip())
|
||||
|
||||
length = int(header.strip())
|
||||
received = self._sock.recv(length).decode("utf-8")
|
||||
logging.debug("received %d bytes: %s", length, received)
|
||||
|
||||
logging.debug("recv header: '%s'", header)
|
||||
logging.debug("received %d bytes: %s", len(received), received)
|
||||
return received
|
||||
except socket.error as e:
|
||||
logging.error(e)
|
||||
|
|
@ -104,12 +111,17 @@ class TCPClient:
|
|||
def isConnected(self):
|
||||
"""!Property of client connected state"""
|
||||
try:
|
||||
aliveMsg = "<alive>"
|
||||
header = str(len(aliveMsg)).ljust(HEADERSIZE)
|
||||
self._sock.sendall(bytes(header + aliveMsg, "utf-8"))
|
||||
return True
|
||||
if self._sock:
|
||||
_, write, _ = select.select([], [self._sock], [], 0.1)
|
||||
if write:
|
||||
data = "<keep-alive>".encode("utf-8")
|
||||
header = str(len(data)).ljust(HEADERSIZE).encode("utf-8")
|
||||
self._sock.sendall(header + data)
|
||||
return True
|
||||
return False
|
||||
except socket.error as e:
|
||||
if e.errno is 32: # broken pipe - no one will read from this pipe anymore
|
||||
return False
|
||||
logging.error(e)
|
||||
return False
|
||||
if e.errno != 32:
|
||||
logging.exception(e)
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ class NetCheck:
|
|||
"""!Create a new NetCheck instance
|
||||
|
||||
@param hostname: host against connection check is running ("https://www.google.com/")
|
||||
@param timout: timout for connection check in sec. (1)"""
|
||||
@param timeout: timeout for connection check in sec. (1)"""
|
||||
self._hostname = hostname
|
||||
self._timeout = timeout
|
||||
self._connectionState = False
|
||||
self.connectionState = False
|
||||
self.checkConn() # initiate a first check
|
||||
|
||||
def checkConn(self):
|
||||
|
|
@ -40,14 +40,9 @@ class NetCheck:
|
|||
try:
|
||||
urlopen(self._hostname, timeout=self._timeout)
|
||||
logging.debug("%s is reachable", self._hostname)
|
||||
self._connectionState = True
|
||||
self.connectionState = True
|
||||
return True
|
||||
except: # todo find right exception type
|
||||
logging.warning("%s is not reachable", self._hostname)
|
||||
self._connectionState = False
|
||||
self.connectionState = False
|
||||
return False
|
||||
|
||||
@property
|
||||
def connectionState(self):
|
||||
"""!Property for the last connection state from checkConn()"""
|
||||
return self._connectionState
|
||||
|
|
|
|||
|
|
@ -46,17 +46,18 @@ class _ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
|||
if not read:
|
||||
continue # nothing to read on the socket
|
||||
|
||||
header = self.request.recv(HEADERSIZE)
|
||||
header = self.request.recv(HEADERSIZE).decode("utf-8")
|
||||
if not len(header):
|
||||
break # empty data -> socked closed
|
||||
|
||||
length = int(header.decode("utf-8").strip())
|
||||
length = int(header.strip())
|
||||
data = self.request.recv(length).decode("utf-8")
|
||||
|
||||
if data == "<alive>":
|
||||
if data == "<keep-alive>":
|
||||
continue
|
||||
|
||||
logging.debug("%s recv %d bytes: %s", req_name, length, data)
|
||||
logging.debug("%s recv header: '%s'", req_name, header)
|
||||
logging.debug("%s recv %d bytes:\n%s", req_name, len(data), data)
|
||||
|
||||
# add a new entry and the decoded data dict as an string in utf-8 and an timestamp
|
||||
self.server.alarmQueue.put_nowait((self.client_address[0], data, time.time())) # queue is threadsafe
|
||||
|
|
@ -64,15 +65,15 @@ class _ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
|
|||
|
||||
logging.debug("%s send: [ack]", req_name)
|
||||
|
||||
data = "[ack]"
|
||||
header = str(len(data)).ljust(HEADERSIZE)
|
||||
self.request.sendall(bytes(header + data, "utf-8"))
|
||||
self.request.close()
|
||||
data = "[ack]".encode("utf-8")
|
||||
header = str(len(data)).ljust(HEADERSIZE).encode("utf-8")
|
||||
self.request.sendall(header + data)
|
||||
|
||||
except socket.error as e:
|
||||
logging.error(e)
|
||||
return False
|
||||
finally:
|
||||
self.request.close()
|
||||
del self.server.clientsConnected[threading.current_thread().name]
|
||||
logging.info("Client disconnected: %s", self.client_address[0])
|
||||
|
||||
|
|
@ -115,7 +116,7 @@ class TCPServer:
|
|||
@return True or False"""
|
||||
if not self.isRunning:
|
||||
try:
|
||||
socketserver.TCPServer.allow_reuse_address = False # because we can start two instances on same port elsewhere
|
||||
socketserver.TCPServer.allow_reuse_address = True # because we can start two instances on same port elsewhere
|
||||
self._server = _ThreadedTCPServer(("", port), _ThreadedTCPRequestHandler)
|
||||
self._server.timeout = self._timeout
|
||||
self._server.alarmQueue = self._alarmQueue
|
||||
|
|
@ -144,6 +145,7 @@ class TCPServer:
|
|||
if self.isRunning:
|
||||
self._server.shutdown()
|
||||
self._server.isActive = False
|
||||
self._server.server_close()
|
||||
self._server_thread.join()
|
||||
self._server_thread = None
|
||||
self._server = None
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class Packet:
|
|||
|
||||
@param fieldName: Name of the data to set
|
||||
@param value: Value to set"""
|
||||
self._packet[fieldName] = value
|
||||
self._packet[fieldName] = str(value)
|
||||
|
||||
def get(self, fieldName):
|
||||
"""!Returns the value from a single field.
|
||||
|
|
@ -52,7 +52,7 @@ class Packet:
|
|||
@param fieldName: Name of the field
|
||||
@return Value or None"""
|
||||
try:
|
||||
return self._packet[fieldName]
|
||||
return str(self._packet[fieldName])
|
||||
except:
|
||||
logging.warning("field not found: %s", fieldName)
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -32,9 +32,6 @@ class ProcessManager:
|
|||
self._processHandle = None
|
||||
self._textMode = textMode
|
||||
|
||||
def __del__(self):
|
||||
self.stop()
|
||||
|
||||
def addArgument(self, arg):
|
||||
"""!add a new argument
|
||||
|
||||
|
|
|
|||
|
|
@ -15,24 +15,22 @@
|
|||
@description: Class for a single BOSWatch packet router route point
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class Route:
|
||||
"""!Class for single routing points"""
|
||||
def __init__(self, name, callback):
|
||||
def __init__(self, name, callback, statsCallback=None, cleanupCallback=None):
|
||||
"""!Create a instance of an route point
|
||||
|
||||
@param name: name of the route point
|
||||
@param callback: instance of the callback function
|
||||
@param statsCallback: instance of the callback to get statistics (None)
|
||||
@param cleanupCallback: instance of the callback to run a cleanup method (None)
|
||||
"""
|
||||
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
|
||||
self.name = name
|
||||
self.callback = callback
|
||||
self.statistics = statsCallback
|
||||
self.cleanup = cleanupCallback
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"""
|
||||
import logging
|
||||
import copy
|
||||
import time
|
||||
|
||||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
|
@ -26,17 +27,25 @@ class Router:
|
|||
"""!Create a new router
|
||||
|
||||
@param name: name of the router"""
|
||||
self._name = name
|
||||
self._routeList = []
|
||||
logging.debug("[%s] new router", self._name)
|
||||
self.name = name
|
||||
self.routeList = []
|
||||
|
||||
# for time counting
|
||||
self._cumTime = 0
|
||||
self._routerTime = 0
|
||||
|
||||
# for statistics
|
||||
self._runCount = 0
|
||||
|
||||
logging.debug("[%s] add 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)
|
||||
logging.debug("[%s] add route: %s", self.name, route.name)
|
||||
self.routeList.append(route)
|
||||
|
||||
def runRouter(self, bwPacket):
|
||||
"""!Run the router
|
||||
|
|
@ -44,29 +53,37 @@ class 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)
|
||||
self._runCount += 1
|
||||
tmpTime = time.time()
|
||||
|
||||
logging.debug("[%s] started", self.name)
|
||||
|
||||
for routeObject in self.routeList:
|
||||
logging.debug("[%s] -> run route: %s", self.name, routeObject.name)
|
||||
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)
|
||||
logging.debug("[%s] stopped", self.name)
|
||||
break
|
||||
|
||||
bwPacket = bwPacket_tmp
|
||||
logging.debug("[%s] <- bwPacket returned: %s", self._name, bwPacket)
|
||||
logging.debug("[%s] finished", self._name)
|
||||
logging.debug("[%s] bwPacket returned", self.name)
|
||||
logging.debug("[%s] finished", self.name)
|
||||
|
||||
self._routerTime = time.time() - tmpTime
|
||||
self._cumTime += self._routerTime
|
||||
|
||||
return bwPacket
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""!Property to get the name of the router"""
|
||||
return self._name
|
||||
def _getStatistics(self):
|
||||
"""!Returns statistical information's from last router run
|
||||
|
||||
@property
|
||||
def routeList(self):
|
||||
"""!Property to get a list of all route points of this router"""
|
||||
return self._routeList
|
||||
@return Statistics as pyton dict"""
|
||||
stats = {"type": "router",
|
||||
"runCount": self._runCount,
|
||||
"cumTime": self._cumTime,
|
||||
"moduleTime": self._routerTime}
|
||||
return stats
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
# todo think about implement threading for routers and the plugin calls (THREAD SAFETY!!!)
|
||||
import logging
|
||||
import importlib
|
||||
import time
|
||||
from boswatch.configYaml import ConfigYAML
|
||||
from boswatch.router.router import Router
|
||||
from boswatch.router.route import Route
|
||||
|
|
@ -30,16 +31,10 @@ class RouterManager:
|
|||
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
|
||||
self._startTime = int(time.time())
|
||||
|
||||
# if there is an error, router list would be empty (see tmp variable)
|
||||
def buildRouter(self, config):
|
||||
def buildRouters(self, config):
|
||||
"""!Initialize Routers from given config file
|
||||
|
||||
@param config: instance of ConfigYaml class
|
||||
|
|
@ -64,33 +59,42 @@ class RouterManager:
|
|||
|
||||
for route in router.get("route"):
|
||||
routeType = route.get("type")
|
||||
routeName = route.get("name")
|
||||
routeRes = route.get("res")
|
||||
routeName = route.get("name", default=routeRes)
|
||||
|
||||
routeConfig = route.get("config", default=ConfigYAML()) # if no config - build a empty
|
||||
|
||||
if routeType is None or routeName is None:
|
||||
if routeType is None or routeRes is None:
|
||||
logging.error("type or name not found in route: %s", route)
|
||||
return False
|
||||
|
||||
try:
|
||||
if routeType == "plugin":
|
||||
importedFile = importlib.import_module(routeType + "." + routeName)
|
||||
importedFile = importlib.import_module(routeType + "." + routeRes)
|
||||
loadedClass = importedFile.BoswatchPlugin(routeConfig)
|
||||
routerDict_tmp[routerName].addRoute(Route(routeName, loadedClass._run))
|
||||
routerDict_tmp[routerName].addRoute(Route(routeName,
|
||||
loadedClass._run,
|
||||
loadedClass._getStatistics,
|
||||
loadedClass._cleanup))
|
||||
|
||||
elif routeType == "module":
|
||||
importedFile = importlib.import_module(routeType + "." + routeName)
|
||||
importedFile = importlib.import_module(routeType + "." + routeRes)
|
||||
loadedClass = importedFile.BoswatchModule(routeConfig)
|
||||
routerDict_tmp[routerName].addRoute(Route(routeName, loadedClass._run))
|
||||
routerDict_tmp[routerName].addRoute(Route(routeName,
|
||||
loadedClass._run,
|
||||
loadedClass._getStatistics,
|
||||
loadedClass._cleanup))
|
||||
|
||||
elif routeType == "router":
|
||||
routerDict_tmp[routerName].addRoute(Route(routeName, routerDict_tmp[routeName].runRouter))
|
||||
routerDict_tmp[routerName].addRoute(Route(routeName, routerDict_tmp[routeRes].runRouter))
|
||||
|
||||
else:
|
||||
logging.error("unknown type '%s' in %s", routeType, route)
|
||||
return False
|
||||
|
||||
except ModuleNotFoundError:
|
||||
logging.error("%s not found: %s", route.get("type"), route.get("name"))
|
||||
# except ModuleNotFoundError: # only since Py3.6
|
||||
except ImportError:
|
||||
logging.error("%s not found: %s", route.get("type"), route.get("res"))
|
||||
return False
|
||||
|
||||
logging.debug("finished building routers")
|
||||
|
|
@ -98,7 +102,7 @@ class RouterManager:
|
|||
self._showRouterRoute()
|
||||
return True
|
||||
|
||||
def runRouter(self, routerRunList, bwPacket):
|
||||
def runRouters(self, routerRunList, bwPacket):
|
||||
"""!Run given Routers
|
||||
|
||||
@param routerRunList: string or list of router names in string form
|
||||
|
|
@ -112,6 +116,16 @@ class RouterManager:
|
|||
else:
|
||||
logging.warning("unknown router: %s", routerName)
|
||||
|
||||
self._saveStats() # write stats to stats file
|
||||
|
||||
def cleanup(self):
|
||||
"""!Run cleanup routines for all loaded route points"""
|
||||
for name, routerObject in self._routerDict.items():
|
||||
logging.debug("Start cleanup for %s", name)
|
||||
for routePoint in routerObject.routeList:
|
||||
if routePoint.cleanup:
|
||||
routePoint.cleanup()
|
||||
|
||||
def _showRouterRoute(self):
|
||||
"""!Show the routes of all routers"""
|
||||
for name, routerObject in self._routerDict.items():
|
||||
|
|
@ -120,3 +134,27 @@ class RouterManager:
|
|||
for routePoint in routerObject.routeList:
|
||||
counter += 1
|
||||
logging.debug(" %d. %s", counter, routePoint.name)
|
||||
|
||||
def _saveStats(self):
|
||||
"""!Save current statistics to file"""
|
||||
lines = []
|
||||
for name, routerObject in self._routerDict.items():
|
||||
lines.append("[" + name + "]")
|
||||
lines.append(" - Route points: " + str(len(routerObject.routeList)))
|
||||
lines.append(" - Runs: " + str(routerObject._getStatistics()['runCount']))
|
||||
for routePoint in routerObject.routeList:
|
||||
lines.append("[+] " + routePoint.name)
|
||||
if routePoint.statistics:
|
||||
if routePoint.statistics()['type'] == "module":
|
||||
lines.append(" - Runs: " + str(routePoint.statistics()['runCount']))
|
||||
lines.append(" - Run errors: " + str(routePoint.statistics()['moduleErrorCount']))
|
||||
elif routePoint.statistics()['type'] == "plugin":
|
||||
lines.append(" - Runs: " + str(routePoint.statistics()['runCount']))
|
||||
lines.append(" - Setup errors: " + str(routePoint.statistics()['setupErrorCount']))
|
||||
lines.append(" - Alarm errors: " + str(routePoint.statistics()['alarmErrorCount']))
|
||||
lines.append(" - Teardown errors: " + str(routePoint.statistics()['teardownErrorCount']))
|
||||
lines.append("")
|
||||
|
||||
with open("stats_" + str(self._startTime) + ".txt", "w") as stats:
|
||||
for line in lines:
|
||||
stats.write(line + "\n")
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ class RepeatedTimer:
|
|||
self._args = args
|
||||
self._kwargs = kwargs
|
||||
self._start = 0
|
||||
self._overdueCount = 0
|
||||
self._lostEvents = 0
|
||||
self.overdueCount = 0
|
||||
self.lostEvents = 0
|
||||
self._isRunning = False
|
||||
self._event = Event()
|
||||
self._thread = None
|
||||
|
|
@ -88,8 +88,8 @@ class RepeatedTimer:
|
|||
lostEvents = int(runTime / self._interval)
|
||||
logging.warning("timer overdue! interval: %0.3f sec. - runtime: %0.3f sec. - "
|
||||
"%d events lost - next call in: %0.3f sec.", self._interval, runTime, lostEvents, self.restTime)
|
||||
self._lostEvents += lostEvents
|
||||
self._overdueCount += 1
|
||||
self.lostEvents += lostEvents
|
||||
self.overdueCount += 1
|
||||
logging.debug("repeatedTimer thread stopped: %s", self._thread.name)
|
||||
self._thread = None # set to none after leave teh thread (running recognize)
|
||||
|
||||
|
|
@ -104,13 +104,3 @@ class RepeatedTimer:
|
|||
def restTime(self):
|
||||
"""!Property to get remaining time till next call"""
|
||||
return self._interval - ((time.time() - self._start) % self._interval)
|
||||
|
||||
@property
|
||||
def overdueCount(self):
|
||||
"""!Property to get a count over all overdues"""
|
||||
return self._overdueCount
|
||||
|
||||
@property
|
||||
def lostEvents(self):
|
||||
"""!Property to get a count over all lost events"""
|
||||
return self._lostEvents
|
||||
|
|
|
|||
|
|
@ -12,28 +12,43 @@
|
|||
@file: wildcard.py
|
||||
@date: 15.01.2018
|
||||
@author: Bastian Schroll
|
||||
@description: Little Helper to replace wildcards in stings
|
||||
@description: Functions to replace wildcards in stings
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
|
||||
# from boswatch.module import file
|
||||
|
||||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
# todo check function and document + write an test
|
||||
# todo maybe can be a module instead of a native boswatch piece
|
||||
# idea: maybe this can be a class with a register_wildcard() method
|
||||
# so the list with wildcards can be modified by other modules
|
||||
# todo check function - write an test
|
||||
|
||||
_additionalWildcards = {}
|
||||
|
||||
|
||||
def registerWildcard(wildcard, bwPacketField):
|
||||
"""!Register a new additional wildcard
|
||||
|
||||
@param wildcard: New wildcard string with format: '{WILDCARD}'
|
||||
@param bwPacketField: Field of the bwPacket which is used for wildcard replacement"""
|
||||
if wildcard in _additionalWildcards:
|
||||
logging.error("wildcard always registered: %s", wildcard)
|
||||
return
|
||||
logging.debug("register new wildcard %s for field: %s", wildcard, bwPacketField)
|
||||
_additionalWildcards[wildcard] = bwPacketField
|
||||
|
||||
|
||||
def replaceWildcards(message, bwPacket):
|
||||
"""!Replace the wildcards in a given message
|
||||
|
||||
@param message: Message in which wildcards should be replaced
|
||||
@param bwPacket: bwPacket instance with the replacement information
|
||||
@return Input message with the replaced wildcards"""
|
||||
_wildcards = {
|
||||
# formatting wildcards
|
||||
# todo check if br and par are needed - if not also change config
|
||||
"{BR}": "\r\n",
|
||||
"{LPAR}": "(",
|
||||
"{RPAR}": ")",
|
||||
"{TIME}": time.time(),
|
||||
"{TIME}": time.strftime("%d.%m.%Y %H:%M:%S"),
|
||||
|
||||
# info wildcards
|
||||
# server
|
||||
|
|
@ -78,7 +93,10 @@ def replaceWildcards(message, bwPacket):
|
|||
|
||||
# message for MSG packet is done in poc
|
||||
}
|
||||
for wildcard in _wildcards:
|
||||
message = message.replace(wildcard, _wildcards.get(wildcard))
|
||||
for wildcard, field in _wildcards.items():
|
||||
message = message.replace(wildcard, field)
|
||||
|
||||
for wildcard, field in _additionalWildcards.items():
|
||||
message = message.replace(wildcard, bwPacket.getField(field))
|
||||
|
||||
return message
|
||||
|
|
|
|||
108
bw_client.py
108
bw_client.py
|
|
@ -32,8 +32,6 @@ logging.debug("BOSWatch client has started ...")
|
|||
logging.debug("Import python modules")
|
||||
import argparse
|
||||
logging.debug("- argparse")
|
||||
import threading
|
||||
logging.debug("- threading")
|
||||
import queue
|
||||
logging.debug("- queue")
|
||||
import time
|
||||
|
|
@ -43,16 +41,14 @@ logging.debug("Import BOSWatch modules")
|
|||
from boswatch.configYaml import ConfigYAML
|
||||
from boswatch.network.client import TCPClient
|
||||
from boswatch.network.broadcast import BroadcastClient
|
||||
from boswatch.processManager import ProcessManager
|
||||
from boswatch.decoder.decoder import Decoder
|
||||
from boswatch.utils import header
|
||||
from boswatch.utils import misc
|
||||
|
||||
from boswatch.inputSource.sdrInput import SdrInput
|
||||
|
||||
header.logoToLog()
|
||||
header.infoToLog()
|
||||
|
||||
logging.debug("parse args")
|
||||
# With -h or --help you get the Args help
|
||||
parser = argparse.ArgumentParser(prog="bw_client.py",
|
||||
description="""BOSWatch is a Python Script to receive and
|
||||
|
|
@ -60,7 +56,7 @@ parser = argparse.ArgumentParser(prog="bw_client.py",
|
|||
epilog="""More options you can find in the extern client.ini
|
||||
file in the folder /config""")
|
||||
parser.add_argument("-c", "--config", help="Name to configuration File", required=True)
|
||||
parser.add_argument("-t", "--test", help="Start Client with testdata-set")
|
||||
parser.add_argument("-t", "--test", help="Start Client with testdata-set", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
bwConfig = ConfigYAML()
|
||||
|
|
@ -69,6 +65,10 @@ if not bwConfig.loadConfigFile(paths.CONFIG_PATH + args.config):
|
|||
exit(1)
|
||||
|
||||
# ========== CLIENT CODE ==========
|
||||
bwClient = None
|
||||
inputSource = None
|
||||
inputQueue = queue.Queue()
|
||||
|
||||
try:
|
||||
ip = bwConfig.get("server", "ip", default="127.0.0.1")
|
||||
port = bwConfig.get("server", "port", default="8080")
|
||||
|
|
@ -79,73 +79,33 @@ try:
|
|||
ip = broadcastClient.serverIP
|
||||
port = broadcastClient.serverPort
|
||||
|
||||
inputQueue = queue.Queue()
|
||||
inputThreadRunning = True
|
||||
if not args.test:
|
||||
logging.debug("loading input source: %s", bwConfig.get("client", "inputSource"))
|
||||
if bwConfig.get("client", "inputSource") == "sdr":
|
||||
inputSource = SdrInput(inputQueue, bwConfig.get("inputSource", "sdr"), bwConfig.get("decoder"))
|
||||
else:
|
||||
logging.fatal("Invalid input source: %s", bwConfig.get("client", "inputSource"))
|
||||
exit(1)
|
||||
|
||||
# ========== INPUT CODE ==========
|
||||
def handleSDRInput(dataQueue, sdrConfig, decoderConfig): # todo exception handling inside
|
||||
sdrProc = ProcessManager(str(sdrConfig.get("rtlPath", default="rtl_fm")))
|
||||
sdrProc.addArgument("-d " + str(sdrConfig.get("device", default="0"))) # device id
|
||||
sdrProc.addArgument("-f " + sdrConfig.get("frequency")) # frequencies
|
||||
sdrProc.addArgument("-p " + str(sdrConfig.get("error", default="0"))) # frequency error in ppm
|
||||
sdrProc.addArgument("-l " + str(sdrConfig.get("squelch", default="1"))) # squelch
|
||||
sdrProc.addArgument("-g " + str(sdrConfig.get("gain", default="100"))) # gain
|
||||
sdrProc.addArgument("-M fm") # set mode to fm
|
||||
sdrProc.addArgument("-E DC") # set DC filter
|
||||
sdrProc.addArgument("-s 22050") # bit rate of audio stream
|
||||
sdrProc.start()
|
||||
sdrProc.skipLinesUntil("Output at")
|
||||
|
||||
mmProc = ProcessManager(str(sdrConfig.get("mmPath", default="multimon-ng")), textMode=True)
|
||||
if decoderConfig.get("fms", default=0):
|
||||
mmProc.addArgument("-a FMSFSK")
|
||||
if decoderConfig.get("zvei", default=0):
|
||||
mmProc.addArgument("-a ZVEI1")
|
||||
if decoderConfig.get("poc512", default=0):
|
||||
mmProc.addArgument("-a POCSAG512")
|
||||
if decoderConfig.get("poc1200", default=0):
|
||||
mmProc.addArgument("-a POCSAG1200")
|
||||
if decoderConfig.get("poc2400", default=0):
|
||||
mmProc.addArgument("-a POCSAG2400")
|
||||
mmProc.addArgument("-f alpha")
|
||||
mmProc.addArgument("-t raw -")
|
||||
mmProc.setStdin(sdrProc.stdout)
|
||||
mmProc.start()
|
||||
mmProc.skipLinesUntil("Available demodulators:")
|
||||
|
||||
logging.info("start decoding")
|
||||
while inputThreadRunning:
|
||||
if not sdrProc.isRunning:
|
||||
logging.warning("rtl_fm was down - try to restart")
|
||||
sdrProc.start()
|
||||
sdrProc.skipLinesUntil("Output at") # last line form rtl_fm before data
|
||||
elif not mmProc.isRunning:
|
||||
logging.warning("multimon was down - try to restart")
|
||||
mmProc.start()
|
||||
mmProc.skipLinesUntil("Available demodulators:") # last line from mm before data
|
||||
elif sdrProc.isRunning and mmProc.isRunning:
|
||||
line = mmProc.readline()
|
||||
if line:
|
||||
dataQueue.put_nowait((line, time.time()))
|
||||
logging.debug("Add data to queue")
|
||||
print(line)
|
||||
logging.debug("stopping thread")
|
||||
mmProc.stop()
|
||||
sdrProc.stop()
|
||||
|
||||
# ========== INPUT CODE ==========
|
||||
|
||||
mmThread = threading.Thread(target=handleSDRInput, name="mmReader", args=(inputQueue, bwConfig.get("inputSource", "sdr"), bwConfig.get("decoder")))
|
||||
mmThread.daemon = True
|
||||
mmThread.start()
|
||||
inputSource.start()
|
||||
else:
|
||||
logging.warning("STARTING TESTMODE!")
|
||||
logging.debug("reading testdata from file")
|
||||
testFile = open("test/testdata.list", mode="r", encoding="utf-8")
|
||||
for testData in testFile:
|
||||
if (len(testData.rstrip(' \t\n\r')) > 1) and ("#" not in testData[0]):
|
||||
logging.info("Testdata: %s", testData.rstrip(' \t\n\r'))
|
||||
inputQueue.put_nowait((testData.rstrip(' \t\n\r'), time.time()))
|
||||
logging.debug("finished reading testdata")
|
||||
|
||||
bwClient = TCPClient()
|
||||
bwClient.connect(ip, port)
|
||||
while 1:
|
||||
|
||||
if not bwClient.isConnected:
|
||||
logging.warning("connection to server lost - sleep %d seconds", bwConfig.get("client", "reconnectDelay", default="3"))
|
||||
time.sleep(bwConfig.get("client", "reconnectDelay", default="3"))
|
||||
reconnectDelay = bwConfig.get("client", "reconnectDelay", default="3")
|
||||
logging.warning("connection to server lost - sleep %d seconds", reconnectDelay)
|
||||
time.sleep(reconnectDelay)
|
||||
bwClient.connect(ip, port)
|
||||
|
||||
elif not inputQueue.empty():
|
||||
|
|
@ -164,13 +124,16 @@ try:
|
|||
|
||||
for sendCnt in range(bwConfig.get("client", "sendTries", default="3")):
|
||||
bwClient.transmit(str(bwPacket))
|
||||
if bwClient.receive() == "[ack-]":
|
||||
if bwClient.receive() == "[ack]":
|
||||
logging.debug("ack ok")
|
||||
break
|
||||
logging.warning("cannot send packet - sleep %d seconds", bwConfig.get("client", "sendDelay", default="3"))
|
||||
time.sleep(bwConfig.get("client", "sendDelay", default="3"))
|
||||
sendDelay = bwConfig.get("client", "sendDelay", default="3")
|
||||
logging.warning("cannot send packet - sleep %d seconds", sendDelay)
|
||||
time.sleep(sendDelay)
|
||||
|
||||
else:
|
||||
if args.test:
|
||||
break
|
||||
time.sleep(0.1) # reduce cpu load (wait 100ms)
|
||||
# in worst case a packet have to wait 100ms until it will be processed
|
||||
|
||||
|
|
@ -183,7 +146,8 @@ except: # pragma: no cover
|
|||
logging.exception("BOSWatch interrupted by an error")
|
||||
finally:
|
||||
logging.debug("Starting shutdown routine")
|
||||
bwClient.disconnect()
|
||||
inputThreadRunning = False
|
||||
mmThread.join()
|
||||
if inputSource:
|
||||
inputSource.shutdown()
|
||||
if bwClient:
|
||||
bwClient.disconnect()
|
||||
logging.debug("BOSWatch client has stopped ...")
|
||||
|
|
|
|||
22
bw_server.py
22
bw_server.py
|
|
@ -46,11 +46,9 @@ from boswatch.network.broadcast import BroadcastServer
|
|||
from boswatch.router.routerManager import RouterManager
|
||||
from boswatch.utils import misc
|
||||
|
||||
|
||||
header.logoToLog()
|
||||
header.infoToLog()
|
||||
|
||||
logging.debug("parse args")
|
||||
# With -h or --help you get the Args help
|
||||
parser = argparse.ArgumentParser(prog="bw_server.py",
|
||||
description="""BOSWatch is a Python Script to receive and
|
||||
|
|
@ -67,10 +65,13 @@ if not bwConfig.loadConfigFile(paths.CONFIG_PATH + args.config):
|
|||
exit(1)
|
||||
|
||||
# ############################# begin server system
|
||||
try:
|
||||
bwRoutMan = None
|
||||
bwServer = None
|
||||
bcServer = None
|
||||
|
||||
try:
|
||||
bwRoutMan = RouterManager()
|
||||
if not bwRoutMan.buildRouter(bwConfig):
|
||||
if not bwRoutMan.buildRouters(bwConfig):
|
||||
logging.fatal("Error while building routers")
|
||||
exit(1)
|
||||
|
||||
|
|
@ -97,7 +98,9 @@ try:
|
|||
bwPacket.set("clientIP", data[0])
|
||||
misc.addServerDataToPacket(bwPacket, bwConfig)
|
||||
|
||||
bwRoutMan.runRouter(bwConfig.get("alarmRouter"), bwPacket)
|
||||
logging.debug("[ --- ALARM --- ]")
|
||||
bwRoutMan.runRouters(bwConfig.get("alarmRouter"), bwPacket)
|
||||
logging.debug("[ --- END ALARM --- ]")
|
||||
|
||||
incomingQueue.task_done()
|
||||
|
||||
|
|
@ -109,7 +112,10 @@ except: # pragma: no cover
|
|||
logging.exception("BOSWatch interrupted by an error")
|
||||
finally:
|
||||
logging.debug("Starting shutdown routine")
|
||||
del bwRoutMan
|
||||
bwServer.stop()
|
||||
bcServer.stop()
|
||||
if bwRoutMan:
|
||||
bwRoutMan.cleanup()
|
||||
if bwServer:
|
||||
bwServer.stop()
|
||||
if bcServer:
|
||||
bcServer.stop()
|
||||
logging.debug("BOSWatch server has stopped ...")
|
||||
|
|
|
|||
|
|
@ -19,8 +19,12 @@ router:
|
|||
- name: Router 1
|
||||
route:
|
||||
- type: module
|
||||
name: filter.modeFilter
|
||||
res: filter.modeFilter
|
||||
name: Filter Fms/Zvei
|
||||
config:
|
||||
allowed:
|
||||
- fms
|
||||
- zvei
|
||||
- type: plugin
|
||||
name: test plugin
|
||||
res: template_plugin
|
||||
|
|
|
|||
|
|
@ -110,7 +110,8 @@ Jeder Router kann eine beliebige Anzahl einzelner Routenpunkte enthalten. Diese
|
|||
|Feld|Beschreibung|Default|
|
||||
|----|------------|-------|
|
||||
|type|Art des Routenpunktes (module, plugin, router)||
|
||||
|name|Zu ladende Resource (Siehe entsprechende Kapitel)||
|
||||
|res|Zu ladende Resource (Siehe entsprechende Kapitel)||
|
||||
|name|Optionaler Name des Routenpunktes|gleich wie Resource|
|
||||
|config|Konfigurationseinstellungen des Routenpunktes (Siehe entsprechende Kapitel)||
|
||||
|
||||
**Beispiel:**
|
||||
|
|
@ -119,7 +120,8 @@ router:
|
|||
- name: Router 1
|
||||
route:
|
||||
- type: module
|
||||
name: filter.modeFilter
|
||||
res: filter.modeFilter
|
||||
name: Filter Fms/Zvei
|
||||
config:
|
||||
allowed:
|
||||
- fms
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ Die Plugin Basisklasse bietet einige Methoden, welche vom Plugin überschrieben
|
|||
Jedes Modul oder Plugin wird in einem Router folgendermaßen deklariert:
|
||||
```yaml
|
||||
- type: module # oder 'plugin'
|
||||
name: template_module # Name der Python Datei (ohne .py)
|
||||
res: template_module # Name der Python Datei (ohne .py)
|
||||
name: Mein Modul # optionaler Name
|
||||
config: # config-Sektion
|
||||
option1: value 1
|
||||
option2:
|
||||
|
|
@ -43,7 +44,7 @@ Jedes Modul oder Plugin wird in einem Router folgendermaßen deklariert:
|
|||
- list 1
|
||||
- list 2
|
||||
```
|
||||
Eine entsprechende Dokumentation der Parameter ist in der Dokumentation der [Konfiguration](../config.md) zu hinterlegen.
|
||||
Eine entsprechende Dokumentation der Parameter **muss** in der Dokumentation des jeweiligen Moduls oder Plugins hinterleget werden.
|
||||
|
||||
### Konfiguration verwenden
|
||||
Wird der Instanz eine Konfiguration übergeben wird diese in `self.config` abgelegt und kann wie folgt abgerufen werden:
|
||||
|
|
@ -57,7 +58,7 @@ Wird der Instanz eine Konfiguration übergeben wird diese in `self.config` abgel
|
|||
`self.config.get("option2", "underOption1")`
|
||||
> liefert `value 21`
|
||||
|
||||
- Es kann ein Default Wert angegeben werden
|
||||
- Es kann ein Default Wert angegeben werden (falls entsprechender Eintrag fehlt)
|
||||
`self.config.get("notSet", default="defValue")`
|
||||
> liefert `defValue`
|
||||
|
||||
|
|
@ -75,30 +76,58 @@ Aus dieser kann mittels `bwPacket.get(FIELDNAME)` das entsprechende Feld ausgele
|
|||
Mittels `bwPacket.set(FIELDNAME, VALUE)` kann ein Wert hinzugefügt oder modifiziert werden.
|
||||
Eine Auflistung der bereitgestellten Informationen findet sich im entsprechenden [BOSWatch Paket](packet.md) Dokumentation.
|
||||
|
||||
Bitte beachten:
|
||||
**Bitte beachten:**
|
||||
|
||||
- Selbst vom Modul hinzugefügte Felder **müssen** in der Modul Dokumentation unter `Paket Modifikation` aufgeführt werden.
|
||||
- Sollte ein Modul oder Plugin Felder benutzen, welche in einem anderen Modul erstellt werden, **muss** dies im Punkt `Abhänigkeiten` des jeweiligen Moduls oder Plugins zu dokumentieren.
|
||||
- Sollte ein Modul oder Plugin Felder benutzen, welche in einem anderen Modul erstellt werden, **muss** dies im Punkt `Abhänigkeiten` des jeweiligen Moduls oder Plugins dokumentiert werden.
|
||||
|
||||
### Zu beachten bei Module
|
||||
### Rückgabewert bei Modulen
|
||||
Module können Pakete beliebig verändern. Diese Änderungen werden im Router entsprechend weitergeleitet.
|
||||
|
||||
Mögliche Rückgabewerte eines Moduls:
|
||||
|
||||
- `return bwPacket` gibt das modifizierte bwPacket an den Router zurück
|
||||
- `return None` Router fährt mit dem unveränderten bwPacket fort (Input = Output)
|
||||
- `return False` Router stopt sofort die Ausführung (zB. in Filtern verwendet)
|
||||
- `return bwPacket` Gibt das modifizierte bwPacket an den Router zurück (Paket Modifikation)
|
||||
- `return None` Der Router fährt mit dem unveränderten bwPacket fort (Input = Output)
|
||||
- `return False` Der Router stopt sofort die Ausführung (zB. in Filtern verwendet)
|
||||
|
||||
### Zu beachten bei Plugins
|
||||
### Rückgabewert bei Plugins
|
||||
Plugins geben keine Pakete mehr zurück. Sie fungieren ausschließlich als Endpunkt.
|
||||
Die Plugin Basisklasse liefert intern immer ein `None` an den Router zurück,
|
||||
was zur weiteren Ausführung des Routers mit dem original Paket führt. Daher macht es in Plugins keinen Sinn ein Paket zu modifizieren.
|
||||
|
||||
---
|
||||
## Nutzung der Wildcards
|
||||
|
||||
Es gibt einige vordefinierte Wildcards welche in der [BOSWatch Paket](packet.md) Dokumentation zu finden sind.
|
||||
|
||||
Außerdem sind die folgenden allgemeinen Wildcards definiert:
|
||||
|
||||
- `{BR}` - Zeilenumbruch `\r\n`
|
||||
- `{LPAR}` - öffnende Klammer `(`
|
||||
- `{RPAR}` - schließende Klammer `)`
|
||||
- `{TIME}` - Aktueller Zeitstempel im Format `%d.%m.%Y %H:%M:%S`
|
||||
|
||||
### Wildcards registrieren [Module]
|
||||
Module können zusätzliche Wildcards registrieren welche anschließend in den Plugins ebenfalls geparst werden können.
|
||||
Dies kann über die interne Methode `self.registerWildcard(newWildcard, bwPacketField)` gemacht werden.
|
||||
|
||||
- `newWildcard` muss im folgenden Format angegeben werden: `{WILDCARD}`
|
||||
- `bwPacketField` ist der Name des Feldes im bwPacket - gestezt per `bwPacket.set(FIELDNAME, VALUE)`
|
||||
|
||||
**Bitte beachten:**
|
||||
|
||||
- Selbst vom Modul registrierte Wildcards **müssen** in der Modul Dokumentation unter `Zusätzliche Wildcards` aufgeführt werden.
|
||||
|
||||
### Wildcards parsen [Plugins]
|
||||
Das parsen der Wildcars funktioniert komfortabel über die interne Methode `msg = self.parseWildcards(msg)`.
|
||||
|
||||
- `msg` enstrpicht dabei dem String in welchem die Wildcards ersetzt werden sollen
|
||||
|
||||
Die Platzhalter der Wildcards findet man in der [BOSWatch Paket](packet.md) Dokumentation.
|
||||
|
||||
Sollten Module zusätzliche Wildcards registrieren, findet man Informationen dazu in der jeweiligen Modul Dokumentation
|
||||
|
||||
---
|
||||
## Richtiges Logging
|
||||
tbd ...
|
||||
|
||||
---
|
||||
## Wildcards parsen (Plugin only)
|
||||
Das parsen der Wildcars funktioniert komfortabel über die interne Methode `self.parseWildcards(MSG)`.
|
||||
Die Platzhalter der Wildcards findet man in der [BOSWatch Paket](packet.md) Dokumentation.
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# <center>BOSWatch Packet Format</center>
|
||||
# <center>BOSWatch Paket Format</center>
|
||||
|
||||
Ein BOSWatch Datenpaket wird in einem Python Dict abgebildet. In der nachfolgenden Tabelle sind die genutzten Felder abgebildet.
|
||||
|
||||
|
|
@ -48,13 +48,4 @@ Ein BOSWatch Datenpaket wird in einem Python Dict abgebildet. In der nachfolgend
|
|||
|status|X||||`{STAT}`||
|
||||
|direction|X||||`{DIR}`||
|
||||
|dirextionText|X||||`{DIRT}`|(Fhz->Lst, Lst->Fhz)|
|
||||
|vehicle|X||||`{VEC}`||
|
||||
|vehicle|X||||`{VEC}`||
|
||||
|tacticalInfo|X||||`{TACI}`|(I, II, III, IV)|
|
||||
|
||||
---
|
||||
## Weitere Wildcards
|
||||
- `{BR}` - Zeilenumbruch `\r\n`
|
||||
- `{LPAR}` - öffnende Klammer `(`
|
||||
- `{RPAR}` - schließende Klammer `)`
|
||||
- `{TIME}` - Aktueller zeitstempel
|
||||
|
|
|
|||
1
docu/docs/img/client.drawio
Normal file
1
docu/docs/img/client.drawio
Normal file
|
|
@ -0,0 +1 @@
|
|||
<mxfile host="www.draw.io" modified="2019-10-26T07:55:48.917Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/77.0.3865.90 Chrome/77.0.3865.90 Safari/537.36" etag="82d1pq0jwhTcNaaLB_Ck" version="12.1.7" type="device" pages="1"><diagram id="9aEBdlF2oZdVepulqs3T" name="Page-1">7Vjbcts2EP0aPdrDi0gpj5FsJ9Nxpm7VadMnD0iuSNQgwYJLU+rXFzeSoijJlyhOMq09o9EeAIvdPdhDQhN/mW8+CFJmn3gCbOI5yWbiX008bx5M5acCtgbwZ74BUkETA7k9sKL/gAUdi9Y0gWowETlnSMshGPOigBgHGBGCN8Npa86Gu5YkhRGwigkbo3/QBDObljfr8Y9A06zd2Q3fmZGctJNtJlVGEt7sQP71xF8KztF8yzdLYKp2bV3Mupsjo11gAgp8zoKmWa0XC//q4Sf8rYohvZn/vrqY29hw2yYMiczfmlxgxlNeEHbdowvB6yIB5dWRVj/nlvNSgq4E/wLErSWT1MgllGHO7ChsKH5Wyy/DmTX/1GZgrauNda6N7Y5xB4LmgCBarECx/bxr7HhSZu9KW62vcfFsPSteixhOVKw9hESkgCfmeWaeKufOBpaaD8BlFmIrJwhgBOnj8LgRe2rTbl5PrPxiuX0Bz9bvI2G13WlE/CMIpPLgv2c0LSSEis1Fi96SCNgdryhSrkYjjshzOYHtDcSyooqeBbF+OmDnBPAaGS1g2TWt4mRNGVtyxoWOx1/PY4hjiVco+APsjERSVQK1IhUkodL9FRXSjdm/UAeyW9X2rKeQjJQq13yTKqm6LAAbLh6qS+WF3yNvdJhHj4YqBWxOktmqnm15q3mhNZteQILAQNmudjjOV2I//CZd/uat6T2zNYPvqjW9A60ZMlQNwWUFvDBFXRWDRfuAmbTDbfh3zduBi0qz815OcKflph9svfwKMagMZdUyASRpvcpMDu0u4VEAkXgaqUpSPCtI/3SQpEjUuwTE8jVDx6w+E4JEd5dsLi8kuZKtIqrKYdwmhGFY9PUxCWT363yinrndFvS8aetkT8Y/LvQXZJTX8q0q58VFkZ7KaXQy9pRlqBtNRhFWJdF920jZHWrESNod/Td+GNws1f951NkNn5RnN3DeUp9dd1TGNxDo1wvt9NwCapfecarFzBIV7hHlunsEGKW3q/Y46MJ4PS3Tb6nMnwgtvm9ZXoFWYyPCssUfAPXtrMMqELIrVZXXHRbxqiEYZ52M6/HKXJjijCrxsVLu0KKs0aj7C2Xwxxep6fRpkZq9qUjN/hsvkcEzXyLProFfRE5wVKo6YfilBjl2UDH2eG0vSvFWXtISfSl6okUiw/Nt1AFSDlLN/s/mqnesl7q73LHb3xl6yZ8Gg16ajXspPNBK86/VSe8OkPX/Zdxexhu5nEFV3efy4ZCf5wCE+2IajMXUOXQCghefAGn2P+mZF5H+d1H/+l8=</diagram></mxfile>
|
||||
BIN
docu/docs/img/client.png
Normal file
BIN
docu/docs/img/client.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
1
docu/docs/img/router.drawio
Normal file
1
docu/docs/img/router.drawio
Normal file
|
|
@ -0,0 +1 @@
|
|||
<mxfile host="www.draw.io" modified="2019-10-28T07:09:19.767Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36" etag="YtlXSxxCTdQhtX4skT94" version="12.1.7" type="device" pages="1"><diagram id="LuQiMuTTxK4eFKppVUgf" name="Page-1">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</diagram></mxfile>
|
||||
BIN
docu/docs/img/router.png
Normal file
BIN
docu/docs/img/router.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
1
docu/docs/img/server.drawio
Normal file
1
docu/docs/img/server.drawio
Normal file
|
|
@ -0,0 +1 @@
|
|||
<mxfile host="www.draw.io" modified="2019-10-26T14:21:37.009Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/77.0.3865.90 Chrome/77.0.3865.90 Safari/537.36" etag="lnG6lN8TfbR21d0bu-ug" version="12.1.7" type="device" pages="1"><diagram id="9aEBdlF2oZdVepulqs3T" name="Page-1">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</diagram></mxfile>
|
||||
BIN
docu/docs/img/server.png
Normal file
BIN
docu/docs/img/server.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 45 KiB |
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
<center>
|
||||

|
||||
|
||||
Falls du uns unterstützen möchtest würden wir uns über eine Spende freuen.
|
||||
Server, Hosting, Domain sowie Kaffee kosten leider Geld ;-)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CLK9VBN2MSLZY&source=url)
|
||||
</center>
|
||||
|
||||
**Es wird darauf hingewiesen, dass für die Teilnahme am BOS-Funk nur nach den Technischen Richtlinien der BOS zugelassene Funkanlagen verwendet werden dürfen.**
|
||||
|
|
@ -15,3 +11,12 @@ Server, Hosting, Domain sowie Kaffee kosten leider Geld ;-)
|
|||
---
|
||||
|
||||
**The intercept of the German BOS radio is strictly prohibited and will be prosecuted. The use is only permitted for authorized personnel.**
|
||||
|
||||
---
|
||||
|
||||
<center>
|
||||
Falls du uns unterstützen möchtest würden wir uns über eine Spende freuen.
|
||||
Server, Hosting, Domain sowie Kaffee kosten leider Geld ;-)
|
||||
|
||||
[](https://www.paypal.me/BSchroll)
|
||||
</center>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# <center>Broadcast Service</center>
|
||||
|
||||
Durch den Broadcast Service haben CLients die Möglichkeit, automatisch den Server zu finden und sich mit diesem zu verbinden. Dazu stellt der Server die benötigten Verbinungsinformationen per Broadcast Service bereit.
|
||||
Durch den Broadcast Service haben Clients die Möglichkeit, automatisch den Server zu finden und sich mit diesem zu verbinden. Dazu stellt der Server die benötigten Verbinungsinformationen per Broadcast Service bereit.
|
||||
|
||||
**Hinweis:** *Server und Client müssen sich im selben Subnetz befinden.*
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ Durch den Broadcast Service haben CLients die Möglichkeit, automatisch den Serv
|
|||
## Aufbau
|
||||
|
||||
Der Broadcast Service besteht aus 2 Teilen - einem Server und einem Clienten.
|
||||
Nachfolgend soll der Ablauf einer Verbunding des Clienen zum Server mittels des Broadcast Services erklärt werden.
|
||||
Nachfolgend soll der Ablauf einer Verbindung des Clienten zum Server mittels des Broadcast Services erklärt werden.
|
||||
|
||||
<center></center>
|
||||
|
||||
|
|
@ -16,16 +16,16 @@ Nachfolgend soll der Ablauf einer Verbunding des Clienen zum Server mittels des
|
|||
## Ablauf
|
||||
|
||||
### Schritt 1 - Broadcast Server starten
|
||||
Im ersten Schritt wird auf dem Server ein zusätzlicher Broadcast Server in einem seperaten Thread gestartet. Dieser lauscht auf einem festgelegten Port auf UDP Broadcast Pakete. Nun kann eine beliebige Anzahl von Clienten mittels des Broadcast Services die Verbinundgdaten des Server abfragen.
|
||||
Im ersten Schritt wird auf dem Server ein zusätzlicher Broadcast Server in einem seperaten Thread gestartet. Dieser lauscht auf einem festgelegten Port auf UDP Broadcast Pakete. Nun kann eine beliebige Anzahl von Clienten mittels des Broadcast Services die Verbindungsgdaten des Servers abfragen.
|
||||
|
||||
### Schritt 2 - Broadcast durch Clienten
|
||||
Die Client Applikation startet nun zur Abfrage der Verbindungsdaten einen BC Clienten und sendet dort auf dem festgelegten Port ein Paket per UDP Boradcast. Der Inhalt des Paketes ist das Magic-Word `<BW3-Request>` und wird von allen im selben Subnetz vohandenen Gegenstellen empfangen. Nun wird auf eine Antwort des Broadcast Server mit den Verbindungsdaten gewartet.
|
||||
Die Client Applikation startet nun zur Abfrage der Verbindungsdaten einen BC Clienten und sendet dort auf dem festgelegten Port ein Paket per UDP Boradcast. Der Inhalt des Paketes ist das Magic-Word `<BW3-Request>` und wird von allen im selben Subnetz befindlichen Gegenstellen empfangen. Nun wartet der Client auf eine Antwort des Broadcast Server mit den Verbindungsdaten.
|
||||
|
||||
### Schritt 3 - Verbindungsdaten senden
|
||||
Wird nun ein Broadcast Paket empfangen, prüft der BC Server die Daten auf das Magic-Word `<BW3-Request>`. Wird dieses erkannt, liest der Server die Absender-IP-Addresse aus dem Paket aus und sendet eine Antwort direkt an diesen Clienten. Dieses Antwortpaket sieht folgendermaßen aus: `<BW3-Result>;8080` wobei die `8080` hier den normalen TCP Kommunikationsport des Server darstellt.
|
||||
Wird nun ein Broadcast Paket empfangen, prüft der BC Server die Daten auf das Magic-Word `<BW3-Request>`. Wird dieses erkannt, liest der Server die Absender-IP-Addresse aus dem Paket aus und sendet eine Antwort direkt an diesen Clienten. Dieses Antwortpaket sieht folgendermaßen aus: `<BW3-Result>;8080` wobei die `8080` hier den normalen TCP Kommunikationsport des Servers darstellt.
|
||||
|
||||
### Schritt 4 - Verbindungsdaten empfangen
|
||||
Nachdem der Client das direkt an ihn gerichtete Paket mit den Verbindungsdaten vom Server empfangen hat, prüft er auf das Magic-Word `<BW3-Result>`. Ist dieses enthalten wird der Port für die TCP Verbundindung aus dem Paket extrahiert. Außerdem wird die IP-Addresse des Absenders aus dem Paket gelesen.
|
||||
Anschließend stehen dem Clienten die Verbindungsdaten des Servers zur Verfügung und er kann sich per TCP über den angegebenen Port mit dem BOSWatch Server verbindden um seine Alarmierungs-Pakete zu versenden.
|
||||
Nachdem der Client das direkt an ihn gerichtete Paket mit den Verbindungsdaten vom Server empfangen hat, prüft er auf das Magic-Word `<BW3-Result>`. Ist dieses enthalten wird der Port für die TCP Verbindung aus dem Paket extrahiert. Außerdem wird die IP-Addresse des Absenders aus dem Paket gelesen.
|
||||
Anschließend stehen dem Clienten die Verbindungsdaten des Servers zur Verfügung und er kann sich per TCP auf den angegebenen Port mit dem BOSWatch Server verbinden um seine Alarmierungs-Pakete abzusetzen.
|
||||
|
||||
Da der Broadcast Server in einem eigenen Thread, unabhängig vom Hauptprogram läuft, können ganz einfach weitere Clienten per Broadcast Service die Verbindungsdaten des Servers abrufen.
|
||||
|
|
|
|||
93
docu/docs/information/router.md
Normal file
93
docu/docs/information/router.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# <center>Routing Mechanismus</center>
|
||||
|
||||
BOSWatch 3 hat einen Routing Mechanismus integriert. Mit diesem ist es auf einfache Weise möglich, den Verlauf von Alarmpaketen zu steuern.
|
||||
|
||||
|
||||
---
|
||||
## Ablauf
|
||||
|
||||
Nachfolgender Ablauf soll am Beispiel eines Alarms mit einem Pocsag Paket erklärt werden.
|
||||
|
||||
<center></center>
|
||||
|
||||
- BOSWatch startet alle Router, welche in der config als `alarmRouter` konfiguriert worden sind (in diesem Fall nur `Router1`)
|
||||
- Der Router `Router1` beginnt seine Ausführung und arbeitet die einzelnen Routenpunkte sequentiell ab
|
||||
- Das Modul `descriptor` wird aufgerufen und fügt ggf. Beschreibungen zum Paket hinzu
|
||||
- Das Modul `doubleFilter` wird aufgerufen und blockiert doppelte Alarme
|
||||
(hier würde die Ausführung dieses Routers und damit des kompletten Alarmprozesses stoppen wenn der Alarm als doppelter erkannt würde)
|
||||
- Der Router `Router2` wir nun aufgerufen (bis zur Rückkehr aus `Router2` ist der Router `Router1` angehalten)
|
||||
- Der Router `Router2` beginnt seine Ausführung und arbeitet die einzelnen Routenpunkte sequentiell ab
|
||||
- Das Modul `modeFilter` wird aufgerufen und stoppt den Router da es sich nicht um ein FMS Paket handelt
|
||||
- Es wird zur Ausführung von `Router1` zurückgekehrt
|
||||
- Der Router `Router3` beginnt seine Ausführung und arbeitet die einzelnen Routenpunkte sequentiell ab
|
||||
- Das Modul `modeFilter` wird aufgerufen und leitet das Paket weiter da es sich um ein Pocsag Paket handelt
|
||||
- Das Plugin `Telegram` wird aufgerufen
|
||||
- Das Plugin `MySQL` wird augerufen
|
||||
- Es wird zur Ausführung von `Router1` zurückgekehrt
|
||||
- Der Router `Router1` setzt seine Ausführung fort
|
||||
- Das Modul `modeFilter` wird aufgerufen und stoppt den Router da es sich nicht um ein ZVEI Paket handelt
|
||||
|
||||
Jetzt sind alle Routenpunkte abgearbeitet und die Alarmierung damit abgeschlossen.
|
||||
|
||||
---
|
||||
## Konfiguration
|
||||
|
||||
Nachfolgend ist die Router Konfiguration des BW3-Servers für das obige Beispiel zu finden:
|
||||
|
||||
```yaml
|
||||
alarmRouter:
|
||||
- Router1
|
||||
|
||||
router:
|
||||
- name: Router1
|
||||
route:
|
||||
- type: module
|
||||
res: descriptor
|
||||
config:
|
||||
[...]
|
||||
- type: module
|
||||
res: filter.doubleFilter
|
||||
config:
|
||||
[...]
|
||||
- type: router
|
||||
res: Router2
|
||||
- type: router
|
||||
res: Router3
|
||||
- type: module
|
||||
res: filter.modeFilter
|
||||
config:
|
||||
allowed:
|
||||
- zvei
|
||||
- type: plugin
|
||||
res: sms
|
||||
config:
|
||||
[...]
|
||||
|
||||
- name: Router2
|
||||
route:
|
||||
- type: module
|
||||
res: filter.modeFilter
|
||||
config:
|
||||
allowed:
|
||||
- fms
|
||||
- type: plugin
|
||||
res: mysql
|
||||
config:
|
||||
[...]
|
||||
|
||||
- name: Router3
|
||||
route:
|
||||
- type: module
|
||||
res: filter.modeFilter
|
||||
config:
|
||||
allowed:
|
||||
- pocsag
|
||||
- type: plugin
|
||||
res: telegram
|
||||
config:
|
||||
[...]
|
||||
- type: plugin
|
||||
res: mysql
|
||||
config:
|
||||
[...]
|
||||
```
|
||||
|
|
@ -17,6 +17,8 @@ nachträglich an den Server übermittelt werden.
|
|||
|
||||
Dabei überwacht der Client selbstständig die benötigten Programme zum Empfang der Daten und startet diese bei einem Fehler ggf. neu.
|
||||
|
||||
<center></center>
|
||||
|
||||
---
|
||||
## BOSWatch Server
|
||||
|
||||
|
|
@ -26,4 +28,6 @@ Verarbeitung der Daten.
|
|||
Auch hier werden die empfangenen Daten in From von bwPacket's in einer Queue abelegt um zu gewährleisten, das auch während einer länger
|
||||
dauernden Plugin Ausführung alle Pakete korrekt empfangen werden können und es zu keinen Verlusten kommt.
|
||||
Die Verarbeitung der Pakete geschieht anschließend in sogenannten Routern, welche aufgrund ihres Umfangs jedoch in einem eigenen Kapitel
|
||||
erklärt werden. Diese steuern die Verteilung der Daten an die einzelnen Plugins.
|
||||
erklärt werden. Diese steuern die Verteilung der Daten an die einzelnen Plugins.
|
||||
|
||||
<center></center>
|
||||
66
docu/docs/modul/descriptor.md
Normal file
66
docu/docs/modul/descriptor.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# <center>Descriptor</center>
|
||||
---
|
||||
|
||||
## Beschreibung
|
||||
Mit diesem Modul können einem Alarmpaket beliebige Beschreibungen in Abhänigkeit der enthaltenen Informationen hinzugefügt werden.
|
||||
|
||||
## Resource
|
||||
`descriptor`
|
||||
|
||||
## Konfiguration
|
||||
|
||||
Informationen zum Aufbau eines [BOSWatch Pakets](../develop/packet.md)
|
||||
|
||||
|Feld|Beschreibung|Default|
|
||||
|----|------------|-------|
|
||||
|scanField|Feld des BW Pakets welches geprüft werden soll||
|
||||
|descrField|Name des Feldes im BW Paket in welchem die Beschreibung gespeichert werden soll||
|
||||
|wildcard|Optional: Es kann für das angelegte `descrField` automatisch ein Wildcard registriert werden|None|
|
||||
|descriptions|Liste der Beschreibungen||
|
||||
|
||||
#### `descriptions:`
|
||||
|
||||
|Feld|Beschreibung|Default|
|
||||
|----|------------|-------|
|
||||
|for|Inhalt im `scanField` auf welchem geprüft werden soll||
|
||||
|add|Beschreibungstext welcher im `descrField` hinterlegt werden soll||
|
||||
|
||||
**Beispiel:**
|
||||
```yaml
|
||||
- type: module
|
||||
res: descriptor
|
||||
config:
|
||||
- scanField: zvei
|
||||
descrField: description
|
||||
wildcard: "{DESCR}"
|
||||
descriptions:
|
||||
- for: 12345
|
||||
add: FF DescriptorTest
|
||||
- for: 45678
|
||||
add: FF TestDescription
|
||||
- scanField: status
|
||||
descrField: fmsStatDescr
|
||||
wildcard: "{STATUSTEXT}"
|
||||
descriptions:
|
||||
- for: 1
|
||||
add: Frei (Funk)
|
||||
- for: 2
|
||||
add: Frei (Wache)
|
||||
- ...
|
||||
```
|
||||
|
||||
---
|
||||
## Abhängigkeiten
|
||||
|
||||
- keine
|
||||
|
||||
---
|
||||
## Paket Modifikationen
|
||||
|
||||
- Wenn im Paket das Feld `scanField` vorhanden ist, wird das Feld `descrField` dem Paket hinzugefügt
|
||||
- Wenn keine Beschreibung vorhanden ist, wird im Feld `descrField` der Inhalt des Feldes `scanField` hinterlegt
|
||||
|
||||
---
|
||||
## Zusätzliche Wildcards
|
||||
|
||||
- Von der Konfiguration abhängig
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
---
|
||||
|
||||
## Beschreibung
|
||||
Mit diesem Modul ist es Möglich, die Pakete auf bestimmte Modes (FMS, POCSAG, ZVEI) zu Filtern. Je nach Konfiguration werden Pakete eines bestimmten Modes im aktuellen Router weitergeleitet oder verworfen.
|
||||
Mit diesem Modul ist es möglich, die Pakete auf bestimmte Modes (FMS, POCSAG, ZVEI) zu Filtern. Je nach Konfiguration werden Pakete eines bestimmten Modes im aktuellen Router weitergeleitet oder verworfen.
|
||||
|
||||
## Resource
|
||||
`filter.modeFilter`
|
||||
|
|
@ -16,7 +16,7 @@ Mit diesem Modul ist es Möglich, die Pakete auf bestimmte Modes (FMS, POCSAG, Z
|
|||
**Beispiel:**
|
||||
```yaml
|
||||
- type: module
|
||||
name: filter.modeFilter
|
||||
res: filter.modeFilter
|
||||
config:
|
||||
allowed:
|
||||
- fms
|
||||
|
|
@ -32,3 +32,8 @@ Mit diesem Modul ist es Möglich, die Pakete auf bestimmte Modes (FMS, POCSAG, Z
|
|||
## Paket Modifikationen
|
||||
|
||||
- keine
|
||||
|
||||
---
|
||||
## Zusätzliche Wildcards
|
||||
|
||||
- keine
|
||||
|
|
|
|||
65
docu/docs/modul/regex_filter.md
Normal file
65
docu/docs/modul/regex_filter.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# <center>Regex Filter</center>
|
||||
---
|
||||
|
||||
## Beschreibung
|
||||
Mit diesem Modul ist es möglich, komplexe Filter basierend auf Regulären Ausdrücken (Regex) anzulegen.
|
||||
Für einen Filter können beliebig viele Checks angelegt werden, welche Felder eines BOSWatch Pakets mittels Regex prüfen.
|
||||
|
||||
Folgendes gilt:
|
||||
|
||||
- Die Filter werden nacheinander abgearbeitet
|
||||
- Innerhalb des Filters werden die Checks nacheinander abgearbeitet
|
||||
- Sobald ein einzelner Check fehlschlägt ist der ganze Filter fehlgeschlagen
|
||||
- Sobald ein Filter mit all seinen Checks besteht, wird mit der Ausführung des Routers fortgefahren
|
||||
- Sollten alle Filter fehlschlagen wird die Ausführung des Routers beendet
|
||||
|
||||
Vereinfacht kann man sagen, dass einzelnen Router ODER-verknüpft und die jeweiligen Checks UND-verknüpft sind.
|
||||
|
||||
## Resource
|
||||
`filter.regexFilter`
|
||||
|
||||
## Konfiguration
|
||||
|
||||
|Feld|Beschreibung|Default|
|
||||
|----|------------|-------|
|
||||
|name|Beliebiger Name des Filters||
|
||||
|checks|Liste der einzelnen Checks innerhalb des Filters||
|
||||
|
||||
#### `checks:`
|
||||
|
||||
|Feld|Beschreibung|Default|
|
||||
|----|------------|-------|
|
||||
|field|Name des Feldes innerhalb des BOSWatch Pakets welches untersucht werden soll||
|
||||
|regex|Regulärer Ausdruck (Bei Sonderzeichen " " verwenden)||
|
||||
|
||||
**Beispiel:**
|
||||
```yaml
|
||||
- type: module
|
||||
res: filter.regexFilter
|
||||
config:
|
||||
- name: "Zvei filter"
|
||||
checks:
|
||||
- field: zvei
|
||||
regex: "65[0-9]{3}" # all zvei with starting 65
|
||||
- name: "FMS Stat 3"
|
||||
checks:
|
||||
- field: mode
|
||||
regex: "fms" # check if mode is fms
|
||||
- field: status
|
||||
regex: "3" # check if status is 3
|
||||
```
|
||||
|
||||
---
|
||||
## Abhängigkeiten
|
||||
|
||||
- keine
|
||||
|
||||
---
|
||||
## Paket Modifikationen
|
||||
|
||||
- keine
|
||||
|
||||
---
|
||||
## Zusätzliche Wildcards
|
||||
|
||||
- keine
|
||||
|
|
@ -15,10 +15,12 @@ nav:
|
|||
- Server/Cient Prinzip: information/serverclient.md
|
||||
- Broadcast Service: information/broadcast.md
|
||||
# - Modul/Plugin Konzept: tbd.md
|
||||
# - Routing Mechanismus: tbd.md
|
||||
- Routing Mechanismus: information/router.md
|
||||
- Changelog: changelog.md
|
||||
- Module:
|
||||
- Mode Filter: modul/mode_filter.md
|
||||
- Regex Filter: modul/regex_filter.md
|
||||
- Descriptor: modul/descriptor.md
|
||||
- Plugins: tbd.md
|
||||
- Entwickler:
|
||||
- Eigenes Modul/Plugin schreiben: develop/ModulPlugin.md
|
||||
|
|
|
|||
58
module/descriptor.py
Normal file
58
module/descriptor.py
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""!
|
||||
____ ____ ______ __ __ __ _____
|
||||
/ __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ /
|
||||
/ __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ <
|
||||
/ /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ /
|
||||
/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/
|
||||
German BOS Information Script
|
||||
by Bastian Schroll
|
||||
|
||||
@file: descriptor.py
|
||||
@date: 27.10.2019
|
||||
@author: Bastian Schroll
|
||||
@description: Module to add descriptions to bwPackets
|
||||
"""
|
||||
import logging
|
||||
from module.moduleBase import ModuleBase
|
||||
|
||||
# ###################### #
|
||||
# Custom plugin includes #
|
||||
|
||||
# ###################### #
|
||||
|
||||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class BoswatchModule(ModuleBase):
|
||||
"""!Adds descriptions to bwPackets"""
|
||||
def __init__(self, config):
|
||||
"""!Do not change anything here!"""
|
||||
super().__init__(__name__, config) # you can access the config class on 'self.config'
|
||||
|
||||
def onLoad(self):
|
||||
"""!Called by import of the plugin"""
|
||||
for descriptor in self.config:
|
||||
if descriptor.get("wildcard", default=None):
|
||||
self.registerWildcard(descriptor.get("wildcard"), descriptor.get("descrField"))
|
||||
|
||||
def doWork(self, bwPacket):
|
||||
"""!start an run of the module.
|
||||
|
||||
@param bwPacket: A BOSWatch packet instance"""
|
||||
for descriptor in self.config:
|
||||
for description in descriptor.get("descriptions"):
|
||||
if not bwPacket.get(descriptor.get("scanField")):
|
||||
break # scanField is not available in this packet
|
||||
bwPacket.set(descriptor.get("descrField"), description.get("for"))
|
||||
if str(description.get("for")) == bwPacket.get(descriptor.get("scanField")):
|
||||
logging.debug("Description '%s' added in packet field '%s'",
|
||||
description.get("add"), descriptor.get("descrField"))
|
||||
bwPacket.set(descriptor.get("descrField"), description.get("add"))
|
||||
break # this descriptor has found a description - run next descriptor
|
||||
return bwPacket
|
||||
|
||||
def onUnload(self):
|
||||
"""!Called by destruction of the plugin"""
|
||||
pass
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
@description: Filter module for the packet type
|
||||
"""
|
||||
import logging
|
||||
from module.module import Module
|
||||
from module.moduleBase import ModuleBase
|
||||
|
||||
# ###################### #
|
||||
# Custom plugin includes #
|
||||
|
|
@ -25,7 +25,7 @@ from module.module import Module
|
|||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class BoswatchModule(Module):
|
||||
class BoswatchModule(ModuleBase):
|
||||
"""!Filter of specific bwPacket mode"""
|
||||
def __init__(self, config):
|
||||
"""!Do not change anything here!"""
|
||||
|
|
@ -38,8 +38,7 @@ class BoswatchModule(Module):
|
|||
def doWork(self, bwPacket):
|
||||
"""!start an run of the module.
|
||||
|
||||
@param bwPacket: A BOSWatch packet instance
|
||||
@return bwPacket or False"""
|
||||
@param bwPacket: A BOSWatch packet instance"""
|
||||
|
||||
for mode in self.config.get("allowed", default=[]):
|
||||
if bwPacket.get("mode") == mode:
|
||||
|
|
|
|||
65
module/filter/regexFilter.py
Normal file
65
module/filter/regexFilter.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""!
|
||||
____ ____ ______ __ __ __ _____
|
||||
/ __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ /
|
||||
/ __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ <
|
||||
/ /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ /
|
||||
/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/
|
||||
German BOS Information Script
|
||||
by Bastian Schroll
|
||||
|
||||
@file: regexFilter.py
|
||||
@date: 26.10.2019
|
||||
@author: Bastian Schroll
|
||||
@description: Regex filter module
|
||||
"""
|
||||
import logging
|
||||
from module.moduleBase import ModuleBase
|
||||
|
||||
# ###################### #
|
||||
# Custom plugin includes #
|
||||
import re
|
||||
# ###################### #
|
||||
|
||||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class BoswatchModule(ModuleBase):
|
||||
"""!Regex based filter mechanism"""
|
||||
def __init__(self, config):
|
||||
"""!Do not change anything here!"""
|
||||
super().__init__(__name__, config) # you can access the config class on 'self.config'
|
||||
|
||||
def onLoad(self):
|
||||
"""!Called by import of the plugin"""
|
||||
pass
|
||||
|
||||
def doWork(self, bwPacket):
|
||||
"""!start an run of the module.
|
||||
|
||||
@param bwPacket: A BOSWatch packet instance"""
|
||||
for regexFilter in self.config:
|
||||
checkFailed = False
|
||||
logging.debug("try filter '%s' with %d check(s)", regexFilter.get("name"), len(regexFilter.get("checks")))
|
||||
|
||||
for check in regexFilter.get("checks"):
|
||||
fieldData = bwPacket.get(check.get("field"))
|
||||
|
||||
if not fieldData or not re.search(check.get("regex"), fieldData):
|
||||
logging.debug("[-] field '%s' with regex '%s'", check.get("field"), check.get("regex"))
|
||||
checkFailed = True
|
||||
break # if one check failed we break this filter
|
||||
else:
|
||||
logging.debug("[+] field '%s' with regex '%s'", check.get("field"), check.get("regex"))
|
||||
|
||||
if not checkFailed:
|
||||
logging.debug("[PASSED] filter '%s'", regexFilter.get("name"))
|
||||
return None # None -> Router will go on with this packet
|
||||
logging.debug("[FAILED] filter '%s'", regexFilter.get("name"))
|
||||
|
||||
return False # False -> Router will stop further processing
|
||||
|
||||
def onUnload(self):
|
||||
"""!Called by destruction of the plugin"""
|
||||
pass
|
||||
|
|
@ -9,32 +9,34 @@
|
|||
German BOS Information Script
|
||||
by Bastian Schroll
|
||||
|
||||
@file: module.py
|
||||
@file: moduleBase.py
|
||||
@date: 01.03.2019
|
||||
@author: Bastian Schroll
|
||||
@description: Module main class to inherit
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
from abc import ABC
|
||||
|
||||
from boswatch import wildcard
|
||||
|
||||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class Module:
|
||||
class ModuleBase(ABC):
|
||||
"""!Main module class"""
|
||||
|
||||
_modulesActive = 0
|
||||
_modulesActive = []
|
||||
|
||||
def __init__(self, moduleName, config):
|
||||
"""!init preload some needed locals and then call onLoad() directly"""
|
||||
self._moduleName = moduleName
|
||||
self.config = config
|
||||
self._modulesActive += 1
|
||||
self._modulesActive.append(self)
|
||||
|
||||
# for time counting
|
||||
self._cumTime = 0
|
||||
self._moduleTime = 0
|
||||
self._tmpTime = 0
|
||||
|
||||
# for statistics
|
||||
self._runCount = 0
|
||||
|
|
@ -43,28 +45,28 @@ class Module:
|
|||
logging.debug("[%s] onLoad()", moduleName)
|
||||
self.onLoad()
|
||||
|
||||
def __del__(self):
|
||||
"""!Destructor calls onUnload() directly"""
|
||||
def _cleanup(self):
|
||||
"""!Cleanup routine calls onUnload() directly"""
|
||||
logging.debug("[%s] onUnload()", self._moduleName)
|
||||
self._modulesActive -= 1
|
||||
self._modulesActive.remove(self)
|
||||
self.onUnload()
|
||||
|
||||
def _run(self, bwPacket):
|
||||
"""!start an rund of the module.
|
||||
"""!start an run of the module.
|
||||
|
||||
@param bwPacket: A BOSWatch packet instance
|
||||
@return bwPacket or False"""
|
||||
self._runCount += 1
|
||||
logging.debug("[%s] run #%d", self._moduleName, self._runCount)
|
||||
|
||||
self._tmpTime = time.time()
|
||||
tmpTime = time.time()
|
||||
try:
|
||||
logging.debug("[%s] doWork()", self._moduleName)
|
||||
bwPacket = self.doWork(bwPacket)
|
||||
except:
|
||||
self._moduleErrorCount += 1
|
||||
logging.exception("[%s] alarm error", self._moduleName)
|
||||
self._moduleTime = time.time() - self._tmpTime
|
||||
self._moduleTime = time.time() - tmpTime
|
||||
|
||||
self._cumTime += self._moduleTime
|
||||
|
||||
|
|
@ -76,7 +78,8 @@ class Module:
|
|||
"""!Returns statistical information's from last module run
|
||||
|
||||
@return Statistics as pyton dict"""
|
||||
stats = {"runCount": self._runCount,
|
||||
stats = {"type": "module",
|
||||
"runCount": self._runCount,
|
||||
"cumTime": self._cumTime,
|
||||
"moduleTime": self._moduleTime,
|
||||
"moduleErrorCount": self._moduleErrorCount}
|
||||
|
|
@ -84,17 +87,31 @@ class Module:
|
|||
|
||||
def onLoad(self):
|
||||
"""!Called by import of the module
|
||||
Must be inherit"""
|
||||
can be inherited"""
|
||||
pass
|
||||
|
||||
def doWork(self, bwPacket):
|
||||
"""!Called module run
|
||||
Must be inherit
|
||||
can be inherited
|
||||
|
||||
@param bwPacket: bwPacket instance"""
|
||||
logging.warning("no functionality in module %s", self._moduleName)
|
||||
|
||||
def onUnload(self):
|
||||
"""!Called by destruction of the module
|
||||
Must be inherit"""
|
||||
"""!Called on shutdown of boswatch
|
||||
can be inherited"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def registerWildcard(newWildcard, bwPacketField):
|
||||
"""!Register a new wildcard
|
||||
|
||||
@param newWildcard: wildcard where parser searching for
|
||||
@param bwPacketField: field from bwPacket where holds replacement data"""
|
||||
if not newWildcard.startswith("{") or not newWildcard.endswith("}"):
|
||||
logging.error("wildcard not registered - false format: %s", newWildcard)
|
||||
return
|
||||
if bwPacketField == "":
|
||||
logging.error("wildcard not registered - bwPacket field is empty")
|
||||
return
|
||||
wildcard.registerWildcard(newWildcard, bwPacketField)
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
@description: Template Module File
|
||||
"""
|
||||
import logging
|
||||
from module.module import Module
|
||||
from module.moduleBase import ModuleBase
|
||||
|
||||
# ###################### #
|
||||
# Custom plugin includes #
|
||||
|
|
@ -25,21 +25,21 @@ from module.module import Module
|
|||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class BoswatchModule(Module):
|
||||
class BoswatchModul(ModuleBase):
|
||||
"""!Description of the Module"""
|
||||
def __init__(self, config):
|
||||
"""!Do not change anything here!"""
|
||||
super().__init__(__name__, config) # you can access the config class on 'self.config'
|
||||
|
||||
def onLoad(self):
|
||||
"""!Called by import of the plugin"""
|
||||
"""!Called by import of the plugin
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
||||
def doWork(self, bwPacket):
|
||||
"""!start an run of the module.
|
||||
|
||||
@param bwPacket: A BOSWatch packet instance
|
||||
@return bwPacket or False"""
|
||||
@param bwPacket: A BOSWatch packet instance"""
|
||||
if bwPacket.get("mode") == "fms":
|
||||
pass
|
||||
elif bwPacket.get("mode") == "zvei":
|
||||
|
|
@ -52,5 +52,6 @@ class BoswatchModule(Module):
|
|||
return bwPacket
|
||||
|
||||
def onUnload(self):
|
||||
"""!Called by destruction of the plugin"""
|
||||
"""!Called by destruction of the plugin
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -9,29 +9,30 @@
|
|||
German BOS Information Script
|
||||
by Bastian Schroll
|
||||
|
||||
@file: plugin.py
|
||||
@file: pluginBase.py
|
||||
@date: 08.01.2018
|
||||
@author: Bastian Schroll
|
||||
@description: Plugin main class to inherit
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
from abc import ABC
|
||||
|
||||
from boswatch import wildcard
|
||||
|
||||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class Plugin:
|
||||
class PluginBase(ABC):
|
||||
"""!Main plugin class"""
|
||||
|
||||
_pluginsActive = 0
|
||||
_pluginsActive = []
|
||||
|
||||
def __init__(self, pluginName, config):
|
||||
"""!init preload some needed locals and then call onLoad() directly"""
|
||||
self._pluginName = pluginName
|
||||
self.config = config
|
||||
self._pluginsActive += 1
|
||||
self._pluginsActive.append(self)
|
||||
|
||||
# to save the packet while alarm is running for other functions
|
||||
self._bwPacket = None
|
||||
|
|
@ -42,7 +43,6 @@ class Plugin:
|
|||
self._setupTime = 0
|
||||
self._alarmTime = 0
|
||||
self._teardownTime = 0
|
||||
self._tmpTime = 0
|
||||
|
||||
# for statistics
|
||||
self._runCount = 0
|
||||
|
|
@ -53,10 +53,10 @@ class Plugin:
|
|||
logging.debug("[%s] onLoad()", pluginName)
|
||||
self.onLoad()
|
||||
|
||||
def __del__(self):
|
||||
"""!Destructor calls onUnload() directly"""
|
||||
def _cleanup(self):
|
||||
"""!Cleanup routine calls onUnload() directly"""
|
||||
logging.debug("[%s] onUnload()", self._pluginName)
|
||||
self._pluginsActive -= 1
|
||||
self._pluginsActive.remove(self)
|
||||
self.onUnload()
|
||||
|
||||
def _run(self, bwPacket):
|
||||
|
|
@ -70,7 +70,7 @@ class Plugin:
|
|||
|
||||
self._bwPacket = bwPacket
|
||||
|
||||
self._tmpTime = time.time()
|
||||
tmpTime = time.time()
|
||||
try:
|
||||
logging.debug("[%s] setup()", self._pluginName)
|
||||
self.setup()
|
||||
|
|
@ -78,8 +78,8 @@ class Plugin:
|
|||
self._setupErrorCount += 1
|
||||
logging.exception("[%s] error in setup()", self._pluginName)
|
||||
|
||||
self._setupTime = time.time() - self._tmpTime
|
||||
self._tmpTime = time.time()
|
||||
self._setupTime = time.time() - tmpTime
|
||||
tmpTime = time.time()
|
||||
try:
|
||||
|
||||
if bwPacket.get("mode") == "fms":
|
||||
|
|
@ -98,8 +98,8 @@ class Plugin:
|
|||
self._alarmErrorCount += 1
|
||||
logging.exception("[%s] alarm error", self._pluginName)
|
||||
|
||||
self._alarmTime = time.time() - self._tmpTime
|
||||
self._tmpTime = time.time()
|
||||
self._alarmTime = time.time() - tmpTime
|
||||
tmpTime = time.time()
|
||||
try:
|
||||
logging.debug("[%s] teardown()", self._pluginName)
|
||||
self.teardown()
|
||||
|
|
@ -107,7 +107,7 @@ class Plugin:
|
|||
self._teardownErrorCount += 1
|
||||
logging.exception("[%s] error in teardown()", self._pluginName)
|
||||
|
||||
self._teardownTime = time.time() - self._tmpTime
|
||||
self._teardownTime = time.time() - tmpTime
|
||||
self._sumTime = self._setupTime + self._alarmTime + self._teardownTime
|
||||
self._cumTime += self._sumTime
|
||||
|
||||
|
|
@ -124,7 +124,8 @@ class Plugin:
|
|||
"""!Returns statistical information's from last plugin run
|
||||
|
||||
@return Statistics as pyton dict"""
|
||||
stats = {"runCount": self._runCount,
|
||||
stats = {"type": "plugin",
|
||||
"runCount": self._runCount,
|
||||
"sumTime": self._sumTime,
|
||||
"cumTime": self._cumTime,
|
||||
"setupTime": self._setupTime,
|
||||
|
|
@ -137,50 +138,50 @@ class Plugin:
|
|||
|
||||
def onLoad(self):
|
||||
"""!Called by import of the plugin
|
||||
Must be inherit"""
|
||||
can be inherited"""
|
||||
pass
|
||||
|
||||
def setup(self):
|
||||
"""!Called before alarm
|
||||
Must be inherit"""
|
||||
can be inherited"""
|
||||
pass
|
||||
|
||||
def fms(self, bwPacket):
|
||||
"""!Called on FMS alarm
|
||||
Must be inherit
|
||||
can be inherited
|
||||
|
||||
@param bwPacket: bwPacket instance"""
|
||||
logging.warning("ZVEI not implemented in %s", self._pluginName)
|
||||
|
||||
def pocsag(self, bwPacket):
|
||||
"""!Called on POCSAG alarm
|
||||
Must be inherit
|
||||
can be inherited
|
||||
|
||||
@param bwPacket: bwPacket instance"""
|
||||
logging.warning("POCSAG not implemented in %s", self._pluginName)
|
||||
|
||||
def zvei(self, bwPacket):
|
||||
"""!Called on ZVEI alarm
|
||||
Must be inherit
|
||||
can be inherited
|
||||
|
||||
@param bwPacket: bwPacket instance"""
|
||||
logging.warning("ZVEI not implemented in %s", self._pluginName)
|
||||
|
||||
def msg(self, bwPacket):
|
||||
"""!Called on MSG packet
|
||||
Must be inherit
|
||||
can be inherited
|
||||
|
||||
@param bwPacket: bwPacket instance"""
|
||||
logging.warning("MSG not implemented in %s", self._pluginName)
|
||||
|
||||
def teardown(self):
|
||||
"""!Called after alarm
|
||||
Must be inherit"""
|
||||
can be inherited"""
|
||||
pass
|
||||
|
||||
def onUnload(self):
|
||||
"""!Called by destruction of the plugin
|
||||
Must be inherit"""
|
||||
"""!Called on shutdown of boswatch
|
||||
can be inherited"""
|
||||
pass
|
||||
|
||||
def parseWildcards(self, msg):
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
@description: Template Plugin File
|
||||
"""
|
||||
import logging
|
||||
from plugin.plugin import Plugin
|
||||
from plugin.pluginBase import PluginBase
|
||||
|
||||
# ###################### #
|
||||
# Custom plugin includes #
|
||||
|
|
@ -25,46 +25,56 @@ from plugin.plugin import Plugin
|
|||
logging.debug("- %s loaded", __name__)
|
||||
|
||||
|
||||
class BoswatchPlugin(Plugin):
|
||||
class BoswatchPlugin(PluginBase):
|
||||
"""!Description of the Plugin"""
|
||||
def __init__(self, config):
|
||||
"""!Do not change anything here!"""
|
||||
super().__init__(__name__, config) # you can access the config class on 'self.config'
|
||||
|
||||
def onLoad(self):
|
||||
"""!Called by import of the plugin"""
|
||||
"""!Called by import of the plugin
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
||||
def setup(self):
|
||||
"""!Called before alarm"""
|
||||
"""!Called before alarm
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
||||
def fms(self, bwPacket):
|
||||
"""!Called on FMS alarm
|
||||
|
||||
@param bwPacket: bwPacket instance"""
|
||||
@param bwPacket: bwPacket instance
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
||||
def pocsag(self, bwPacket):
|
||||
"""!Called on POCSAG alarm
|
||||
|
||||
@param bwPacket: bwPacket instance"""
|
||||
@param bwPacket: bwPacket instance
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
||||
def zvei(self, bwPacket):
|
||||
"""!Called on ZVEI alarm
|
||||
|
||||
@param bwPacket: bwPacket instance"""
|
||||
@param bwPacket: bwPacket instance
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
||||
def msg(self, bwPacket):
|
||||
"""!Called on MSG packet
|
||||
|
||||
@param bwPacket: bwPacket instance"""
|
||||
@param bwPacket: bwPacket instance
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
||||
def teardown(self):
|
||||
"""!Called after alarm"""
|
||||
"""!Called after alarm
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
||||
def onUnload(self):
|
||||
"""!Called by destruction of the plugin"""
|
||||
"""!Called by destruction of the plugin
|
||||
Remove if not implemented"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -10,4 +10,5 @@ mkdocs
|
|||
pytest
|
||||
pytest-cov
|
||||
pytest-pep8
|
||||
pytest-flakes
|
||||
pytest-randomly
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
python -m pytest -c "_gen/pytest.ini"
|
||||
_bin\win\doxygen\doxygen.exe _gen/doxygen.ini
|
||||
_bin\win\cloc_1_72\cloc-1.72.exe . --exclude-lang=XML --exclude-dir=_docu,_config,_info,doxygen.ini --by-file-by-lang
|
||||
|
|
@ -23,10 +23,11 @@ import pytest
|
|||
|
||||
from boswatch.network.server import TCPServer
|
||||
from boswatch.network.client import TCPClient
|
||||
import threading
|
||||
|
||||
|
||||
def setup_method(method):
|
||||
logging.debug("[TEST] %s.%s", method.__module__, method.__name__)
|
||||
def setup_function(function):
|
||||
logging.debug("[TEST] %s.%s", function.__module__, function.__name__)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -137,7 +138,7 @@ def test_clientMultiCommunicate(getServer):
|
|||
assert testClient2.receive() == "[ack]"
|
||||
assert testClient1.receive() == "[ack]"
|
||||
# check server msg queue
|
||||
assert dataQueue.qsize() == 3
|
||||
assert getRunningServer._alarmQueue.qsize() == 3
|
||||
# disconnect all
|
||||
assert testClient1.disconnect()
|
||||
assert testClient2.disconnect()
|
||||
|
|
@ -181,11 +182,11 @@ def test_serverStopsWhileConnected(getRunningServer, getClient):
|
|||
"""!Shutdown server while client is connected"""
|
||||
getClient.connect()
|
||||
getRunningServer.stop()
|
||||
timeout = 10
|
||||
timeout = 5
|
||||
while getClient.isConnected:
|
||||
time.sleep(0.1)
|
||||
timeout = timeout - 1
|
||||
if timeout is 0:
|
||||
if timeout == 0:
|
||||
break
|
||||
assert timeout
|
||||
|
||||
|
|
@ -206,10 +207,41 @@ def test_serverGetOutput(getRunningServer):
|
|||
assert testClient1.receive() == "[ack]"
|
||||
assert testClient2.receive() == "[ack]"
|
||||
# _check server output data
|
||||
assert dataQueue.qsize() == 2
|
||||
assert dataQueue.get(True, 1)[1] == "test1"
|
||||
assert dataQueue.get(True, 1)[1] == "test2"
|
||||
assert dataQueue.qsize() is 0 # Last _check must be None
|
||||
assert getRunningServer._alarmQueue.qsize() == 2
|
||||
assert getRunningServer._alarmQueue.get(True, 1)[1] == "test1"
|
||||
assert getRunningServer._alarmQueue.get(True, 1)[1] == "test2"
|
||||
assert getRunningServer._alarmQueue.qsize() == 0 # Last _check must be None
|
||||
# disconnect all
|
||||
assert testClient1.disconnect()
|
||||
assert testClient2.disconnect()
|
||||
|
||||
|
||||
def test_serverHighLoad(getRunningServer):
|
||||
"""!High load server test with 10 send threads each will send 100 msg with 324 bytes size"""
|
||||
logging.debug("start sendThreads")
|
||||
threads = []
|
||||
for thr_id in range(10):
|
||||
thr = threading.Thread(target=sendThread, name="sendThread-" + str(thr_id))
|
||||
thr.daemon = True
|
||||
thr.start()
|
||||
threads.append(thr)
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
logging.debug("finished sendThreads")
|
||||
assert getRunningServer._alarmQueue.qsize() == 1000
|
||||
|
||||
|
||||
def sendThread():
|
||||
client = TCPClient()
|
||||
client.connect()
|
||||
time.sleep(0.1)
|
||||
for i in range(100):
|
||||
# actually this string is 324 bytes long
|
||||
client.transmit("HigLoadTestString-HigLoadTestString-HigLoadTestString-HigLoadTestString-HigLoadTestString-HigLoadTestString-"
|
||||
"HigLoadTestString-HigLoadTestString-HigLoadTestString-HigLoadTestString-HigLoadTestString-HigLoadTestString-"
|
||||
"HigLoadTestString-HigLoadTestString-HigLoadTestString-HigLoadTestString-HigLoadTestString-HigLoadTestString-")
|
||||
if not client.receive() == "[ack]":
|
||||
logging.error("missing [ACK]")
|
||||
|
||||
time.sleep(0.1)
|
||||
client.disconnect()
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ from boswatch.network.broadcast import BroadcastServer
|
|||
from boswatch.network.broadcast import BroadcastClient
|
||||
|
||||
|
||||
def setup_method(method):
|
||||
logging.debug("[TEST] %s.%s", method.__module__, method.__name__)
|
||||
def setup_function(function):
|
||||
logging.debug("[TEST] %s.%s", function.__module__, function.__name__)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ from boswatch.utils import paths
|
|||
from boswatch.configYaml import ConfigYAML
|
||||
|
||||
|
||||
def setup_method(method):
|
||||
logging.debug("[TEST] %s.%s", method.__module__, method.__name__)
|
||||
def setup_function(function):
|
||||
logging.debug("[TEST] %s.%s", function.__module__, function.__name__)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -89,7 +89,7 @@ def test_configIterationList(getFilledConfig):
|
|||
for item in getFilledConfig.get("list"):
|
||||
assert type(item) is str
|
||||
counter += 1
|
||||
assert counter is 3
|
||||
assert counter == 3
|
||||
|
||||
|
||||
def test_configIterationListWithNestedList(getFilledConfig):
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import logging
|
|||
from boswatch.decoder.decoder import Decoder
|
||||
|
||||
|
||||
def setup_method(method):
|
||||
logging.debug("[TEST] %s.%s", method.__module__, method.__name__)
|
||||
def setup_function(function):
|
||||
logging.debug("[TEST] %s.%s", function.__module__, function.__name__)
|
||||
|
||||
|
||||
def test_decoderNoData():
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ import logging
|
|||
from boswatch.utils import header
|
||||
|
||||
|
||||
def setup_method(method):
|
||||
logging.debug("[TEST] %s.%s", method.__module__, method.__name__)
|
||||
def setup_function(function):
|
||||
logging.debug("[TEST] %s.%s", function.__module__, function.__name__)
|
||||
|
||||
|
||||
def test_logoToLog():
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import pytest
|
|||
from boswatch.packet import Packet
|
||||
|
||||
|
||||
def setup_method(method):
|
||||
logging.debug("[TEST] %s.%s", method.__module__, method.__name__)
|
||||
def setup_function(function):
|
||||
logging.debug("[TEST] %s.%s", function.__module__, function.__name__)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
@ -34,19 +34,19 @@ def buildPacket():
|
|||
|
||||
def test_createPacket(buildPacket):
|
||||
"""!Create a packet"""
|
||||
assert buildPacket is not ""
|
||||
assert buildPacket != ""
|
||||
|
||||
|
||||
def test_copyPacket(buildPacket):
|
||||
"""!Copy a packet to an new instance"""
|
||||
bwCopyPacket = Packet(buildPacket.__str__())
|
||||
assert bwCopyPacket is not ""
|
||||
assert bwCopyPacket != ""
|
||||
|
||||
|
||||
def test_getPacketString(buildPacket):
|
||||
"""!get the intern packet dict as string"""
|
||||
assert type(buildPacket.__str__()) is str
|
||||
assert buildPacket.__str__() is not ""
|
||||
assert buildPacket.__str__() != ""
|
||||
|
||||
|
||||
def test_getNotSetField(buildPacket):
|
||||
|
|
@ -57,4 +57,4 @@ def test_getNotSetField(buildPacket):
|
|||
def test_setGetField(buildPacket):
|
||||
"""!set and get a field"""
|
||||
buildPacket.set("testField", "test")
|
||||
assert buildPacket.get("testField") is "test"
|
||||
assert buildPacket.get("testField") == "test"
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import os
|
|||
from boswatch.utils import paths
|
||||
|
||||
|
||||
def setup_method(method):
|
||||
logging.debug("[TEST] %s.%s", method.__module__, method.__name__)
|
||||
def setup_function(function):
|
||||
logging.debug("[TEST] %s.%s", function.__module__, function.__name__)
|
||||
|
||||
|
||||
def test_fileExists():
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import pytest
|
|||
from boswatch.timer import RepeatedTimer
|
||||
|
||||
|
||||
def setup_method(method):
|
||||
logging.debug("[TEST] %s.%s", method.__module__, method.__name__)
|
||||
def setup_function(function):
|
||||
logging.debug("[TEST] %s.%s", function.__module__, function.__name__)
|
||||
|
||||
|
||||
def testTargetFast():
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
# by Bastian Schroll
|
||||
|
||||
[pytest]
|
||||
addopts = -v --pep8 --cov=boswatch/ --cov-report=term-missing --log-level=CRITICAL
|
||||
addopts = -v --pep8 --flakes --cov=boswatch/ --cov-report=term-missing --log-level=CRITICAL
|
||||
|
||||
# classic or progress
|
||||
console_output_style = progress
|
||||
|
|
|
|||
|
|
@ -90,16 +90,16 @@ POCSAG1200: Address: 9000000 Function: 1 Alpha: BOSWatch-Test: out of filter
|
|||
POCSAG1200: Address: 0871004 Function: 1 Alpha: Dies ist ein Probealarm!
|
||||
## Multicast Alarm
|
||||
POCSAG1200: Address: 0871002 Function: 0 Alpha: <EOT><FF>
|
||||
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: 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<NUL>
|
||||
|
||||
# regEx-Filter?
|
||||
|
|
|
|||
Loading…
Reference in a new issue