91 lines
3.4 KiB
Python
91 lines
3.4 KiB
Python
from pymodbus.client import ModbusTcpClient as ModbusClient
|
|
from pymodbus.exceptions import ModbusIOException
|
|
from time import sleep
|
|
import threading
|
|
from loguru import logger
|
|
|
|
|
|
|
|
class ModbusHandler(threading.Thread):
|
|
def __init__(self, config, processImage):
|
|
super().__init__()
|
|
|
|
self.config = config["modbus"]
|
|
self.processImage = processImage
|
|
self.killBill = False
|
|
|
|
def stop(self):
|
|
self.killBill = True
|
|
|
|
def run(self):
|
|
modbusClient = self.config["client"]
|
|
modbusRefreshPeriod = float(self.config["scanrate"])
|
|
|
|
client = ModbusClient(modbusClient)
|
|
|
|
|
|
try:
|
|
client.connect()
|
|
|
|
res = client.read_holding_registers(0x1010, 4)
|
|
if isinstance(res, ModbusIOException):
|
|
raise Exception(self.processImage)
|
|
|
|
if len(res.registers) != 4:
|
|
raise Exception("Unexpected number of registers for process image ({})".format(len(res.registers)))
|
|
|
|
(analogOutputBits, analogInputBits, digitalOutputBits, digitalInputBits) = res.registers
|
|
logger.debug(f"AO: {analogOutputBits}, AI: {analogInputBits}, DO: {digitalOutputBits}, DI: {digitalInputBits}")
|
|
|
|
self.processImage.init(digitalOutputBits, digitalInputBits, analogInputBits)
|
|
|
|
reg = client.read_coils(0, digitalOutputBits)
|
|
if isinstance(reg, ModbusIOException):
|
|
raise Exception(reg)
|
|
with self.processImage:
|
|
self.processImage.setCoils(reg.bits)
|
|
|
|
while not self.killBill:
|
|
try:
|
|
if not client.is_socket_open():
|
|
client.connect()
|
|
|
|
reg = client.read_input_registers(0, analogInputBits // 8)
|
|
if isinstance(reg, ModbusIOException):
|
|
raise Exception(reg)
|
|
analogInputs = reg.registers
|
|
|
|
reg = client.read_discrete_inputs(0, digitalInputBits)
|
|
if isinstance(reg, ModbusIOException):
|
|
raise Exception(reg)
|
|
discreteInputs = reg.bits
|
|
|
|
coils = None
|
|
with self.processImage:
|
|
self.processImage.setAnalogsInputs(analogInputs)
|
|
self.processImage.setDiscreteInputs(discreteInputs)
|
|
if self.processImage.hasPendingInputChanges():
|
|
self.processImage.notify()
|
|
if self.processImage.hasPendingOutputChanges():
|
|
coils = self.processImage.getCoils()
|
|
|
|
if coils:
|
|
logger.debug("write coils to device: {}".format(coils))
|
|
reg = client.write_coils(0, coils)
|
|
if isinstance(reg, ModbusIOException):
|
|
raise Exception(reg)
|
|
|
|
except Exception as e:
|
|
logger.error("Exception in inner modbus handler loop: {}".format(e))
|
|
client.close()
|
|
finally:
|
|
#sleep(modbusRefreshPeriod)
|
|
if (self.processImage.coilEvent.wait(timeout=modbusRefreshPeriod)):
|
|
self.processImage.coilEvent.clear()
|
|
|
|
|
|
except Exception as e:
|
|
logger.error("Exception in modbus handler: {}".format(e))
|
|
finally:
|
|
client.close()
|