From d97b7469fec2bdd58aa6fd1b6e093a5fef553cac Mon Sep 17 00:00:00 2001 From: Wolfgang Hottgenroth Date: Tue, 16 Jul 2019 14:11:04 +0200 Subject: [PATCH] jsonifying register file done so far --- snippets/test9.py | 14 +++++ src/CmdServer.py | 12 +--- src/RegisterDatapoint.py | 82 +++++++++++++----------- src/loadRegisterFile.py | 6 ++ src/registers.json | 129 ++++++++++++++++++++++++++++++++++++++ src/updateRegisterFile.py | 11 ++-- 6 files changed, 203 insertions(+), 51 deletions(-) create mode 100644 snippets/test9.py create mode 100644 src/loadRegisterFile.py create mode 100644 src/registers.json diff --git a/snippets/test9.py b/snippets/test9.py new file mode 100644 index 0000000..b0982e9 --- /dev/null +++ b/snippets/test9.py @@ -0,0 +1,14 @@ +class A(object): + def __init__(self): + self.a = 1 + def x(self): + return self.a + +class B(A): + def __init__(self): + self.a = 2 + def x(self): + return self.a + def y(self): + return super().x() + diff --git a/src/CmdServer.py b/src/CmdServer.py index 955f3e6..17cbd02 100644 --- a/src/CmdServer.py +++ b/src/CmdServer.py @@ -309,23 +309,17 @@ class CmdInterpreter(cmd.Cmd): self.__println("DO NOT FORGET TO SAVE AFTERWARDS!") def do_save(self, arg): - for i in self.registers: - self.logger.warn("{0!s}: {1!s}".format(i, i.__dict__)) - with open(self.config.registerFile, 'wb') as f: - pickle.dump(self.registers, f) + RegisterDatapoint.saveRegisterFile(self.registers, self.config.registerFile) def help_save(self): self.__println("Usage: save") self.__println("Saves a modified register list into the register file.") def do_load(self, arg): - registers = None - with open(self.config.registerFile, 'rb') as f: - registers = pickle.load(f) try: - RegisterDatapoint.checkRegisterList(registers) + registers = RegisterDatapoint.loadRegisterList(self.config.registerFile) self.registers = registers - except ValueError as e: + except Exception as e: self.__println("Unable to load register list: {0!s}".format(e)) def help_load(self): diff --git a/src/RegisterDatapoint.py b/src/RegisterDatapoint.py index b40cb88..a230278 100644 --- a/src/RegisterDatapoint.py +++ b/src/RegisterDatapoint.py @@ -3,18 +3,45 @@ from pymodbus.pdu import ExceptionResponse from pymodbus.exceptions import ModbusIOException import MqttProcessor import logging +import json import pickle +class JsonifyEncoder(json.JSONEncoder): + def default(self, o): + res = None + try: + res = o.jsonify() + except (TypeError, AttributeError): + if type(o) == datetime.timedelta: + res = o.total_seconds() + else: + 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']) + o = klass(**j['args']) + return o + else: + return j + + + class DatapointException(Exception): pass class AbstractModbusDatapoint(object): def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None): + self.argList = ['label', 'unit', 'address', 'count', 'scanRate'] self.label = label self.unit = unit self.address = address self.count = count - self.scanRate = scanRate + if type(scanRate) == float: + self.scanRate = datetime.timedelta(seconds=scanRate) + else: + self.scanRate = scanRate self.type = 'abstract data point' self.enqueued = False self.lastContact = None @@ -32,14 +59,21 @@ class AbstractModbusDatapoint(object): self.scanRate, self.enqueued, self.lastContact, self.errorCount, self.processCount)) + def jsonify(self): + 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, publishTopic=None, subscribeTopic=None, feedbackTopic=None): super().__init__(label, unit, address, count, scanRate) + self.argList = self.argList + ['publishTopic', 'subscribeTopic', 'feedbackTopic'] self.publishTopic = publishTopic self.subscribeTopic = subscribeTopic self.feedbackTopic = feedbackTopic @@ -79,6 +113,7 @@ class HoldingRegisterDatapoint(AbstractModbusDatapoint): class ReadOnlyDatapoint(AbstractModbusDatapoint): def __init__(self, label=None, unit=None, address=None, count=None, scanRate=None, updateOnly=None, publishTopic=None): super().__init__(label, unit, address, count, scanRate) + self.argList = self.argList + ['updateOnly', 'publishTopic'] self.updateOnly = updateOnly self.lastValue = None self.publishTopic = publishTopic @@ -137,39 +172,14 @@ class DiscreteInputDatapoint(ReadOnlyDatapoint): -def loadRegisterList(registerList): - # Load, check and auto-update registers file - - with open(registerList, 'rb') as f: - datapoints = pickle.load(f) - - checkRegisterList(datapoints) - - newDatapoints = [] - for dp in datapoints: - ndp = type(dp)() - for k,v in dp.__dict__.items(): - ndp.__dict__[k] = v - newDatapoints.append(ndp) - logging.getLogger('loadRegisterList').debug("Datapoint loaded: {0!s}".format(ndp)) - - checkRegisterList(newDatapoints, reset=True) - - with open(registerList, 'wb') as f: - pickle.dump(newDatapoints, f) - - return newDatapoints - - -def checkRegisterList(registers, reset=False): - for r in registers: - if not isinstance(r, AbstractModbusDatapoint): - raise ValueError('Entry in register list {0!s} is not derived from class AbstractModbusDatapoint'.format(r)) - else: - if reset: - r.errorCount = 0 - r.processCount = 0 - r.enqueued = False - - +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 diff --git a/src/loadRegisterFile.py b/src/loadRegisterFile.py new file mode 100644 index 0000000..7a18791 --- /dev/null +++ b/src/loadRegisterFile.py @@ -0,0 +1,6 @@ +import RegisterDatapoint + +registers = RegisterDatapoint.loadRegisterList('registers.json') + +for r in registers: + print("{0!s}".format(r)) \ No newline at end of file diff --git a/src/registers.json b/src/registers.json new file mode 100644 index 0000000..44ae397 --- /dev/null +++ b/src/registers.json @@ -0,0 +1,129 @@ +[ + { + "args": { + "address": 1, + "count": 1, + "label": "Temperature", + "publishTopic": "Pub/Temperature", + "scanRate": 60.0, + "unit": 5, + "updateOnly": false + }, + "type": "InputRegisterDatapoint" + }, + { + "args": { + "address": 2, + "count": 1, + "label": "Humidity", + "publishTopic": "Pub/Humidity", + "scanRate": null, + "unit": 5, + "updateOnly": false + }, + "type": "InputRegisterDatapoint" + }, + { + "args": { + "address": 0, + "count": 1, + "label": "Switches", + "publishTopic": "Pub/Switches", + "scanRate": 60.0, + "unit": 4, + "updateOnly": false + }, + "type": "DiscreteInputDatapoint" + }, + { + "args": { + "address": 40010, + "count": 2, + "feedbackTopic": null, + "label": "Counter1", + "publishTopic": "Pub/Counter1", + "scanRate": 60.0, + "subscribeTopic": null, + "unit": 4 + }, + "type": "HoldingRegisterDatapoint" + }, + { + "args": { + "address": 40012, + "count": 2, + "feedbackTopic": null, + "label": "Counter2", + "publishTopic": "Pub/Counter2", + "scanRate": 60.0, + "subscribeTopic": null, + "unit": 4 + }, + "type": "HoldingRegisterDatapoint" + }, + { + "args": { + "address": 40014, + "count": 2, + "feedbackTopic": null, + "label": "Counter3", + "publishTopic": "Pub/Counter3", + "scanRate": 60.0, + "subscribeTopic": null, + "unit": 4 + }, + "type": "HoldingRegisterDatapoint" + }, + { + "args": { + "address": 40016, + "count": 2, + "feedbackTopic": null, + "label": "Counter4", + "publishTopic": "Pub/Counter4", + "scanRate": 60.0, + "subscribeTopic": null, + "unit": 4 + }, + "type": "HoldingRegisterDatapoint" + }, + { + "args": { + "address": 40001, + "count": 1, + "feedbackTopic": null, + "label": "Version", + "publishTopic": "Pub/Version", + "scanRate": 1.0, + "subscribeTopic": null, + "unit": 4 + }, + "type": "HoldingRegisterDatapoint" + }, + { + "args": { + "address": 40001, + "count": 1, + "feedbackTopic": null, + "label": "Version", + "publishTopic": "Pub/Version", + "scanRate": 10.0, + "subscribeTopic": null, + "unit": 4 + }, + "type": "HoldingRegisterDatapoint" + }, + { + "args": { + "address": 40001, + "count": 1, + "feedbackTopic": "FB/Version", + "label": "Version_W", + "publishTopic": null, + "scanRate": 0.0, + "subscribeTopic": "Sub/Version", + "unit": 4 + }, + "type": "HoldingRegisterDatapoint" + } +] \ No newline at end of file diff --git a/src/updateRegisterFile.py b/src/updateRegisterFile.py index 2c4b7e8..39d7b5c 100644 --- a/src/updateRegisterFile.py +++ b/src/updateRegisterFile.py @@ -1,14 +1,12 @@ import datetime import RegisterDatapoint import pickle - +import json with open('registers.pkl', 'rb') as f: datapoints = pickle.load(f) -RegisterDatapoint.checkRegisterList(datapoints, reset=True) - newDatapoints = [] for dp in datapoints: ndp = type(dp)() @@ -17,9 +15,10 @@ for dp in datapoints: ndp.__dict__[k] = v newDatapoints.append(ndp) -RegisterDatapoint.checkRegisterList(newDatapoints, reset=True) -with open('registers.pkl', 'wb') as f: - pickle.dump(newDatapoints, f) +js = json.dumps(newDatapoints, cls=RegisterDatapoint.JsonifyEncoder, sort_keys=True, indent=4) +print(js) + +RegisterDatapoint.saveRegisterList(newDatapoints, 'registers.json') \ No newline at end of file