Compare commits
1 Commits
deploy
...
direct_reg
Author | SHA1 | Date | |
---|---|---|---|
b97b849bfa
|
@ -1,29 +0,0 @@
|
|||||||
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
|
|
||||||
|
|
@ -8,10 +8,7 @@ import RegisterDatapoint
|
|||||||
import logging
|
import logging
|
||||||
import Converters
|
import Converters
|
||||||
|
|
||||||
|
class CmdInterpreterException(ValueError): pass
|
||||||
class CmdInterpreterException(ValueError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def parseIntArbitraryBase(s):
|
def parseIntArbitraryBase(s):
|
||||||
i = 0
|
i = 0
|
||||||
@ -23,7 +20,6 @@ def parseIntArbitraryBase(s):
|
|||||||
i = int(s, 10)
|
i = int(s, 10)
|
||||||
return i
|
return i
|
||||||
|
|
||||||
|
|
||||||
class CmdInterpreter(cmd.Cmd):
|
class CmdInterpreter(cmd.Cmd):
|
||||||
def __init__(self, infile, outfile, config, notifier, registers):
|
def __init__(self, infile, outfile, config, notifier, registers):
|
||||||
super().__init__(stdin=infile, stdout=outfile)
|
super().__init__(stdin=infile, stdout=outfile)
|
||||||
@ -33,7 +29,7 @@ class CmdInterpreter(cmd.Cmd):
|
|||||||
self.registers = registers
|
self.registers = registers
|
||||||
self.prompt = "test8> "
|
self.prompt = "test8> "
|
||||||
self.intro = "test8 admin interface"
|
self.intro = "test8 admin interface"
|
||||||
self.splitterRe = re.compile(r'\s+')
|
self.splitterRe = re.compile('\s+')
|
||||||
self.logger = logging.getLogger('CmdInterpreter')
|
self.logger = logging.getLogger('CmdInterpreter')
|
||||||
|
|
||||||
def onecmd(self, line):
|
def onecmd(self, line):
|
||||||
@ -51,14 +47,14 @@ class CmdInterpreter(cmd.Cmd):
|
|||||||
self.stdout.write(text)
|
self.stdout.write(text)
|
||||||
self.stdout.write("\n\r")
|
self.stdout.write("\n\r")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __listConverterNames(self):
|
def __listConverterNames(self):
|
||||||
return [name for name in Converters.Converters]
|
return [ name for name in Converters.Converters ]
|
||||||
|
|
||||||
def do_add_hr(self, arg):
|
def do_add_hr(self, arg):
|
||||||
try:
|
try:
|
||||||
(label, unit, address, count,
|
(label, unit, address, count, scanrate, readTopic, writeTopic, feedbackTopic, converter) = self.splitterRe.split(arg)
|
||||||
scanrate, readTopic, writeTopic,
|
|
||||||
feedbackTopic, converter) = self.splitterRe.split(arg)
|
|
||||||
self.__println("Label: {0}".format(label))
|
self.__println("Label: {0}".format(label))
|
||||||
self.__println("Unit: {0}".format(unit))
|
self.__println("Unit: {0}".format(unit))
|
||||||
self.__println("Address: {0}".format(address))
|
self.__println("Address: {0}".format(address))
|
||||||
@ -112,29 +108,34 @@ class CmdInterpreter(cmd.Cmd):
|
|||||||
self.__println("<WriteTopic> Topic to be subscribe to receive data to be")
|
self.__println("<WriteTopic> Topic to be subscribe to receive data to be")
|
||||||
self.__println(" written")
|
self.__println(" written")
|
||||||
self.__println("<FeedbackTopic> Topic to publish feedback after a write process,")
|
self.__println("<FeedbackTopic> Topic to publish feedback after a write process,")
|
||||||
self.__println("<Converter> Converter for data, one of {0}"
|
self.__println("<Converter> Converter for data, one of {0}".format(', '.join(self.__listConverterNames())))
|
||||||
.format(', '.join(self.__listConverterNames())))
|
|
||||||
|
|
||||||
def do_add_coil(self, arg):
|
def do_add_coil(self, arg):
|
||||||
try:
|
try:
|
||||||
(label, unit, address, scanrate, writeTopic, feedbackTopic) = self.splitterRe.split(arg)
|
(label, unit, address, scanrate, readTopic, writeTopic, feedbackTopic) = self.splitterRe.split(arg)
|
||||||
self.__println("Label: {0}".format(label))
|
self.__println("Label: {0}".format(label))
|
||||||
self.__println("Unit: {0}".format(unit))
|
self.__println("Unit: {0}".format(unit))
|
||||||
self.__println("Address: {0}".format(address))
|
self.__println("Address: {0}".format(address))
|
||||||
|
self.__println("ScanRate: {0}".format(scanrate))
|
||||||
|
self.__println("ReadTopic: {0}".format(readTopic))
|
||||||
self.__println("WriteTopic: {0}".format(writeTopic))
|
self.__println("WriteTopic: {0}".format(writeTopic))
|
||||||
self.__println("FeedbackTopic: {0}".format(feedbackTopic))
|
self.__println("FeedbackTopic: {0}".format(feedbackTopic))
|
||||||
|
|
||||||
|
if readTopic == 'None':
|
||||||
|
readTopic = None
|
||||||
if writeTopic == 'None':
|
if writeTopic == 'None':
|
||||||
writeTopic = None
|
writeTopic = None
|
||||||
if feedbackTopic == 'None':
|
if feedbackTopic == 'None':
|
||||||
feedbackTopic = None
|
feedbackTopic = None
|
||||||
unit = parseIntArbitraryBase(unit)
|
unit = parseIntArbitraryBase(unit)
|
||||||
address = parseIntArbitraryBase(address)
|
address = parseIntArbitraryBase(address)
|
||||||
scanrate = 0.0
|
scanrate = float(scanrate)
|
||||||
r = RegisterDatapoint.CoilDatapoint(label=label,
|
r = RegisterDatapoint.CoilDatapoint(label=label,
|
||||||
unit=unit,
|
unit=unit,
|
||||||
address=address,
|
address=address,
|
||||||
scanRate=datetime.timedelta(seconds=scanrate),
|
scanRate=datetime.timedelta(seconds=scanrate),
|
||||||
|
publishTopic=readTopic,
|
||||||
subscribeTopic=writeTopic,
|
subscribeTopic=writeTopic,
|
||||||
feedbackTopic=feedbackTopic)
|
feedbackTopic=feedbackTopic)
|
||||||
self.registers.append(r)
|
self.registers.append(r)
|
||||||
@ -143,7 +144,7 @@ class CmdInterpreter(cmd.Cmd):
|
|||||||
|
|
||||||
def help_add_coil(self):
|
def help_add_coil(self):
|
||||||
self.__println("Usage: add_coil <Label> <Unit> <Address> <ScanRate>")
|
self.__println("Usage: add_coil <Label> <Unit> <Address> <ScanRate>")
|
||||||
self.__println(" <WriteTopic> <FeedbackTopic>")
|
self.__println(" <ReadTopic> <WriteTopic> <FeedbackTopic>")
|
||||||
self.__println("Adds a coil")
|
self.__println("Adds a coil")
|
||||||
self.__println("DO NOT FORGET TO SAVE AFTERWARDS!")
|
self.__println("DO NOT FORGET TO SAVE AFTERWARDS!")
|
||||||
self.__println("---------------------------------------------------------------------")
|
self.__println("---------------------------------------------------------------------")
|
||||||
@ -152,6 +153,7 @@ class CmdInterpreter(cmd.Cmd):
|
|||||||
self.__println("<Address> Register address within the device")
|
self.__println("<Address> Register address within the device")
|
||||||
self.__println("<ScanRate> Scanrate in seconds (float), for write datapoints")
|
self.__println("<ScanRate> Scanrate in seconds (float), for write datapoints")
|
||||||
self.__println(" set to zero (0)")
|
self.__println(" set to zero (0)")
|
||||||
|
self.__println("<ReadTopic> Topic to publish read data")
|
||||||
self.__println("<WriteTopic> Topic to be subscribe to receive data to be")
|
self.__println("<WriteTopic> Topic to be subscribe to receive data to be")
|
||||||
self.__println(" written")
|
self.__println(" written")
|
||||||
self.__println("<FeedbackTopic> Topic to publish feedback after a write process,")
|
self.__println("<FeedbackTopic> Topic to publish feedback after a write process,")
|
||||||
@ -206,8 +208,7 @@ class CmdInterpreter(cmd.Cmd):
|
|||||||
self.__println("<ScanRate> Scanrate in seconds (float)")
|
self.__println("<ScanRate> Scanrate in seconds (float)")
|
||||||
self.__println("<UpdateOnly> Publish only when value has changed")
|
self.__println("<UpdateOnly> Publish only when value has changed")
|
||||||
self.__println("<ReadTopic> Topic to publish read data")
|
self.__println("<ReadTopic> Topic to publish read data")
|
||||||
self.__println("<Converter> Converter for data, one of {0}"
|
self.__println("<Converter> Converter for data, one of {0}".format(', '.join(self.__listConverterNames())))
|
||||||
.format(', '.join(self.__listConverterNames())))
|
|
||||||
|
|
||||||
def do_add_di(self, arg):
|
def do_add_di(self, arg):
|
||||||
try:
|
try:
|
||||||
@ -288,8 +289,7 @@ class CmdInterpreter(cmd.Cmd):
|
|||||||
ratio = -1
|
ratio = -1
|
||||||
else:
|
else:
|
||||||
ratio = float(r.errorCount) / float(processCount)
|
ratio = float(r.errorCount) / float(processCount)
|
||||||
self.__println("#{0:2d}: {1:15s} ({2:2d}, {3:5d}), rc: {4:7d}, "
|
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}"
|
||||||
"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,
|
.format(i, r.label, r.unit, r.address, r.readCount, r.writeCount,
|
||||||
processCount, r.errorCount, ratio))
|
processCount, r.errorCount, ratio))
|
||||||
|
|
||||||
@ -400,6 +400,7 @@ class CmdInterpreter(cmd.Cmd):
|
|||||||
self.__println("Reload the register file, overwrite all unsaved changes.")
|
self.__println("Reload the register file, overwrite all unsaved changes.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CmdHandle(socketserver.StreamRequestHandler):
|
class CmdHandle(socketserver.StreamRequestHandler):
|
||||||
def handle(self):
|
def handle(self):
|
||||||
logger = logging.getLogger('CmdHandle')
|
logger = logging.getLogger('CmdHandle')
|
||||||
@ -411,28 +412,25 @@ class CmdHandle(socketserver.StreamRequestHandler):
|
|||||||
except ConnectionAbortedError as e:
|
except ConnectionAbortedError as e:
|
||||||
logger.info("Cmd handle externally interrupted")
|
logger.info("Cmd handle externally interrupted")
|
||||||
|
|
||||||
|
|
||||||
class MyThreadingTCPServer(socketserver.ThreadingTCPServer):
|
class MyThreadingTCPServer(socketserver.ThreadingTCPServer):
|
||||||
def __init__(self, host, handler, userData):
|
def __init__(self, host, handler, userData):
|
||||||
super().__init__(host, handler)
|
super().__init__(host, handler)
|
||||||
self.userData = userData
|
self.userData = userData
|
||||||
|
|
||||||
|
|
||||||
class MyCmdUserData(object):
|
class MyCmdUserData(object):
|
||||||
def __init__(self, config, notifier, registers):
|
def __init__(self, config, notifier, registers):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.notifier = notifier
|
self.notifier = notifier
|
||||||
self.registers = registers
|
self.registers = registers
|
||||||
|
|
||||||
|
|
||||||
class CmdServer(threading.Thread):
|
class CmdServer(threading.Thread):
|
||||||
def __init__(self, config, notifier, registers):
|
def __init__(self, config, notifier, registers):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.config = config
|
self.config = config
|
||||||
self.server = MyThreadingTCPServer((config.cmdAddress, config.cmdPort),
|
self.server = MyThreadingTCPServer((config.cmdAddress, config.cmdPort), CmdHandle, MyCmdUserData(config, notifier, registers))
|
||||||
CmdHandle,
|
|
||||||
MyCmdUserData(config, notifier, registers))
|
|
||||||
# self.daemon = True
|
# self.daemon = True
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.server.serve_forever()
|
self.server.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ import logging
|
|||||||
|
|
||||||
ERROR_PIN = 29
|
ERROR_PIN = 29
|
||||||
|
|
||||||
|
|
||||||
class CommunicationProcessor(threading.Thread):
|
class CommunicationProcessor(threading.Thread):
|
||||||
def __init__(self, config, queue, pubQueue):
|
def __init__(self, config, queue, pubQueue):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -34,6 +33,7 @@ class CommunicationProcessor(threading.Thread):
|
|||||||
return MyRS485.MyRS485(port=self.config.serialPort, baudrate=self.config.serialBaudRate, stopbits=1,
|
return MyRS485.MyRS485(port=self.config.serialPort, baudrate=self.config.serialBaudRate, stopbits=1,
|
||||||
timeout=1)
|
timeout=1)
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
client = ModbusSerialClient(method='rtu')
|
client = ModbusSerialClient(method='rtu')
|
||||||
client.socket = self.__getSerial()
|
client.socket = self.__getSerial()
|
||||||
@ -56,3 +56,7 @@ class CommunicationProcessor(threading.Thread):
|
|||||||
client.socket = self.__getSerial()
|
client.socket = self.__getSerial()
|
||||||
finally:
|
finally:
|
||||||
time.sleep(self.config.interCommDelay)
|
time.sleep(self.config.interCommDelay)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -6,25 +6,13 @@
|
|||||||
from struct import pack, unpack
|
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 = {
|
Converters = {
|
||||||
"dht20TOFloat": {
|
"dht20TOFloat": {
|
||||||
"in": lambda x: float(x[0]) / 10.0,
|
"in": lambda x : float(x[0]) / 10.0,
|
||||||
"out": None
|
"out": None
|
||||||
},
|
},
|
||||||
"uint32": {
|
"uint32": {
|
||||||
"in": lambda x: unpack('L', pack('HH', *x))[0],
|
"in": lambda x : unpack('L', pack('HH', *x))[0],
|
||||||
"out": lambda x: unpack('HH', pack('L', int(x)))
|
"out": lambda x : unpack('HH', pack('L', int(x)))
|
||||||
},
|
|
||||||
"fix1twos": {
|
|
||||||
"in": lambda x: fix1twos(x),
|
|
||||||
"out": None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import MqttProcessor
|
|||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
|
||||||
class Heartbeat(threading.Thread):
|
class Heartbeat(threading.Thread):
|
||||||
def __init__(self, config, pubQueue):
|
def __init__(self, config, pubQueue):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -4,7 +4,6 @@ from NotificationForwarder import AbstractNotificationReceiver
|
|||||||
import logging
|
import logging
|
||||||
import Pins
|
import Pins
|
||||||
|
|
||||||
|
|
||||||
class PublishItem(object):
|
class PublishItem(object):
|
||||||
def __init__(self, topic, payload):
|
def __init__(self, topic, payload):
|
||||||
self.topic = topic
|
self.topic = topic
|
||||||
@ -13,19 +12,15 @@ class PublishItem(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'Topic: {0}, Payload: {1}'.format(self.topic, self.payload)
|
return 'Topic: {0}, Payload: {1}'.format(self.topic, self.payload)
|
||||||
|
|
||||||
|
|
||||||
def mqttOnConnectCallback(client, userdata, flags, rc):
|
def mqttOnConnectCallback(client, userdata, flags, rc):
|
||||||
userdata.onConnect()
|
userdata.onConnect()
|
||||||
|
|
||||||
|
|
||||||
def mqttOnMessageCallback(client, userdata, message):
|
def mqttOnMessageCallback(client, userdata, message):
|
||||||
userdata.onMessage(message.topic, message.payload)
|
userdata.onMessage(message.topic, message.payload)
|
||||||
|
|
||||||
|
|
||||||
def mqttOnDisconnectCallback(client, userdata, rc):
|
def mqttOnDisconnectCallback(client, userdata, rc):
|
||||||
userdata.onDisconnect(rc)
|
userdata.onDisconnect(rc)
|
||||||
|
|
||||||
|
|
||||||
class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
|
class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
|
||||||
def __init__(self, config, registers, queue, pubQueue):
|
def __init__(self, config, registers, queue, pubQueue):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -35,15 +30,14 @@ class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
|
|||||||
self.pubQueue = pubQueue
|
self.pubQueue = pubQueue
|
||||||
self.client = mqtt.Client(userdata=self)
|
self.client = mqtt.Client(userdata=self)
|
||||||
self.subscriptions = []
|
self.subscriptions = []
|
||||||
self.topicRegisterMap = {}
|
self.topicRegisterMap ={}
|
||||||
# self.daemon = True
|
# self.daemon = True
|
||||||
self.logger = logging.getLogger('MqttProcessor')
|
self.logger = logging.getLogger('MqttProcessor')
|
||||||
|
|
||||||
def __processUpdatedRegisters(self, force=False):
|
def __processUpdatedRegisters(self, force=False):
|
||||||
self.logger.debug("MqttProcessor.__updateSubscriptions")
|
self.logger.debug("MqttProcessor.__updateSubscriptions")
|
||||||
|
|
||||||
subscribeTopics = [r.subscribeTopic for r in self.registers if hasattr(r, 'subscribeTopic')
|
subscribeTopics = [ r.subscribeTopic for r in self.registers if hasattr(r,'subscribeTopic') and r.subscribeTopic]
|
||||||
and r.subscribeTopic]
|
|
||||||
self.logger.debug("Topics: {0!s}".format(subscribeTopics))
|
self.logger.debug("Topics: {0!s}".format(subscribeTopics))
|
||||||
|
|
||||||
for subscribeTopic in subscribeTopics:
|
for subscribeTopic in subscribeTopics:
|
||||||
@ -58,8 +52,7 @@ class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
|
|||||||
self.client.unsubscribe(subscription)
|
self.client.unsubscribe(subscription)
|
||||||
self.subscriptions.remove(subscription)
|
self.subscriptions.remove(subscription)
|
||||||
|
|
||||||
self.topicRegisterMap = {r.subscribeTopic: r for r in self.registers if hasattr(r, 'subscribeTopic')
|
self.topicRegisterMap = { r.subscribeTopic: r for r in self.registers if hasattr(r,'subscribeTopic') and r.subscribeTopic }
|
||||||
and r.subscribeTopic}
|
|
||||||
|
|
||||||
def receiveNotification(self, arg):
|
def receiveNotification(self, arg):
|
||||||
self.logger.info("MqttProcessor:registersChanged")
|
self.logger.info("MqttProcessor:registersChanged")
|
||||||
@ -83,6 +76,7 @@ class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
|
|||||||
else:
|
else:
|
||||||
self.logger.error("Invalid object in publish queue")
|
self.logger.error("Invalid object in publish queue")
|
||||||
|
|
||||||
|
|
||||||
def onConnect(self):
|
def onConnect(self):
|
||||||
# print("MqttProcessor.onConnect")
|
# print("MqttProcessor.onConnect")
|
||||||
self.__processUpdatedRegisters(force=True)
|
self.__processUpdatedRegisters(force=True)
|
||||||
@ -97,3 +91,4 @@ class MqttProcessor(threading.Thread, AbstractNotificationReceiver):
|
|||||||
self.logger.debug("{0}: {1!s} -> {2!s}".format(topic, payload, r))
|
self.logger.debug("{0}: {1!s} -> {2!s}".format(topic, payload, r))
|
||||||
r.onMessage(payload)
|
r.onMessage(payload)
|
||||||
self.queue.put(r)
|
self.queue.put(r)
|
||||||
|
|
||||||
|
@ -6,18 +6,12 @@ class MyPriorityQueueItem(object):
|
|||||||
self.itemWithPriority = itemWithPriority
|
self.itemWithPriority = itemWithPriority
|
||||||
|
|
||||||
def __lt__(self, other): return self.itemWithPriority.priority < other.itemWithPriority.priority
|
def __lt__(self, other): return self.itemWithPriority.priority < other.itemWithPriority.priority
|
||||||
|
|
||||||
def __le__(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 __eq__(self, other): return self.itemWithPriority.priority == other.itemWithPriority.priority
|
||||||
|
|
||||||
def __ne__(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 __gt__(self, other): return self.itemWithPriority.priority > other.itemWithPriority.priority
|
||||||
|
|
||||||
def __ge__(self, other): return self.itemWithPriority.priority >= other.itemWithPriority.priority
|
def __ge__(self, other): return self.itemWithPriority.priority >= other.itemWithPriority.priority
|
||||||
|
|
||||||
|
|
||||||
class MyPriorityQueue(queue.PriorityQueue):
|
class MyPriorityQueue(queue.PriorityQueue):
|
||||||
def _put(self, itemWithPriority):
|
def _put(self, itemWithPriority):
|
||||||
i = MyPriorityQueueItem(itemWithPriority)
|
i = MyPriorityQueueItem(itemWithPriority)
|
||||||
|
@ -7,7 +7,6 @@ import termios
|
|||||||
|
|
||||||
DE_PIN = 0
|
DE_PIN = 0
|
||||||
|
|
||||||
|
|
||||||
class MyRS485(serial.Serial):
|
class MyRS485(serial.Serial):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
@ -25,3 +24,4 @@ class MyRS485(serial.Serial):
|
|||||||
break
|
break
|
||||||
# wiringpi.digitalWrite(DE_PIN, wiringpi.LOW)
|
# wiringpi.digitalWrite(DE_PIN, wiringpi.LOW)
|
||||||
Pins.pinsWrite('DE', False)
|
Pins.pinsWrite('DE', False)
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ class AbstractNotificationReceiver(object):
|
|||||||
def receiveNotification(self, arg):
|
def receiveNotification(self, arg):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class NotificationForwarder(object):
|
class NotificationForwarder(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.receivers = []
|
self.receivers = []
|
||||||
|
@ -8,6 +8,7 @@ PINS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def pinsInit():
|
def pinsInit():
|
||||||
wiringpi.wiringPiSetup()
|
wiringpi.wiringPiSetup()
|
||||||
for pin in PINS.values():
|
for pin in PINS.values():
|
||||||
@ -20,3 +21,4 @@ def pinsWrite(pinName, v):
|
|||||||
else:
|
else:
|
||||||
pinState = wiringpi.LOW
|
pinState = wiringpi.LOW
|
||||||
wiringpi.digitalWrite(PINS[pinName], pinState)
|
wiringpi.digitalWrite(PINS[pinName], pinState)
|
||||||
|
|
||||||
|
@ -6,10 +6,7 @@ import logging
|
|||||||
import json
|
import json
|
||||||
import Converters
|
import Converters
|
||||||
|
|
||||||
|
class DatapointException(Exception): pass
|
||||||
class DatapointException(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractModbusDatapoint(object):
|
class AbstractModbusDatapoint(object):
|
||||||
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, converter=None):
|
def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, converter=None):
|
||||||
@ -43,14 +40,15 @@ class AbstractModbusDatapoint(object):
|
|||||||
self.errorCount, self.readCount, self.writeCount, self.converter))
|
self.errorCount, self.readCount, self.writeCount, self.converter))
|
||||||
|
|
||||||
def jsonify(self):
|
def jsonify(self):
|
||||||
return {'type': self.__class__.__name__,
|
return {'type':self.__class__.__name__,
|
||||||
'args': {k: getattr(self, k) for k in self.argList}
|
'args': { k: getattr(self, k) for k in self.argList }
|
||||||
}
|
}
|
||||||
|
|
||||||
def process(self, client):
|
def process(self, client):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HoldingRegisterDatapoint(AbstractModbusDatapoint):
|
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):
|
publishTopic=None, subscribeTopic=None, feedbackTopic=None, converter=None):
|
||||||
@ -61,6 +59,8 @@ class HoldingRegisterDatapoint(AbstractModbusDatapoint):
|
|||||||
self.feedbackTopic = feedbackTopic
|
self.feedbackTopic = feedbackTopic
|
||||||
self.writeRequestValue = None
|
self.writeRequestValue = None
|
||||||
self.type = 'holding register'
|
self.type = 'holding register'
|
||||||
|
self.lastException = None
|
||||||
|
self.lastReadValue = None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ("[{0!s}, publishTopic: {1}, subscribeTopic: {2}, feedbackTopic: {3}, "
|
return ("[{0!s}, publishTopic: {1}, subscribeTopic: {2}, feedbackTopic: {3}, "
|
||||||
@ -83,6 +83,7 @@ class HoldingRegisterDatapoint(AbstractModbusDatapoint):
|
|||||||
else:
|
else:
|
||||||
values = [int(self.writeRequestValue)]
|
values = [int(self.writeRequestValue)]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
self.lastException = e
|
||||||
raise DatapointException("Exception caught when trying to converter modbus data: {0!s}".format(e))
|
raise DatapointException("Exception caught when trying to converter modbus data: {0!s}".format(e))
|
||||||
result = client.write_registers(address=self.address,
|
result = client.write_registers(address=self.address,
|
||||||
unit=self.unit,
|
unit=self.unit,
|
||||||
@ -98,6 +99,7 @@ class HoldingRegisterDatapoint(AbstractModbusDatapoint):
|
|||||||
unit=self.unit)
|
unit=self.unit)
|
||||||
if type(result) in [ExceptionResponse, ModbusIOException]:
|
if type(result) in [ExceptionResponse, ModbusIOException]:
|
||||||
self.errorCount += 1
|
self.errorCount += 1
|
||||||
|
self.lastException = result
|
||||||
raise DatapointException(result)
|
raise DatapointException(result)
|
||||||
logger.debug("{0}: {1!s}".format(self.label, result.registers))
|
logger.debug("{0}: {1!s}".format(self.label, result.registers))
|
||||||
value = None
|
value = None
|
||||||
@ -108,7 +110,9 @@ class HoldingRegisterDatapoint(AbstractModbusDatapoint):
|
|||||||
else:
|
else:
|
||||||
value = result.registers
|
value = result.registers
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
self.lastException = e
|
||||||
raise DatapointException("Exception caught when trying to converter modbus data: {0!s}".format(e))
|
raise DatapointException("Exception caught when trying to converter modbus data: {0!s}".format(e))
|
||||||
|
self.lastReadValue = value
|
||||||
if self.publishTopic:
|
if self.publishTopic:
|
||||||
pubQueue.put(MqttProcessor.PublishItem(self.publishTopic, str(value)))
|
pubQueue.put(MqttProcessor.PublishItem(self.publishTopic, str(value)))
|
||||||
self.lastContact = datetime.datetime.now()
|
self.lastContact = datetime.datetime.now()
|
||||||
@ -118,10 +122,11 @@ class HoldingRegisterDatapoint(AbstractModbusDatapoint):
|
|||||||
|
|
||||||
|
|
||||||
class CoilDatapoint(AbstractModbusDatapoint):
|
class CoilDatapoint(AbstractModbusDatapoint):
|
||||||
def __init__(self, label=None, unit=None, address=None, scanRate=None, subscribeTopic=None,
|
def __init__(self, label=None, unit=None, address=None, scanRate=None, publishTopic=None, subscribeTopic=None,
|
||||||
feedbackTopic=None):
|
feedbackTopic=None):
|
||||||
super().__init__(label, unit, address, 1, scanRate, None)
|
super().__init__(label, unit, address, 1, scanRate, None)
|
||||||
self.argList = ['label', 'unit', 'address', 'scanRate', 'subscribeTopic', 'feedbackTopic']
|
self.argList = ['label', 'unit','address','scanRate','publishTopic', 'subscribeTopic', 'feedbackTopic']
|
||||||
|
self.publishTopic = publishTopic
|
||||||
self.subscribeTopic = subscribeTopic
|
self.subscribeTopic = subscribeTopic
|
||||||
self.feedbackTopic = feedbackTopic
|
self.feedbackTopic = feedbackTopic
|
||||||
self.writeRequestValue = None
|
self.writeRequestValue = None
|
||||||
@ -130,11 +135,11 @@ class CoilDatapoint(AbstractModbusDatapoint):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ("{0}, {1}: unit: {2}, address: {3}, scanRate: {4}, "
|
return ("{0}, {1}: unit: {2}, address: {3}, scanRate: {4}, "
|
||||||
"enqueued: {5}, lastContact: {6}, errorCount: {7}, readCount: {8}, "
|
"enqueued: {5}, lastContact: {6}, errorCount: {7}, readCount: {8}, "
|
||||||
"writeCount: {9}, subscribeTopic: {10}, feedbackTopic: {11}"
|
"writeCount: {9}, publishTopic: {10}, subscribeTopic: {11}, feedbackTopic: {12}"
|
||||||
.format(self.type, self.label, self.unit, self.address,
|
.format(self.type, self.label, self.unit, self.address,
|
||||||
self.scanRate, self.enqueued, self.lastContact,
|
self.scanRate, self.enqueued, self.lastContact,
|
||||||
self.errorCount, self.readCount, self.writeCount,
|
self.errorCount, self.readCount, self.writeCount,
|
||||||
self.subscribeTopic, self.feedbackTopic))
|
self.publishTopic, self.subscribeTopic, self.feedbackTopic))
|
||||||
|
|
||||||
def onMessage(self, value):
|
def onMessage(self, value):
|
||||||
self.writeRequestValue = value.decode()
|
self.writeRequestValue = value.decode()
|
||||||
@ -146,7 +151,7 @@ class CoilDatapoint(AbstractModbusDatapoint):
|
|||||||
logger.debug("Coil, perform write operation")
|
logger.debug("Coil, perform write operation")
|
||||||
self.writeCount += 1
|
self.writeCount += 1
|
||||||
logger.debug("{0}: raw: {1!s}".format(self.label, self.writeRequestValue))
|
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']:
|
if self.writeRequestValue in ['true', 'True', 'yes', 'Yes', 'On', 'on']:
|
||||||
value = True
|
value = True
|
||||||
elif self.writeRequestValue in ['false', 'False', 'no', 'No', 'Off', 'off']:
|
elif self.writeRequestValue in ['false', 'False', 'no', 'No', 'Off', 'off']:
|
||||||
@ -158,17 +163,25 @@ class CoilDatapoint(AbstractModbusDatapoint):
|
|||||||
unit=self.unit,
|
unit=self.unit,
|
||||||
value=value)
|
value=value)
|
||||||
logger.debug("Write result: {0!s}".format(result))
|
logger.debug("Write result: {0!s}".format(result))
|
||||||
if self.feedbackTopic:
|
|
||||||
pubQueue.put(MqttProcessor.PublishItem(self.feedbackTopic, str(value)))
|
|
||||||
self.writeRequestValue = None
|
self.writeRequestValue = None
|
||||||
else:
|
else:
|
||||||
# no write op, strange
|
# perform read operation
|
||||||
logger.debug("Coil, process call but no write value available, strange")
|
logger.debug("Coil, perform read operation")
|
||||||
|
self.readCount += 1
|
||||||
|
result = client.read_coils(address=self.address,
|
||||||
|
unit=self.unit)
|
||||||
|
if type(result) in [ExceptionResponse, ModbusIOException]:
|
||||||
|
self.errorCount += 1
|
||||||
|
raise DatapointException(result)
|
||||||
|
logger.debug("{0}: {1!s}".format(self.label, result.getBit(0)))
|
||||||
|
value = result.getBit(0)
|
||||||
|
if self.publishTopic:
|
||||||
|
pubQueue.put(MqttProcessor.PublishItem(self.publishTopic, str(value)))
|
||||||
|
self.lastContact = datetime.datetime.now()
|
||||||
|
|
||||||
|
|
||||||
class ReadOnlyDatapoint(AbstractModbusDatapoint):
|
class ReadOnlyDatapoint(AbstractModbusDatapoint):
|
||||||
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):
|
||||||
publishTopic=None, converter=None):
|
|
||||||
super().__init__(label, unit, address, count, scanRate, converter)
|
super().__init__(label, unit, address, count, scanRate, converter)
|
||||||
self.argList = self.argList + ['updateOnly', 'publishTopic']
|
self.argList = self.argList + ['updateOnly', 'publishTopic']
|
||||||
self.updateOnly = updateOnly
|
self.updateOnly = updateOnly
|
||||||
@ -181,6 +194,7 @@ class ReadOnlyDatapoint(AbstractModbusDatapoint):
|
|||||||
self.lastValue))
|
self.lastValue))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InputRegisterDatapoint(ReadOnlyDatapoint):
|
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):
|
publishTopic=None, converter=None):
|
||||||
@ -245,11 +259,12 @@ class DiscreteInputDatapoint(ReadOnlyDatapoint):
|
|||||||
self.lastValues[i] = result.getBit(i)
|
self.lastValues[i] = result.getBit(i)
|
||||||
logger.debug("{0}, {1}: changed: {2!s}".format(self.label, i, result.getBit(i)))
|
logger.debug("{0}, {1}: changed: {2!s}".format(self.label, i, result.getBit(i)))
|
||||||
if self.publishTopic:
|
if self.publishTopic:
|
||||||
pubQueue.put(MqttProcessor.PublishItem("{0}/{1}"
|
pubQueue.put(MqttProcessor.PublishItem("{0}/{1}".format(self.publishTopic, i), str(result.getBit(i))))
|
||||||
.format(self.publishTopic, i), str(result.getBit(i))))
|
|
||||||
self.lastContact = datetime.datetime.now()
|
self.lastContact = datetime.datetime.now()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class JsonifyEncoder(json.JSONEncoder):
|
class JsonifyEncoder(json.JSONEncoder):
|
||||||
def default(self, o):
|
def default(self, o):
|
||||||
res = None
|
res = None
|
||||||
@ -262,7 +277,6 @@ class JsonifyEncoder(json.JSONEncoder):
|
|||||||
res = super().default(o)
|
res = super().default(o)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def datapointObjectHook(j):
|
def datapointObjectHook(j):
|
||||||
if type(j) == dict and 'type' in j and 'args' in j:
|
if type(j) == dict and 'type' in j and 'args' in j:
|
||||||
klass = eval(j['type'])
|
klass = eval(j['type'])
|
||||||
@ -271,15 +285,14 @@ def datapointObjectHook(j):
|
|||||||
else:
|
else:
|
||||||
return j
|
return j
|
||||||
|
|
||||||
|
|
||||||
def saveRegisterList(registerList, registerListFile):
|
def saveRegisterList(registerList, registerListFile):
|
||||||
js = json.dumps(registerList, cls=JsonifyEncoder, sort_keys=True, indent=4)
|
js = json.dumps(registerList, cls=JsonifyEncoder, sort_keys=True, indent=4)
|
||||||
with open(registerListFile, 'w') as f:
|
with open(registerListFile, 'w') as f:
|
||||||
f.write(js)
|
f.write(js)
|
||||||
|
|
||||||
|
|
||||||
def loadRegisterList(registerListFile):
|
def loadRegisterList(registerListFile):
|
||||||
with open(registerListFile, 'r') as f:
|
with open(registerListFile, 'r') as f:
|
||||||
js = f.read()
|
js = f.read()
|
||||||
registerList = json.loads(js, object_hook=datapointObjectHook)
|
registerList = json.loads(js, object_hook=datapointObjectHook)
|
||||||
return registerList
|
return registerList
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ import datetime
|
|||||||
from NotificationForwarder import AbstractNotificationReceiver
|
from NotificationForwarder import AbstractNotificationReceiver
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
class ScanRateConsideringQueueFeeder(threading.Thread, AbstractNotificationReceiver):
|
class ScanRateConsideringQueueFeeder(threading.Thread, AbstractNotificationReceiver):
|
||||||
def __init__(self, config, registers, queue):
|
def __init__(self, config, registers, queue):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -30,7 +29,7 @@ class ScanRateConsideringQueueFeeder(threading.Thread, AbstractNotificationRecei
|
|||||||
((not r.lastContact) or
|
((not r.lastContact) or
|
||||||
(r.lastContact + r.scanRate < datetime.datetime.now())))
|
(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:
|
for r in registersToBeHandled:
|
||||||
self.queue.put(r)
|
self.queue.put(r)
|
||||||
r.enqueued = True
|
r.enqueued = True
|
||||||
|
@ -4,14 +4,12 @@ import pickle
|
|||||||
|
|
||||||
|
|
||||||
datapoints = [
|
datapoints = [
|
||||||
RegisterDatapoint.InputRegisterDatapoint('Temperature', 5, 0x0001, 1,
|
RegisterDatapoint.InputRegisterDatapoint('Temperature', 5, 0x0001, 1, datetime.timedelta(seconds=1.0), False, 'Pub/Temperature'),
|
||||||
datetime.timedelta(seconds=1.0), False, 'Pub/Temperature'),
|
RegisterDatapoint.InputRegisterDatapoint('Humidity', 5, 0x0002, 1, datetime.timedelta(seconds=1.0), True, 'Pub/Humidity'),
|
||||||
RegisterDatapoint.InputRegisterDatapoint('Humidity', 5, 0x0002, 1,
|
RegisterDatapoint.DiscreteInputDatapoint('Switches', 4, 0x0000, 1, datetime.timedelta(seconds=1.0), True, 'Pub/Switches'),
|
||||||
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:
|
with open('registers.pkl', 'wb') as f:
|
||||||
pickle.dump(datapoints, f)
|
pickle.dump(datapoints, f)
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ if __name__ == "__main__":
|
|||||||
config = Config.Config()
|
config = Config.Config()
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.DEBUG)
|
||||||
fh = logging.FileHandler(config.logFile)
|
fh = logging.FileHandler(config.logFile)
|
||||||
fh.setLevel(logging.INFO)
|
fh.setLevel(logging.DEBUG)
|
||||||
ch = logging.StreamHandler()
|
ch = logging.StreamHandler()
|
||||||
ch.setLevel(logging.ERROR)
|
ch.setLevel(logging.ERROR)
|
||||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
@ -36,6 +36,7 @@ if __name__ == "__main__":
|
|||||||
nf = NotificationForwarder.NotificationForwarder()
|
nf = NotificationForwarder.NotificationForwarder()
|
||||||
logger.debug('infrastructure prepared')
|
logger.debug('infrastructure prepared')
|
||||||
|
|
||||||
|
|
||||||
datapoints = RegisterDatapoint.loadRegisterList(config.registerFile)
|
datapoints = RegisterDatapoint.loadRegisterList(config.registerFile)
|
||||||
logger.debug('datapoints read')
|
logger.debug('datapoints read')
|
||||||
|
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
[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
|
|
||||||
|
|
||||||
|
|
@ -32,57 +32,77 @@
|
|||||||
"converter": null,
|
"converter": null,
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"label": "Switches",
|
"label": "Switches",
|
||||||
"publishTopic": "IoT/Status/Modbus2/Switches",
|
"publishTopic": "Pub/Switches",
|
||||||
"scanRate": 0.5,
|
"scanRate": 0.5,
|
||||||
"unit": 4,
|
"unit": 4,
|
||||||
"updateOnly": true
|
"updateOnly": false
|
||||||
},
|
},
|
||||||
"type": "DiscreteInputDatapoint"
|
"type": "DiscreteInputDatapoint"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"args": {
|
"args": {
|
||||||
"address": 0,
|
"address": 40010,
|
||||||
"feedbackTopic": "IoT/Feedback/Modbus2/Coils/0",
|
"converter": "uint32",
|
||||||
"label": "Coil0",
|
"count": 2,
|
||||||
"publishTopic": "IoT/Status/Modbus2/Coils/0",
|
"feedbackTopic": "FB/Counter1",
|
||||||
"scanRate": 0.0,
|
"label": "Counter1",
|
||||||
"subscribeTopic": "IoT/Action/Modbus2/Coils/0",
|
"publishTopic": "Pub/Counter1",
|
||||||
|
"scanRate": 1.0,
|
||||||
|
"subscribeTopic": "Sub/Counter1",
|
||||||
"unit": 4
|
"unit": 4
|
||||||
},
|
},
|
||||||
"type": "CoilDatapoint"
|
"type": "HoldingRegisterDatapoint"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"args": {
|
"args": {
|
||||||
"address": 1,
|
"address": 40012,
|
||||||
"feedbackTopic": "IoT/Feedback/Modbus2/Coils/1",
|
"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",
|
"label": "Coil1",
|
||||||
"publishTopic": "IoT/Status/Modbus2/Coils/1",
|
"publishTopic": "Pub/Coil1",
|
||||||
"scanRate": 0.0,
|
"scanRate": 1.0,
|
||||||
"subscribeTopic": "IoT/Action/Modbus2/Coils/1",
|
"subscribeTopic": "Sub/Coil1",
|
||||||
"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
|
"unit": 4
|
||||||
},
|
},
|
||||||
"type": "CoilDatapoint"
|
"type": "CoilDatapoint"
|
||||||
@ -90,27 +110,13 @@
|
|||||||
{
|
{
|
||||||
"args": {
|
"args": {
|
||||||
"address": 1,
|
"address": 1,
|
||||||
"converter": "fix1twos",
|
"feedbackTopic": "FB/Coil2",
|
||||||
"count": 1,
|
"label": "Coil2",
|
||||||
"label": "wago1",
|
"publishTopic": "Pub/Coil2",
|
||||||
"publishTopic": "IoT/Measurement/Modbus2/Wago1",
|
|
||||||
"scanRate": 1.0,
|
"scanRate": 1.0,
|
||||||
"unit": 11,
|
"subscribeTopic": "Sub/Coil2",
|
||||||
"updateOnly": false
|
"unit": 4
|
||||||
},
|
},
|
||||||
"type": "InputRegisterDatapoint"
|
"type": "CoilDatapoint"
|
||||||
},
|
|
||||||
{
|
|
||||||
"args": {
|
|
||||||
"address": 0,
|
|
||||||
"converter": "fix1twos",
|
|
||||||
"count": 1,
|
|
||||||
"label": "Freezer",
|
|
||||||
"publishTopic": "IoT/Measurement/Modbus2/Freezer",
|
|
||||||
"scanRate": 1.0,
|
|
||||||
"unit": 11,
|
|
||||||
"updateOnly": false
|
|
||||||
},
|
|
||||||
"type": "InputRegisterDatapoint"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
@ -10,12 +10,15 @@ with open('registers.pkl', 'rb') as f:
|
|||||||
newDatapoints = []
|
newDatapoints = []
|
||||||
for dp in datapoints:
|
for dp in datapoints:
|
||||||
ndp = type(dp)()
|
ndp = type(dp)()
|
||||||
for k, v in dp.__dict__.items():
|
for k,v in dp.__dict__.items():
|
||||||
if k != 'logger':
|
if k != 'logger':
|
||||||
ndp.__dict__[k] = v
|
ndp.__dict__[k] = v
|
||||||
newDatapoints.append(ndp)
|
newDatapoints.append(ndp)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
js = json.dumps(newDatapoints, cls=RegisterDatapoint.JsonifyEncoder, sort_keys=True, indent=4)
|
js = json.dumps(newDatapoints, cls=RegisterDatapoint.JsonifyEncoder, sort_keys=True, indent=4)
|
||||||
print(js)
|
print(js)
|
||||||
|
|
||||||
|
|
||||||
RegisterDatapoint.saveRegisterList(newDatapoints, 'registers.json')
|
RegisterDatapoint.saveRegisterList(newDatapoints, 'registers.json')
|
Reference in New Issue
Block a user