From 87e337a69db353e591ab16ccc554bd717229ad60 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Thu, 30 Jun 2022 09:20:13 -0700 Subject: [PATCH] Add support for multiple serial devices --- barcode_server.yaml | 5 ++-- barcode_server/cli.py | 4 +-- barcode_server/config.py | 13 +++++++-- barcode_server/notifier/file.py | 44 +++++++++++++++++++++++++++++-- barcode_server/notifier/serial.py | 24 ++++++++++++----- barcode_server/stats.py | 1 + barcode_server/webserver.py | 13 +++++---- tests/websocket_notifier_test.py | 4 +-- 8 files changed, 85 insertions(+), 23 deletions(-) diff --git a/barcode_server.yaml b/barcode_server.yaml index ea6c59c..3e85012 100644 --- a/barcode_server.yaml +++ b/barcode_server.yaml @@ -24,7 +24,8 @@ barcode_server: # (optional) Serial push configuration serial: # URL to send events to using a request - path: "/dev/ttyUSB0" + pathA: "/dev/ttyUSB0" + pathB: "/dev/ttyUSB1" # (optional) file push configuration file: @@ -62,7 +63,7 @@ barcode_server: # A list of regex patterns to match USB device names against devices: - - ".*Barcode.*" + - ".*ode.*" # A list of absolute file paths to devices device_paths: #- "/dev/input/barcode_scanner" diff --git a/barcode_server/cli.py b/barcode_server/cli.py index 3c7f2f4..8a7e6ee 100644 --- a/barcode_server/cli.py +++ b/barcode_server/cli.py @@ -50,7 +50,7 @@ def c_run(): """ from barcode_server.barcode import BarcodeReader from barcode_server.config import AppConfig - from barcode_server.webserver import Webserver + from barcode_server.webserver import WebServer signal.signal(signal.SIGINT, signal_handler) @@ -64,7 +64,7 @@ def c_run(): LOGGER.info(f"Instance ID: {config.INSTANCE_ID.value}") barcode_reader = BarcodeReader(config) - webserver = Webserver(config, barcode_reader) + webserver = WebServer(config, barcode_reader) # start prometheus server if config.STATS_PORT.value is not None: diff --git a/barcode_server/config.py b/barcode_server/config.py index 7afbbac..eed2770 100644 --- a/barcode_server/config.py +++ b/barcode_server/config.py @@ -213,11 +213,20 @@ class AppConfig(ConfigBase): required=True ) - SERIAL_PATH = StringConfigEntry( + SERIAL_PATH_A = StringConfigEntry( key_path=[ CONFIG_NODE_ROOT, CONFIG_NODE_SERIAL, - "path" + "pathA" + ], + required=False + ) + + SERIAL_PATH_B = StringConfigEntry( + key_path=[ + CONFIG_NODE_ROOT, + CONFIG_NODE_SERIAL, + "pathB" ], required=False ) diff --git a/barcode_server/notifier/file.py b/barcode_server/notifier/file.py index b23a058..d386240 100644 --- a/barcode_server/notifier/file.py +++ b/barcode_server/notifier/file.py @@ -1,11 +1,14 @@ +import os import logging -# import time as sleep +from pathlib import Path +from datetime import datetime from prometheus_async.aio import time from barcode_server.barcode import BarcodeEvent from barcode_server.notifier import BarcodeNotifier from barcode_server.stats import FILE_NOTIFIER_TIME +from barcode_server.util import barcode_event_to_json LOGGER = logging.getLogger(__name__) @@ -13,8 +16,45 @@ class FileNotifier(BarcodeNotifier): def __init__(self, path: str): super().__init__() + self.inputDevicePathA = None + self.inputDevicePathB = None self.path = path + pathObject = Path(path) + if not pathObject.exists(): + pathObject.mkdir(exist_ok=True) + + # def ticks(dt): + # return (dt - datetime(1, 1, 1)).total_seconds() * 10000000 @time(FILE_NOTIFIER_TIME) async def _send_event(self, event: BarcodeEvent): - LOGGER.debug(f"Notified {self.path}: {event.barcode}") + now = datetime.now() + date_time = now.strftime("%Y-%m-%d") + weekNumberOfYear = now.strftime("%Y_Week_%U") + if self.inputDevicePathA is None: + self.inputDevicePathA = event.input_device.path + elif self.inputDevicePathB is None: + self.inputDevicePathB = event.input_device.path + weekPath = f"{self.path}/{weekNumberOfYear}" + dateTimePath = f"{weekPath}/{date_time}" + if event.input_device.path == self.inputDevicePathA: + fullPath = f"{dateTimePath}/A" + elif event.input_device.path == self.inputDevicePathB: + fullPath = f"{dateTimePath}/B" + else: + fullPath = f"{dateTimePath}/Z" + json = barcode_event_to_json(self.config.INSTANCE_ID.value, event) + ticks = int((datetime.utcnow() - datetime(1, 1, 1)).total_seconds() * 10000000) + pathObject = Path(weekPath) + if not pathObject.exists(): + pathObject.mkdir(exist_ok=True) + pathObject = Path(dateTimePath) + if not pathObject.exists(): + pathObject.mkdir(exist_ok=True) + pathObject = Path(fullPath) + if not pathObject.exists(): + pathObject.mkdir(exist_ok=True) + f = open(f"{fullPath}/{str(ticks)}.json", 'wb') + f.write(json) + f.close() + LOGGER.debug(f"Notified {fullPath}: {event.barcode}") diff --git a/barcode_server/notifier/serial.py b/barcode_server/notifier/serial.py index b67fb13..b5f8d7d 100644 --- a/barcode_server/notifier/serial.py +++ b/barcode_server/notifier/serial.py @@ -1,6 +1,5 @@ import serial import logging -# import time as sleep from prometheus_async.aio import time @@ -12,12 +11,25 @@ LOGGER = logging.getLogger(__name__) class SerialNotifier(BarcodeNotifier): - def __init__(self, path: str, usb: serial): + def __init__(self, pathA: str, pathB: str): super().__init__() - self.path = path - self.usb = usb + self.pathA = pathA + self.pathB = pathB + self.inputDevicePathA = None + self.inputDevicePathB = None + self.usbA = serial.Serial(pathA, 9600, timeout=2) + if pathB is not None: + self.usbB = serial.Serial(pathB, 9600, timeout=2) @time(SERIAL_NOTIFIER_TIME) async def _send_event(self, event: BarcodeEvent): - self.usb.write(event.barcode.encode()) - LOGGER.debug(f"Notified {self.path}: {event.barcode}") + if self.inputDevicePathA is None: + self.inputDevicePathA = event.input_device.path + elif self.inputDevicePathB is None: + self.inputDevicePathB = event.input_device.path + if event.input_device.path == self.inputDevicePathA: + self.usbA.write(event.barcode.encode()) + LOGGER.debug(f"Notified {self.pathA}: {self.inputDevicePathA}: {event.barcode}") + elif event.input_device.path == self.inputDevicePathB: + self.usbB.write(event.barcode.encode()) + LOGGER.debug(f"Notified {self.pathB}: {self.inputDevicePathB}: {event.barcode}") diff --git a/barcode_server/stats.py b/barcode_server/stats.py index b9c2fb2..43a4e37 100644 --- a/barcode_server/stats.py +++ b/barcode_server/stats.py @@ -27,3 +27,4 @@ WEBSOCKET_NOTIFIER_TIME = NOTIFIER_TIME.labels(type='websocket') HTTP_NOTIFIER_TIME = NOTIFIER_TIME.labels(type='http') SERIAL_NOTIFIER_TIME = NOTIFIER_TIME.labels(type='serial') MQTT_NOTIFIER_TIME = NOTIFIER_TIME.labels(type='mqtt') +FILE_NOTIFIER_TIME = NOTIFIER_TIME.labels(type='file') diff --git a/barcode_server/webserver.py b/barcode_server/webserver.py index fe80ad0..970a72a 100644 --- a/barcode_server/webserver.py +++ b/barcode_server/webserver.py @@ -15,6 +15,7 @@ from barcode_server.notifier import BarcodeNotifier from barcode_server.notifier.http import HttpNotifier from barcode_server.notifier.mqtt import MQTTNotifier from barcode_server.notifier.serial import SerialNotifier +from barcode_server.notifier.file import FileNotifier from barcode_server.notifier.ws import WebsocketNotifier from barcode_server.stats import REST_TIME_DEVICES, WEBSOCKET_CLIENT_COUNT from barcode_server.util import input_device_to_dict @@ -23,7 +24,7 @@ LOGGER = logging.getLogger(__name__) routes = web.RouteTableDef() -class Webserver: +class WebServer: def __init__(self, config: AppConfig, barcode_reader: BarcodeReader): self.config = config @@ -35,9 +36,8 @@ class Webserver: self.barcode_reader = barcode_reader self.barcode_reader.add_listener(self.on_barcode) - self.usb = {} - self.notifiers: Dict[str, BarcodeNotifier] = {} + if config.HTTP_URL.value is not None: http_notifier = HttpNotifier( config.HTTP_METHOD.value, @@ -58,13 +58,12 @@ class Webserver: ) self.notifiers["mqtt"] = mqtt_notifier - if config.SERIAL_PATH.value is not None: - self.usb = serial.Serial(config.SERIAL_PATH.value, 9600, timeout=2) - serial_notifier = SerialNotifier(config.SERIAL_PATH.value, self.usb) + if config.SERIAL_PATH_A.value is not None: + serial_notifier = SerialNotifier(config.SERIAL_PATH_A.value, config.SERIAL_PATH_B.value) self.notifiers["serial"] = serial_notifier if config.FILE_PATH.value is not None: - file_notifier = SerialNotifier(config.FILE_PATH.value) + file_notifier = FileNotifier(config.FILE_PATH.value) self.notifiers["file"] = file_notifier async def start(self): diff --git a/tests/websocket_notifier_test.py b/tests/websocket_notifier_test.py index a614d9f..f9c54e3 100644 --- a/tests/websocket_notifier_test.py +++ b/tests/websocket_notifier_test.py @@ -8,7 +8,7 @@ from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop from barcode_server import const from barcode_server.barcode import BarcodeEvent from barcode_server.util import barcode_event_to_json -from barcode_server.webserver import Webserver +from barcode_server.webserver import WebServer def create_barcode_event_mock(barcode: str = None): @@ -46,7 +46,7 @@ class WebsocketNotifierTest(AioHTTPTestCase): Override the get_app method to return your application. """ barcode_reader = MagicMock() - self.webserver = Webserver(self.config, barcode_reader) + self.webserver = WebServer(self.config, barcode_reader) app = self.webserver.create_app() runner = aiohttp.web.AppRunner(app) await runner.setup()