This commit is contained in:
Wolfgang Hottgenroth 2019-09-14 22:06:44 +02:00
commit de7a3fb0da
Signed by: wn
GPG Key ID: 6C1E5E531E0D5D7F
18 changed files with 237 additions and 167 deletions

29
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,29 @@
stages:
- check
- deploy
build:
stage: check
image: registry.gitlab.com/wolutator/base-build-env:latest
tags:
- hottis
- linux
- docker
script:
- for I in src/*.py; do python -m py_compile $I; done
- for I in src/*.py; do python -m pycodestyle --max-line-length=120 $I; done
deploy:
stage: deploy
tags:
- hottis
- linux
- rpi
- modbus
only:
- deploy
script:
- sudo service modbusMaster stop
- cp src/*.py /opt/services/modbusMaster
- sudo service modbusMaster start

View File

@ -8,7 +8,10 @@ import RegisterDatapoint
import logging
import Converters
class CmdInterpreterException(ValueError): pass
class CmdInterpreterException(ValueError):
pass
def parseIntArbitraryBase(s):
i = 0
@ -20,6 +23,7 @@ def parseIntArbitraryBase(s):
i = int(s, 10)
return i
class CmdInterpreter(cmd.Cmd):
def __init__(self, infile, outfile, config, notifier, registers):
super().__init__(stdin=infile, stdout=outfile)
@ -29,7 +33,7 @@ class CmdInterpreter(cmd.Cmd):
self.registers = registers
self.prompt = "test8> "
self.intro = "test8 admin interface"
self.splitterRe = re.compile('\s+')
self.splitterRe = re.compile(r'\s+')
self.logger = logging.getLogger('CmdInterpreter')
def onecmd(self, line):
@ -47,14 +51,14 @@ class CmdInterpreter(cmd.Cmd):
self.stdout.write(text)
self.stdout.write("\n\r")
def __listConverterNames(self):
return [ name for name in Converters.Converters ]
return [name for name in Converters.Converters]
def do_add_hr(self, arg):
try:
(label, unit, address, count, scanrate, readTopic, writeTopic, feedbackTopic, converter) = self.splitterRe.split(arg)
(label, unit, address, count,
scanrate, readTopic, writeTopic,
feedbackTopic, converter) = self.splitterRe.split(arg)
self.__println("Label: {0}".format(label))
self.__println("Unit: {0}".format(unit))
self.__println("Address: {0}".format(address))
@ -77,14 +81,14 @@ class CmdInterpreter(cmd.Cmd):
address = parseIntArbitraryBase(address)
count = parseIntArbitraryBase(count)
scanrate = float(scanrate)
r = RegisterDatapoint.HoldingRegisterDatapoint(label=label,
unit=unit,
address=address,
count=count,
scanRate=datetime.timedelta(seconds=scanrate),
publishTopic=readTopic,
subscribe=writeTopic,
feedbackTopic=feedbackTopic,
r = RegisterDatapoint.HoldingRegisterDatapoint(label=label,
unit=unit,
address=address,
count=count,
scanRate=datetime.timedelta(seconds=scanrate),
publishTopic=readTopic,
subscribe=writeTopic,
feedbackTopic=feedbackTopic,
converter=converter)
self.registers.append(r)
except ValueError as e:
@ -108,9 +112,9 @@ class CmdInterpreter(cmd.Cmd):
self.__println("<WriteTopic> Topic to be subscribe to receive data to be")
self.__println(" written")
self.__println("<FeedbackTopic> Topic to publish feedback after a write process,")
self.__println("<Converter> Converter for data, one of {0}".format(', '.join(self.__listConverterNames())))
self.__println("<Converter> Converter for data, one of {0}"
.format(', '.join(self.__listConverterNames())))
def do_add_coil(self, arg):
try:
(label, unit, address, scanrate, readTopic, writeTopic, feedbackTopic) = self.splitterRe.split(arg)
@ -134,7 +138,6 @@ class CmdInterpreter(cmd.Cmd):
unit=unit,
address=address,
scanRate=datetime.timedelta(seconds=scanrate),
publishTopic=readTopic,
subscribeTopic=writeTopic,
feedbackTopic=feedbackTopic)
self.registers.append(r)
@ -156,7 +159,7 @@ class CmdInterpreter(cmd.Cmd):
self.__println("<WriteTopic> Topic to be subscribe to receive data to be")
self.__println(" written")
self.__println("<FeedbackTopic> Topic to publish feedback after a write process,")
def do_add_ir(self, arg):
try:
(label, unit, address, count, scanrate, updateOnly, readTopic, converter) = self.splitterRe.split(arg)
@ -183,12 +186,12 @@ class CmdInterpreter(cmd.Cmd):
address = parseIntArbitraryBase(address)
count = parseIntArbitraryBase(count)
scanrate = float(scanrate)
r = RegisterDatapoint.InputRegisterDatapoint(label=label,
unit=unit,
address=address,
count=count, scanRate=datetime.timedelta(seconds=scanrate),
updateOnly=updateOnly,
publishTopic=readTopic,
r = RegisterDatapoint.InputRegisterDatapoint(label=label,
unit=unit,
address=address,
count=count, scanRate=datetime.timedelta(seconds=scanrate),
updateOnly=updateOnly,
publishTopic=readTopic,
converter=converter)
self.registers.append(r)
except ValueError as e:
@ -207,7 +210,8 @@ class CmdInterpreter(cmd.Cmd):
self.__println("<ScanRate> Scanrate in seconds (float)")
self.__println("<UpdateOnly> Publish only when value has changed")
self.__println("<ReadTopic> Topic to publish read data")
self.__println("<Converter> Converter for data, one of {0}".format(', '.join(self.__listConverterNames())))
self.__println("<Converter> Converter for data, one of {0}"
.format(', '.join(self.__listConverterNames())))
def do_add_di(self, arg):
try:
@ -234,13 +238,13 @@ class CmdInterpreter(cmd.Cmd):
count = parseIntArbitraryBase(count)
scanrate = float(scanrate)
bitCount = int(bitCount)
r = RegisterDatapoint.DiscreteInputDatapoint(label=label,
unit=unit,
address=address,
count=count,
scanRate=datetime.timedelta(seconds=scanrate),
updateOnly=updateOnly,
publishTopic=readTopic,
r = RegisterDatapoint.DiscreteInputDatapoint(label=label,
unit=unit,
address=address,
count=count,
scanRate=datetime.timedelta(seconds=scanrate),
updateOnly=updateOnly,
publishTopic=readTopic,
bitCount=bitCount)
self.registers.append(r)
except ValueError as e:
@ -264,7 +268,7 @@ class CmdInterpreter(cmd.Cmd):
def do_list(self, arg):
for i, r in enumerate(self.registers):
self.__println("#{0}: {1!s}".format(i, r))
def help_list(self):
self.__println("Usage: list")
self.__println("-----------")
@ -275,7 +279,7 @@ class CmdInterpreter(cmd.Cmd):
r.errorCount = 0
r.writeCount = 0
r.readCount = 0
def help_reset(self):
self.__println("Usage: reset")
self.__println("-----------")
@ -288,7 +292,8 @@ class CmdInterpreter(cmd.Cmd):
ratio = -1
else:
ratio = float(r.errorCount) / float(processCount)
self.__println("#{0:2d}: {1:15s} ({2:2d}, {3:5d}), rc: {4:7d}, wc: {5:7d}, pc: {6:7d}, ec: {7:7d}, q: {8:1.4f}"
self.__println("#{0:2d}: {1:15s} ({2:2d}, {3:5d}), rc: {4:7d}, "
"wc: {5:7d}, pc: {6:7d}, ec: {7:7d}, q: {8:1.4f}"
.format(i, r.label, r.unit, r.address, r.readCount, r.writeCount,
processCount, r.errorCount, ratio))
@ -323,10 +328,10 @@ class CmdInterpreter(cmd.Cmd):
value = None
else:
raise CmdInterpreterException('unknown type specifier, must be I, F, B, S or T')
if key not in r.__dict__:
raise CmdInterpreterException('selected datapoint does not support key')
r.__dict__[key] = value
except ValueError as e:
self.__println("ERROR: {0!s}, {1!s}".format(e.__class__.__name__, e))
@ -375,7 +380,7 @@ class CmdInterpreter(cmd.Cmd):
def do_quit(self, arg):
self.__println("Bye!")
return True
return True
def __save(self):
RegisterDatapoint.saveRegisterList(self.registers, self.config.registerFile)
@ -399,11 +404,10 @@ class CmdInterpreter(cmd.Cmd):
self.__println("Reload the register file, overwrite all unsaved changes.")
class CmdHandle(socketserver.StreamRequestHandler):
def handle(self):
logger = logging.getLogger('CmdHandle')
cmd = CmdInterpreter(io.TextIOWrapper(self.rfile), io.TextIOWrapper(self.wfile), self.server.userData.config,
cmd = CmdInterpreter(io.TextIOWrapper(self.rfile), io.TextIOWrapper(self.wfile), self.server.userData.config,
self.server.userData.notifier, self.server.userData.registers)
try:
cmd.cmdloop()
@ -411,25 +415,28 @@ class CmdHandle(socketserver.StreamRequestHandler):
except ConnectionAbortedError as e:
logger.info("Cmd handle externally interrupted")
class MyThreadingTCPServer(socketserver.ThreadingTCPServer):
def __init__(self, host, handler, userData):
super().__init__(host, handler)
self.userData = userData
class MyCmdUserData(object):
def __init__(self, config, notifier, registers):
self.config = config
self.notifier = notifier
self.registers = registers
class CmdServer(threading.Thread):
def __init__(self, config, notifier, registers):
super().__init__()
self.config = config
self.server = MyThreadingTCPServer((config.cmdAddress, config.cmdPort), CmdHandle, MyCmdUserData(config, notifier, registers))
self.server = MyThreadingTCPServer((config.cmdAddress, config.cmdPort),
CmdHandle,
MyCmdUserData(config, notifier, registers))
# self.daemon = True
def run(self):
self.server.serve_forever()

View File

@ -12,6 +12,7 @@ import logging
ERROR_PIN = 29
class CommunicationProcessor(threading.Thread):
def __init__(self, config, queue, pubQueue):
super().__init__()
@ -33,7 +34,6 @@ class CommunicationProcessor(threading.Thread):
return MyRS485.MyRS485(port=self.config.serialPort, baudrate=self.config.serialBaudRate, stopbits=1,
timeout=1)
def run(self):
client = ModbusSerialClient(method='rtu')
client.socket = self.__getSerial()
@ -56,7 +56,3 @@ class CommunicationProcessor(threading.Thread):
client.socket = self.__getSerial()
finally:
time.sleep(self.config.interCommDelay)

View File

@ -1,18 +1,30 @@
# in: from Modbus to MQTT, input is a list of 16bit integers, output shall be the desired format
# in: from Modbus to MQTT, input is a list of 16bit integers, output shall be the desired format
# to be sent in the MQTT message
# out: from MQTT to Modbus, input is the format received from MQTT, output shall be a list of
# out: from MQTT to Modbus, input is the format received from MQTT, output shall be a list of
# 16bit integers to be written to the Modbus slave
from struct import pack, unpack
def fix1twos(x):
x = x[0]
r = x
if x & 0x8000:
r = ((x - 1) ^ 0xffff) * -1
return r / 10
Converters = {
"dht20TOFloat": {
"in": lambda x : float(x[0]) / 10.0,
"in": lambda x: float(x[0]) / 10.0,
"out": None
},
"uint32": {
"in": lambda x : unpack('L', pack('HH', *x))[0],
"out": lambda x : unpack('HH', pack('L', int(x)))
"in": lambda x: unpack('L', pack('HH', *x))[0],
"out": lambda x: unpack('HH', pack('L', int(x)))
},
"fix1twos": {
"in": lambda x: fix1twos(x),
"out": None
}
}

View File

@ -3,6 +3,7 @@ import MqttProcessor
import logging
import time
class Heartbeat(threading.Thread):
def __init__(self, config, pubQueue):
super().__init__()

View File

@ -4,6 +4,7 @@ from NotificationForwarder import AbstractNotificationReceiver
import logging
import Pins
class PublishItem(object):
def __init__(self, topic, payload):
self.topic = topic
@ -12,15 +13,19 @@ class PublishItem(object):
def __str__(self):
return 'Topic: {0}, Payload: {1}'.format(self.topic, self.payload)
def mqttOnConnectCallback(client, userdata, flags, rc):
userdata.onConnect()
def mqttOnMessageCallback(client, userdata, message):
userdata.onMessage(message.topic, message.payload)
def mqttOnDisconnectCallback(client, userdata, rc):
userdata.onDisconnect(rc)
class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
def __init__(self, config, registers, queue, pubQueue):
super().__init__()
@ -30,14 +35,15 @@ class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
self.pubQueue = pubQueue
self.client = mqtt.Client(userdata=self)
self.subscriptions = []
self.topicRegisterMap ={}
self.topicRegisterMap = {}
# self.daemon = True
self.logger = logging.getLogger('MqttProcessor')
def __processUpdatedRegisters(self, force=False):
self.logger.debug("MqttProcessor.__updateSubscriptions")
subscribeTopics = [ r.subscribeTopic for r in self.registers if hasattr(r,'subscribeTopic') and r.subscribeTopic]
subscribeTopics = [r.subscribeTopic for r in self.registers if hasattr(r, 'subscribeTopic')
and r.subscribeTopic]
self.logger.debug("Topics: {0!s}".format(subscribeTopics))
for subscribeTopic in subscribeTopics:
@ -52,7 +58,8 @@ class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
self.client.unsubscribe(subscription)
self.subscriptions.remove(subscription)
self.topicRegisterMap = { r.subscribeTopic: r for r in self.registers if hasattr(r,'subscribeTopic') and r.subscribeTopic }
self.topicRegisterMap = {r.subscribeTopic: r for r in self.registers if hasattr(r, 'subscribeTopic')
and r.subscribeTopic}
def receiveNotification(self, arg):
self.logger.info("MqttProcessor:registersChanged")
@ -76,7 +83,6 @@ class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
else:
self.logger.error("Invalid object in publish queue")
def onConnect(self):
# print("MqttProcessor.onConnect")
self.__processUpdatedRegisters(force=True)
@ -91,4 +97,3 @@ class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
self.logger.debug("{0}: {1!s} -> {2!s}".format(topic, payload, r))
r.onMessage(payload)
self.queue.put(r)

View File

@ -6,12 +6,18 @@ class MyPriorityQueueItem(object):
self.itemWithPriority = itemWithPriority
def __lt__(self, other): return self.itemWithPriority.priority < other.itemWithPriority.priority
def __le__(self, other): return self.itemWithPriority.priority <= other.itemWithPriority.priority
def __eq__(self, other): return self.itemWithPriority.priority == other.itemWithPriority.priority
def __ne__(self, other): return self.itemWithPriority.priority != other.itemWithPriority.priority
def __gt__(self, other): return self.itemWithPriority.priority > other.itemWithPriority.priority
def __ge__(self, other): return self.itemWithPriority.priority >= other.itemWithPriority.priority
class MyPriorityQueue(queue.PriorityQueue):
def _put(self, itemWithPriority):
i = MyPriorityQueueItem(itemWithPriority)

View File

@ -7,6 +7,7 @@ import termios
DE_PIN = 0
class MyRS485(serial.Serial):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -24,4 +25,3 @@ class MyRS485(serial.Serial):
break
# wiringpi.digitalWrite(DE_PIN, wiringpi.LOW)
Pins.pinsWrite('DE', False)

View File

@ -3,6 +3,7 @@ class AbstractNotificationReceiver(object):
def receiveNotification(self, arg):
raise NotImplementedError
class NotificationForwarder(object):
def __init__(self):
self.receivers = []

View File

@ -8,7 +8,6 @@ PINS = {
}
def pinsInit():
wiringpi.wiringPiSetup()
for pin in PINS.values():
@ -21,4 +20,3 @@ def pinsWrite(pinName, v):
else:
pinState = wiringpi.LOW
wiringpi.digitalWrite(PINS[pinName], pinState)

View File

@ -6,7 +6,10 @@ import logging
import json
import Converters
class DatapointException(Exception): pass
class DatapointException(Exception):
pass
class AbstractModbusDatapoint(object):
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, converter=None):
@ -40,17 +43,16 @@ class AbstractModbusDatapoint(object):
self.errorCount, self.readCount, self.writeCount, self.converter))
def jsonify(self):
return {'type':self.__class__.__name__,
'args': { k: getattr(self, k) for k in self.argList }
}
return {'type': self.__class__.__name__,
'args': {k: getattr(self, k) for k in self.argList}
}
def process(self, client):
raise NotImplementedError
class HoldingRegisterDatapoint(AbstractModbusDatapoint):
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None,
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None,
publishTopic=None, subscribeTopic=None, feedbackTopic=None, converter=None):
super().__init__(label, unit, address, count, scanRate, converter)
self.argList = self.argList + ['publishTopic', 'subscribeTopic', 'feedbackTopic']
@ -83,16 +85,16 @@ class HoldingRegisterDatapoint(AbstractModbusDatapoint):
except Exception as e:
raise DatapointException("Exception caught when trying to converter modbus data: {0!s}".format(e))
result = client.write_registers(address=self.address,
unit=self.unit,
values=values)
logger.debug("Write result: {0!s}".format(result))
unit=self.unit,
values=values)
logger.debug("Write result: {0!s}".format(result))
self.writeRequestValue = None
else:
# perform read operation
logger.debug("Holding register, perform read operation")
self.readCount += 1
result = client.read_holding_registers(address=self.address,
count=self.count,
result = client.read_holding_registers(address=self.address,
count=self.count,
unit=self.unit)
if type(result) in [ExceptionResponse, ModbusIOException]:
self.errorCount += 1
@ -110,7 +112,7 @@ class HoldingRegisterDatapoint(AbstractModbusDatapoint):
if self.publishTopic:
pubQueue.put(MqttProcessor.PublishItem(self.publishTopic, str(value)))
self.lastContact = datetime.datetime.now()
def onMessage(self, value):
self.writeRequestValue = value
@ -144,7 +146,7 @@ class CoilDatapoint(AbstractModbusDatapoint):
logger.debug("Coil, perform write operation")
self.writeCount += 1
logger.debug("{0}: raw: {1!s}".format(self.label, self.writeRequestValue))
value=None
value = None
if self.writeRequestValue in ['true', 'True', 'yes', 'Yes', 'On', 'on']:
value = True
elif self.writeRequestValue in ['false', 'False', 'no', 'No', 'Off', 'off']:
@ -165,7 +167,8 @@ class CoilDatapoint(AbstractModbusDatapoint):
class ReadOnlyDatapoint(AbstractModbusDatapoint):
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, updateOnly=None, publishTopic=None, converter=None):
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, updateOnly=None,
publishTopic=None, converter=None):
super().__init__(label, unit, address, count, scanRate, converter)
self.argList = self.argList + ['updateOnly', 'publishTopic']
self.updateOnly = updateOnly
@ -178,9 +181,8 @@ class ReadOnlyDatapoint(AbstractModbusDatapoint):
self.lastValue))
class InputRegisterDatapoint(ReadOnlyDatapoint):
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, updateOnly=None,
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, updateOnly=None,
publishTopic=None, converter=None):
super().__init__(label, unit, address, count, scanRate, updateOnly, publishTopic, converter)
self.type = 'input register'
@ -214,7 +216,7 @@ class InputRegisterDatapoint(ReadOnlyDatapoint):
class DiscreteInputDatapoint(ReadOnlyDatapoint):
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, updateOnly=None,
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, updateOnly=None,
publishTopic=None, converter=None, bitCount=8):
super().__init__(label, unit, address, count, scanRate, updateOnly, publishTopic, converter)
self.argList = self.argList + ['bitCount']
@ -243,12 +245,11 @@ class DiscreteInputDatapoint(ReadOnlyDatapoint):
self.lastValues[i] = result.getBit(i)
logger.debug("{0}, {1}: changed: {2!s}".format(self.label, i, result.getBit(i)))
if self.publishTopic:
pubQueue.put(MqttProcessor.PublishItem("{0}/{1}".format(self.publishTopic, i), str(result.getBit(i))))
pubQueue.put(MqttProcessor.PublishItem("{0}/{1}"
.format(self.publishTopic, i), str(result.getBit(i))))
self.lastContact = datetime.datetime.now()
class JsonifyEncoder(json.JSONEncoder):
def default(self, o):
res = None
@ -261,6 +262,7 @@ class JsonifyEncoder(json.JSONEncoder):
res = super().default(o)
return res
def datapointObjectHook(j):
if type(j) == dict and 'type' in j and 'args' in j:
klass = eval(j['type'])
@ -269,14 +271,15 @@ def datapointObjectHook(j):
else:
return j
def saveRegisterList(registerList, registerListFile):
js = json.dumps(registerList, cls=JsonifyEncoder, sort_keys=True, indent=4)
with open(registerListFile, 'w') as f:
f.write(js)
def loadRegisterList(registerListFile):
with open(registerListFile, 'r') as f:
js = f.read()
registerList = json.loads(js, object_hook=datapointObjectHook)
return registerList

View File

@ -3,6 +3,7 @@ import datetime
from NotificationForwarder import AbstractNotificationReceiver
import logging
class ScanRateConsideringQueueFeeder(threading.Thread, AbstractNotificationReceiver):
def __init__(self, config, registers, queue):
super().__init__()
@ -14,7 +15,7 @@ class ScanRateConsideringQueueFeeder(threading.Thread, AbstractNotificationRecei
self.logger = logging.getLogger('ScanRateConsideringQueueFeeder')
def getMinimalScanrate(self):
return min([r.scanRate.total_seconds() for r in self.registers if r.scanRate])
return min([r.scanRate.total_seconds() for r in self.registers if r.scanRate])
def receiveNotification(self, arg):
self.logger.info("ScanRateConsideringQueueFeeder:registersChanged")
@ -26,10 +27,10 @@ class ScanRateConsideringQueueFeeder(threading.Thread, AbstractNotificationRecei
registersToBeHandled = [
r for r in self.registers if ((not r.enqueued) and
(r.scanRate) and
((not r.lastContact) or
((not r.lastContact) or
(r.lastContact + r.scanRate < datetime.datetime.now())))
]
registersToBeHandled.sort(key=lambda x : x.scanRate)
registersToBeHandled.sort(key=lambda x: x.scanRate)
for r in registersToBeHandled:
self.queue.put(r)
r.enqueued = True

View File

@ -4,12 +4,14 @@ import pickle
datapoints = [
RegisterDatapoint.InputRegisterDatapoint('Temperature', 5, 0x0001, 1, datetime.timedelta(seconds=1.0), False, 'Pub/Temperature'),
RegisterDatapoint.InputRegisterDatapoint('Humidity', 5, 0x0002, 1, datetime.timedelta(seconds=1.0), True, 'Pub/Humidity'),
RegisterDatapoint.DiscreteInputDatapoint('Switches', 4, 0x0000, 1, datetime.timedelta(seconds=1.0), True, 'Pub/Switches'),
RegisterDatapoint.InputRegisterDatapoint('Temperature', 5, 0x0001, 1,
datetime.timedelta(seconds=1.0), False, 'Pub/Temperature'),
RegisterDatapoint.InputRegisterDatapoint('Humidity', 5, 0x0002, 1,
datetime.timedelta(seconds=1.0), True, 'Pub/Humidity'),
RegisterDatapoint.DiscreteInputDatapoint('Switches', 4, 0x0000, 1,
datetime.timedelta(seconds=1.0), True, 'Pub/Switches'),
]
with open('registers.pkl', 'wb') as f:
pickle.dump(datapoints, f)

View File

@ -3,4 +3,4 @@ import RegisterDatapoint
registers = RegisterDatapoint.loadRegisterList('registers.json')
for r in registers:
print("{0!s}".format(r))
print("{0!s}".format(r))

View File

@ -36,7 +36,6 @@ if __name__ == "__main__":
nf = NotificationForwarder.NotificationForwarder()
logger.debug('infrastructure prepared')
datapoints = RegisterDatapoint.loadRegisterList(config.registerFile)
logger.debug('datapoints read')
@ -56,7 +55,7 @@ if __name__ == "__main__":
hb = Heartbeat.Heartbeat(config, pubQueue)
hb.start()
logger.debug('Heartbeat started')
qf = ScanRateConsideringQueueFeeder.ScanRateConsideringQueueFeeder(config, datapoints, queue)
nf.register(qf)
qf.start()

19
src/modbusMaster.service Normal file
View File

@ -0,0 +1,19 @@
[Unit]
Description=ModbusMaster
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
GuessMainPID=yes
ExecStart=/usr/bin/python master.py
ExecStop=kill -SIGINT $mainpid
Restart=on-failure
WorkingDirectory=/opt/services/modbusMaster
[Install]
Alias=ModbusMaster
WantedBy=multi-user.target

View File

@ -32,77 +32,21 @@
"converter": null,
"count": 1,
"label": "Switches",
"publishTopic": "Pub/Switches",
"publishTopic": "IoT/Status/Modbus2/Switches",
"scanRate": 0.5,
"unit": 4,
"updateOnly": false
"updateOnly": true
},
"type": "DiscreteInputDatapoint"
},
{
"args": {
"address": 40010,
"converter": "uint32",
"count": 2,
"feedbackTopic": "FB/Counter1",
"label": "Counter1",
"publishTopic": "Pub/Counter1",
"scanRate": 1.0,
"subscribeTopic": "Sub/Counter1",
"unit": 4
},
"type": "HoldingRegisterDatapoint"
},
{
"args": {
"address": 40012,
"converter": "uint32",
"count": 2,
"feedbackTopic": "FB/Counter2",
"label": "Counter2",
"publishTopic": "Pub/Counter2",
"scanRate": null,
"subscribeTopic": "Pub/Counter2",
"unit": 4
},
"type": "HoldingRegisterDatapoint"
},
{
"args": {
"address": 40014,
"converter": "uint32",
"count": 2,
"feedbackTopic": "FB/Counter3",
"label": "Counter3",
"publishTopic": "Pub/Counter3",
"scanRate": null,
"subscribeTopic": "FB/Counter3",
"unit": 4
},
"type": "HoldingRegisterDatapoint"
},
{
"args": {
"address": 40016,
"converter": "uint32",
"count": 2,
"feedbackTopic": "FB/Counter4",
"label": "Counter4",
"publishTopic": "Pub/Counter4",
"scanRate": 1.0,
"subscribeTopic": "Sub/Counter4",
"unit": 4
},
"type": "HoldingRegisterDatapoint"
},
{
"args": {
"address": 0,
"feedbackTopic": "FB/Coil1",
"label": "Coil1",
"publishTopic": "Pub/Coil1",
"scanRate": 1.0,
"subscribeTopic": "Sub/Coil1",
"feedbackTopic": "IoT/Feedback/Modbus2/Coils/0",
"label": "Coil0",
"publishTopic": "IoT/Status/Modbus2/Coils/0",
"scanRate": 0.0,
"subscribeTopic": "IoT/Action/Modbus2/Coils/0",
"unit": 4
},
"type": "CoilDatapoint"
@ -110,13 +54,63 @@
{
"args": {
"address": 1,
"feedbackTopic": "FB/Coil2",
"label": "Coil2",
"publishTopic": "Pub/Coil2",
"scanRate": 1.0,
"subscribeTopic": "Sub/Coil2",
"feedbackTopic": "IoT/Feedback/Modbus2/Coils/1",
"label": "Coil1",
"publishTopic": "IoT/Status/Modbus2/Coils/1",
"scanRate": 0.0,
"subscribeTopic": "IoT/Action/Modbus2/Coils/1",
"unit": 4
},
"type": "CoilDatapoint"
},
{
"args": {
"address": 2,
"feedbackTopic": "IoT/Feedback/Modbus2/Coils/2",
"label": "Coil2",
"publishTopic": "IoT/Status/Modbus2/Coils/2",
"scanRate": 0.0,
"subscribeTopic": "IoT/Action/Modbus2/Coils/2",
"unit": 4
},
"type": "CoilDatapoint"
},
{
"args": {
"address": 3,
"feedbackTopic": "IoT/Feedback/Modbus2/Coils/3",
"label": "Coil3",
"publishTopic": "IoT/Status/Modbus2/Coils/3",
"scanRate": 0.0,
"subscribeTopic": "IoT/Action/Modbus2/Coils/3",
"unit": 4
},
"type": "CoilDatapoint"
},
{
"args": {
"address": 1,
"converter": "fix1twos",
"count": 1,
"label": "wago1",
"publishTopic": "IoT/Measurement/Modbus2/Wago1",
"scanRate": 1.0,
"unit": 11,
"updateOnly": false
},
"type": "InputRegisterDatapoint"
},
{
"args": {
"address": 0,
"converter": "fix1twos",
"count": 1,
"label": "Freezer",
"publishTopic": "IoT/Measurement/Modbus2/Freezer",
"scanRate": 1.0,
"unit": 11,
"updateOnly": false
},
"type": "InputRegisterDatapoint"
}
]
]

View File

@ -10,15 +10,12 @@ with open('registers.pkl', 'rb') as f:
newDatapoints = []
for dp in datapoints:
ndp = type(dp)()
for k,v in dp.__dict__.items():
for k, v in dp.__dict__.items():
if k != 'logger':
ndp.__dict__[k] = v
newDatapoints.append(ndp)
js = json.dumps(newDatapoints, cls=RegisterDatapoint.JsonifyEncoder, sort_keys=True, indent=4)
print(js)
RegisterDatapoint.saveRegisterList(newDatapoints, 'registers.json')
RegisterDatapoint.saveRegisterList(newDatapoints, 'registers.json')