mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-04 13:17:50 +02:00
Net-to-serial broadcast messages to all clients and other cleanups (#20)
* Cleanup and maintenance * Net-to-serial broadcast messages to all clients This will make all clients to stay in sync with any operation occurring, like when having multiple JMRI instances * Update README and python version in containers
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
FROM python:3.10-alpine
|
||||
FROM python:3.11-alpine
|
||||
|
||||
RUN mkdir /opt/dcc && pip -q install pyserial
|
||||
ADD net-to-serial.py config.ini /opt/dcc
|
||||
|
3
daemons/README.md
Normal file
3
daemons/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## DCC++ EX connector
|
||||
|
||||
See [README.md](../README.md)
|
@@ -2,6 +2,7 @@
|
||||
LogLevel = debug
|
||||
ListeningIP = 0.0.0.0
|
||||
ListeningPort = 2560
|
||||
MaxClients = 10
|
||||
|
||||
[Serial]
|
||||
# UNO
|
||||
|
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
import time
|
||||
import logging
|
||||
import serial
|
||||
import asyncio
|
||||
@@ -10,11 +9,15 @@ from pathlib import Path
|
||||
|
||||
|
||||
class SerialDaemon:
|
||||
connected_clients = set()
|
||||
|
||||
def __init__(self, config):
|
||||
self.ser = serial.Serial(
|
||||
config["Serial"]["Port"],
|
||||
timeout=int(config["Serial"]["Timeout"])/1000)
|
||||
timeout=int(config["Serial"]["Timeout"]) / 1000,
|
||||
)
|
||||
self.ser.baudrate = config["Serial"]["Baudrate"]
|
||||
self.max_clients = int(config["Daemon"]["MaxClients"])
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
@@ -43,19 +46,32 @@ class SerialDaemon:
|
||||
|
||||
async def handle_echo(self, reader, writer):
|
||||
"""Process a request from socket and return the response"""
|
||||
while 1: # keep connection to client open
|
||||
data = await reader.read(100)
|
||||
if not data: # client has disconnected
|
||||
break
|
||||
logging.info(
|
||||
"Clients already connected: {} (max: {})".format(
|
||||
len(self.connected_clients),
|
||||
self.max_clients,
|
||||
)
|
||||
)
|
||||
|
||||
addr = writer.get_extra_info('peername')
|
||||
logging.info("Received {} from {}".format(data, addr[0]))
|
||||
|
||||
self.__write_serial(data)
|
||||
response = self.__read_serial()
|
||||
writer.write(response)
|
||||
await writer.drain()
|
||||
logging.info("Sent: {}".format(response))
|
||||
addr = writer.get_extra_info("peername")[0]
|
||||
if len(self.connected_clients) < self.max_clients:
|
||||
self.connected_clients.add(writer)
|
||||
while True: # keep connection to client open
|
||||
data = await reader.read(100)
|
||||
if not data: # client has disconnected
|
||||
break
|
||||
logging.info("Received {} from {}".format(data, addr))
|
||||
self.__write_serial(data)
|
||||
response = self.__read_serial()
|
||||
for client in self.connected_clients:
|
||||
client.write(response)
|
||||
await client.drain()
|
||||
logging.info("Sent: {}".format(response))
|
||||
self.connected_clients.remove(writer)
|
||||
else:
|
||||
logging.warning(
|
||||
"TooManyClients: client {} disconnected".format(addr)
|
||||
)
|
||||
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
@@ -68,33 +84,37 @@ class SerialDaemon:
|
||||
while "DCC-EX" not in line:
|
||||
line = self.__read_serial().decode()
|
||||
board = re.findall(r"<iDCC-EX.*>", line)[0]
|
||||
return(board)
|
||||
return board
|
||||
|
||||
|
||||
async def main():
|
||||
config = configparser.ConfigParser()
|
||||
config.read(
|
||||
Path(__file__).resolve().parent / "config.ini") # mimick os.path.join
|
||||
Path(__file__).resolve().parent / "config.ini"
|
||||
) # mimick os.path.join
|
||||
logging.basicConfig(level=config["Daemon"]["LogLevel"].upper())
|
||||
|
||||
sd = SerialDaemon(config)
|
||||
server = await asyncio.start_server(
|
||||
sd.handle_echo,
|
||||
config["Daemon"]["ListeningIP"],
|
||||
config["Daemon"]["ListeningPort"])
|
||||
config["Daemon"]["ListeningPort"],
|
||||
)
|
||||
addr = server.sockets[0].getsockname()
|
||||
logging.warning("Serving on {} port {}".format(addr[0], addr[1]))
|
||||
logging.warning(
|
||||
logging.info("Serving on {} port {}".format(addr[0], addr[1]))
|
||||
logging.info(
|
||||
"Proxying to {} (Baudrate: {}, Timeout: {})".format(
|
||||
config["Serial"]["Port"],
|
||||
config["Serial"]["Baudrate"],
|
||||
config["Serial"]["Timeout"]))
|
||||
logging.warning("Initializing board")
|
||||
logging.warning("Board {} ready".format(
|
||||
await sd.return_board()))
|
||||
config["Serial"]["Timeout"],
|
||||
)
|
||||
)
|
||||
logging.info("Initializing board")
|
||||
logging.info("Board {} ready".format(await sd.return_board()))
|
||||
|
||||
async with server:
|
||||
await server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
BIN
daemons/simulator/CommandStation-EX-uno-7311f2c.elf
Executable file
BIN
daemons/simulator/CommandStation-EX-uno-7311f2c.elf
Executable file
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user