diff --git a/pyproject.toml b/pyproject.toml index 96a5153..4846adc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ [project.scripts] solarman-decoder = "utils.solarman_decoder:main" solarman-discovery = "utils.solarman_discovery:main" -solarman-discovery = "utils.solarman_discovery_reply:main" +solarman-discovery-reply = "utils.solarman_discovery_reply:main" solarman-rtu-proxy = "utils.solarman_rtu_proxy:main" solarman-scan = "utils.solarman_scan:main" solarman-tcp-proxy = "utils.solarman_tcp_proxy:main" diff --git a/pysolarmanv5/pysolarmanv5.py b/pysolarmanv5/pysolarmanv5.py index 9b2b671..5725fad 100644 --- a/pysolarmanv5/pysolarmanv5.py +++ b/pysolarmanv5/pysolarmanv5.py @@ -19,7 +19,6 @@ from umodbus.client.serial.redundancy_check import get_crc from umodbus.exceptions import error_code_to_exception_map - _WIN_PLATFORM = platform.system() == "Windows" diff --git a/tests/setup_test.py b/tests/setup_test.py index 487aac1..d600cae 100644 --- a/tests/setup_test.py +++ b/tests/setup_test.py @@ -17,7 +17,6 @@ from pysolarmanv5.pysolarmanv5 import CONTROL_CODE, PySolarmanV5, V5FrameError - _WIN_PLATFORM = True if platform.system() == "Windows" else False socketserver.TCPServer.allow_reuse_address = True socketserver.TCPServer.allow_reuse_port = True diff --git a/utils/solarman_tcp_proxy.py b/utils/solarman_tcp_proxy.py index 84fb9d6..e46ab7d 100644 --- a/utils/solarman_tcp_proxy.py +++ b/utils/solarman_tcp_proxy.py @@ -16,10 +16,17 @@ import argparse import asyncio import struct +import logging from functools import partial from umodbus.client.serial.redundancy_check import get_crc from pysolarmanv5 import PySolarmanV5Async +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", +) +log = logging.getLogger("solarman") + async def handle_client( reader: asyncio.StreamReader, @@ -28,49 +35,53 @@ async def handle_client( logger_serial: int, ): solarmanv5 = PySolarmanV5Async( - logger_address, logger_serial, verbose=True, auto_reconnect=True + address=logger_address, + serial=logger_serial, + verbose=True, + auto_reconnect=True, + logger=log, ) await solarmanv5.connect() addr = writer.get_extra_info("peername") - print(f"{addr}: New connection") + log.info(f"{addr}: New connection") try: while True: - # Convert TCP to RTU - header = await reader.readexactly(6) - if not header: + try: + # read Modbus TCP frame + header = await reader.readexactly(6) + # decode header + trans_id, proto_id, length = struct.unpack(">HHH", header) + unit_id = await reader.readexactly(1) + pdu = await reader.readexactly(length - 1) # length includes unit_id + except asyncio.IncompleteReadError: + # connection closed break - trans_id, proto_id, length = struct.unpack(">HHH", header) - unit_id = await reader.readexactly(1) - pdu = await reader.readexactly(length - 1) # length includes unit_id - + # create equivalent RTU frame slave_id = b"\x01" modbus_rtu = slave_id + pdu + get_crc(slave_id + pdu) - try: - # Convert RTU back to TCP - reply_rtu = await solarmanv5.send_raw_modbus_frame(modbus_rtu) + reply_rtu = await solarmanv5.send_raw_modbus_frame(modbus_rtu) - slave_id_reply = reply_rtu[0:1] - pdu_reply = reply_rtu[1:-2] - crc_reply = reply_rtu[-2:] + # slave_id_reply = reply_rtu[0:1] + pdu_reply = reply_rtu[1:-2] + # crc_reply = reply_rtu[-2:] - mbap = struct.pack(">HHH", trans_id, 0, len(pdu_reply) + 1) - reply_tcp = mbap + unit_id + pdu_reply + # Convert RTU back to TCP + mbap = struct.pack(">HHH", trans_id, 0, len(pdu_reply) + 1) + reply_tcp = mbap + unit_id + pdu_reply - writer.write(reply_tcp) - except: - pass + writer.write(reply_tcp) await writer.drain() except OSError: # https://github.com/python/cpython/issues/83037 pass - print(f"{addr}: Connection closed") + log.info(f"{addr}: Connection closed") await solarmanv5.disconnect() @@ -85,14 +96,14 @@ async def run_proxy( port, ) async with server: - print(f"Listening on {bind_address}:{port}") + log.info(f"Listening on {bind_address}:{port}") await server.serve_forever() def main(): parser = argparse.ArgumentParser( - prog="solarman rtu proxy", - description="A Modbus RTU over TCP Proxy for Solarman loggers", + prog="solarman-tcp-proxy", + description="A Modbus TCP Proxy for Solarman loggers", ) parser.add_argument( "-b", "--bind", default="0.0.0.0", help="The address to listen on"