Dane Sabo 3299181c70 Auto sync: 2025-09-02 22:47:53 (10335 files changed)
M  lazy-lock.json

M  lua/custom/configs/lspconfig.lua

M  lua/custom/init.lua

A  lua/custom/journal.lua

A  nvim_venv/bin/Activate.ps1

A  nvim_venv/bin/activate

A  nvim_venv/bin/activate.csh

A  nvim_venv/bin/activate.fish
2025-09-02 22:47:53 -04:00

138 lines
5.0 KiB
Python

"""Implementation of a Threaded Modbus Server."""
from __future__ import annotations
import asyncio
import traceback
from pymodbus.exceptions import ModbusIOException, NoSuchSlaveException
from pymodbus.logging import Log
from pymodbus.pdu.pdu import ExceptionResponse
from pymodbus.transaction import TransactionManager
from pymodbus.transport import CommParams, ModbusProtocol
class ServerRequestHandler(TransactionManager):
"""Handle client connection."""
def __init__(self, owner, trace_packet, trace_pdu, trace_connect):
"""Initialize."""
params = CommParams(
comm_name="server",
comm_type=owner.comm_params.comm_type,
reconnect_delay=0.0,
reconnect_delay_max=0.0,
timeout_connect=0.0,
host=owner.comm_params.source_address[0],
port=owner.comm_params.source_address[1],
handle_local_echo=owner.comm_params.handle_local_echo,
)
self.server = owner
self.framer = self.server.framer(self.server.decoder)
self.running = False
super().__init__(
params,
self.framer,
0,
True,
trace_packet,
trace_pdu,
trace_connect,
)
def callback_new_connection(self) -> ModbusProtocol:
"""Call when listener receive new connection request."""
raise RuntimeError("callback_new_connection should never be called")
def callback_connected(self) -> None:
"""Call when connection is succcesfull."""
super().callback_connected()
slaves = self.server.context.slaves()
if self.server.broadcast_enable:
if 0 not in slaves:
slaves.append(0)
def callback_disconnected(self, call_exc: Exception | None) -> None:
"""Call when connection is lost."""
super().callback_disconnected(call_exc)
try:
if call_exc is None:
Log.debug(
"Handler for stream [{}] has been canceled", self.comm_params.comm_name
)
else:
Log.debug(
"Client Disconnection {} due to {}",
self.comm_params.comm_name,
call_exc,
)
self.running = False
except Exception as exc: # pylint: disable=broad-except
Log.error(
"Datastore unable to fulfill request: {}; {}",
exc,
traceback.format_exc(),
)
def callback_data(self, data: bytes, addr: tuple | None = None) -> int:
"""Handle received data."""
try:
used_len = super().callback_data(data, addr)
except ModbusIOException:
response = ExceptionResponse(
40,
exception_code=ExceptionResponse.ILLEGAL_FUNCTION
)
self.server_send(response, 0)
return(len(data))
if self.last_pdu:
if self.is_server:
self.loop.call_soon(self.handle_later)
else:
self.response_future.set_result(True)
return used_len
def handle_later(self):
"""Change sync (async not allowed in call_soon) to async."""
asyncio.run_coroutine_threadsafe(self.handle_request(), self.loop)
async def handle_request(self):
"""Handle request."""
broadcast = False
if not self.last_pdu:
return
try:
if self.server.broadcast_enable and not self.last_pdu.dev_id:
broadcast = True
# if broadcasting then execute on all slave contexts,
# note response will be ignored
for dev_id in self.server.context.slaves():
response = await self.last_pdu.update_datastore(self.server.context[dev_id])
else:
context = self.server.context[self.last_pdu.dev_id]
response = await self.last_pdu.update_datastore(context)
except NoSuchSlaveException:
Log.error("requested slave does not exist: {}", self.last_pdu.dev_id)
if self.server.ignore_missing_slaves:
return # the client will simply timeout waiting for a response
response = ExceptionResponse(0x00, ExceptionResponse.GATEWAY_NO_RESPONSE)
except Exception as exc: # pylint: disable=broad-except
Log.error(
"Datastore unable to fulfill request: {}; {}",
exc,
traceback.format_exc(),
)
response = ExceptionResponse(0x00, ExceptionResponse.SLAVE_FAILURE)
# no response when broadcasting
if not broadcast:
response.transaction_id = self.last_pdu.transaction_id
response.dev_id = self.last_pdu.dev_id
self.server_send(response, self.last_addr)
def server_send(self, pdu, addr):
"""Send message."""
if not pdu:
Log.debug("Skipping sending response!!")
else:
self.pdu_send(pdu, addr=addr)