From 27170ef12a3af6d1d84707f73160b9d7d063f8f8 Mon Sep 17 00:00:00 2001 From: Emanuele Trabattoni Date: Fri, 30 May 2025 19:00:07 +0200 Subject: [PATCH] upsmon refactoring --- .vscode/launch.json | 23 +++++++- routermon/routermon.py | 4 +- upsmon/ups.py | 116 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 127 insertions(+), 16 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 96b78a9..d57feba 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,13 +10,32 @@ "env": { "INTERVAL" : "5", "INFLUXDB_TOKEN":"rg5dZrBHQxJTH4Etq2jmDduggCC28QaWcua0VVvW4hjsEVhy_JUpVhcyg-aLAbM-TXv92pTB7IGJlyAPvi7Kvw==", - "INFLUXDB_URL" : "http://10.0.2.249:4567", + "INFLUXDB_URL" : "http://edelweiss-srv:4567", "INFLUXDB_ORG" : "edelweiss", "INFLUXDB_BUCKET" : "router", "MIKROTIK_IP": "10.128.0.1", "MIKROTIK_USER": "service", "MIKROTIK_PASSWORD": "edxservice", - "LOG_FILE": "D:\\Test\\routermon.log", + "LOG_FILE": "/tmp/routermon.log", + "LOG_FILE_LVL": "WARNING", + "LOG_CLI_LVL": "DEBUG" + } + }, + { + "name": "Python Debugger: UPSmon", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "env": { + "INTERVAL" : "5", + "INFLUXDB_TOKEN":"rg5dZrBHQxJTH4Etq2jmDduggCC28QaWcua0VVvW4hjsEVhy_JUpVhcyg-aLAbM-TXv92pTB7IGJlyAPvi7Kvw==", + "INFLUXDB_URL" : "http://edelweiss-srv:4567", + "INFLUXDB_ORG" : "edelweiss", + "INFLUXDB_BUCKET" : "ups", + "PORT": "/dev/ttyUSB1", + "BAUD": "2400", + "LOG_FILE": "/tmp/upsmon.log", "LOG_FILE_LVL": "WARNING", "LOG_CLI_LVL": "DEBUG" } diff --git a/routermon/routermon.py b/routermon/routermon.py index c306ffc..2335637 100644 --- a/routermon/routermon.py +++ b/routermon/routermon.py @@ -12,10 +12,10 @@ from influxdb_client.client.write_api import ASYNCHRONOUS, SYNCHRONOUS # Get environment variables env = dict(os.environ) -LOGGER = None +LOGGER: logging.Logger class Runner: - running: property = True + running: bool = True def __init__(self): signal.signal(signal.SIGINT, self.stop) signal.signal(signal.SIGTERM, self.stop) diff --git a/upsmon/ups.py b/upsmon/ups.py index 5cdc24d..8c1e1c7 100644 --- a/upsmon/ups.py +++ b/upsmon/ups.py @@ -1,16 +1,108 @@ +import os +import sys +import time import string import serial -import time +import logging +import signal +import json -port = serial.Serial(port='COM3', baudrate=2400, bytesize=8, parity='N', stopbits=1) +# Get environment variables +env = dict(os.environ) +LOGGER: logging.Logger + +class Runner: + running: bool = True + def __init__(self): + signal.signal(signal.SIGINT, self.stop) + signal.signal(signal.SIGTERM, self.stop) + + def stop(self, _): + self.running = False + +def send(port: serial.Serial, d: str): + port.write((d+'\r').encode('ascii')) + port.flush() + +def receive(port: serial.Serial, d: str) -> str: + r = port.read_until(b'\r').decode('ascii').rstrip() + LOGGER.debug(f"{d} : {r}") + return r + +def bruteforceCommands(port: serial.Serial): + # T and S cause unwanted shutdown + letters = string.ascii_uppercase.replace('T','').replace('S','') + LOGGER.debug(f"Test commands: {letters}") + for c in letters: + send(port, c) + receive(port, c) + for n in range(10): + d = c+f"{n:1d}" + send(port, d) + receive(port, d) + for n in range(100): + d = c+f"{n:02d}" + send(port, d) + receive(port, d) + +def main(): + INTERVAL = int(env['INTERVAL']) + run = Runner.running + port = serial.Serial(port=env['PORT'], baudrate=int(env['BAUD']), bytesize=8, parity='N', stopbits=1) + while run: + try: + send(port, "Q1") + data = receive(port, "Q1").lstrip('(').split() + if len(data) < 8: + LOGGER.error(f"Incomplete data: {data}") + continue + values = { + 'inV': float(data[0]), + 'outV': float(data[2]), + 'loadPercent': float(data[3]), + 'lineFreq': float(data[4]), + 'timeLeft': float(data[5]), + 'onBatt': True if str(data[7]).startswith('1') else False, + 'onLine': True if str(data[7]).endswith('1') else False, + } + LOGGER.debug(f"UPS Status: \n{json.dumps(values, indent=2)}") + time.sleep(INTERVAL) + except KeyboardInterrupt: + run = False + except Exception as e: + print(f"Unexpected exception: [{e}]") + return 1 + port.close() + + +if __name__ == "__main__": + # Logger Constants + LOG_FORMAT = '%(asctime)s| %(levelname)-7s|%(funcName)-10s|%(lineno)-3d: %(message)-50s' + + # Enabling Logger + LOGGER = logging.getLogger(__name__) + LOGGER.setLevel(logging.DEBUG) + LOGGER.propagate = False + formatter = logging.Formatter(LOG_FORMAT, None) + levels = logging.getLevelNamesMapping() + + # File logging + log_name = os.path.abspath(env['LOG_FILE']) + fh = logging.FileHandler(log_name) + fh.setLevel(levels[env['LOG_FILE_LVL']]) + fh.setFormatter(formatter) + LOGGER.addHandler(fh) + + # Console logging + cl = logging.StreamHandler(sys.stdout) + cl.setLevel(levels[env['LOG_CLI_LVL']]) + cl.setFormatter(formatter) + LOGGER.addHandler(cl) + + LOGGER.warning(f"UPSmon started on: {time.asctime()}") + + while main(): + LOGGER.error("Main thread exited unexpectedly") + time.sleep(15) + sys.exit(0) -for c in string.ascii_uppercase: - for n in range(100): - d = c+f"{n:02d}" - port.write((d+chr(13)).encode()) - port.flush() - r = port.read_all().decode('ascii').rstrip() - print(f"{d} : {r}", sep='^H') - if 'NAK' not in r: - print() - time.sleep(0.1) \ No newline at end of file