484 lines
26 KiB
Python
484 lines
26 KiB
Python
# -*- coding: iso-8859-15 -*-
|
|
|
|
|
|
'''
|
|
Created on 11.06.2015
|
|
|
|
@author: wn
|
|
'''
|
|
|
|
import MeterbusTypeConversion
|
|
from MeterbusLibExceptions import *
|
|
import struct
|
|
|
|
class Device(object):
|
|
def __init__(self, address, category, comment, dataItems):
|
|
self.address = address
|
|
self.category = category
|
|
self.comment = comment
|
|
self.dataItems = dataItems
|
|
|
|
class Frame(object):
|
|
def __init__(self, startCharacter, frameLength, firstPayload, telegram):
|
|
self.telegram = telegram
|
|
self.frameName = self.__class__.__name__
|
|
self.frameLength = frameLength
|
|
self.payloadLength = self.frameLength - 6
|
|
self.frameStartCharacter = startCharacter
|
|
self.firstPayload = firstPayload
|
|
|
|
def parse(self):
|
|
if self.telegram[0] != self.frameStartCharacter:
|
|
raise InvalidStartCharException()
|
|
if len(self.telegram) != self.frameLength:
|
|
raise InvalidLengthException()
|
|
self.verifyChecksumAndEnd()
|
|
self.parse2()
|
|
|
|
def verifyChecksumAndEnd(self):
|
|
if (self.firstPayload != 0):
|
|
checksum = sum(self.telegram[self.firstPayload:-2]) & 0xff
|
|
if checksum != self.telegram[-2]:
|
|
raise InvalidChecksumException()
|
|
if self.telegram[-1] != 0x16:
|
|
raise InvalidStopCharException()
|
|
|
|
def parse2(self):
|
|
raise NotImplementedError
|
|
|
|
def getJSON(self):
|
|
raise NotImplementedError
|
|
|
|
|
|
class SingleCharacter(Frame):
|
|
def __init__(self, telegram):
|
|
super(SingleCharacter, self).__init__(0xe5, 1, 0, telegram)
|
|
|
|
def parse2(self):
|
|
pass
|
|
|
|
def getJSON(self):
|
|
j = {}
|
|
return j
|
|
|
|
|
|
class ShortFrame(Frame):
|
|
def __init__(self, telegram):
|
|
super(ShortFrame, self).__init__(0x10, 5, 1, telegram)
|
|
|
|
def parse2(self):
|
|
self.cField = self.telegram[1]
|
|
self.address = self.telegram[2]
|
|
|
|
def getJSON(self):
|
|
j = {'c': self.cField, 'a': self.address}
|
|
return j
|
|
|
|
|
|
class ControlFrame(Frame):
|
|
def __init__(self, telegram):
|
|
super(ControlFrame, self).__init__(0x68, (telegram[1] + 6), 4, telegram)
|
|
|
|
def parse2(self):
|
|
if self.telegram[2] != self.payloadLength:
|
|
raise InvalidSecondLengthException
|
|
if self.payloadLength < 3:
|
|
raise PayloadTooShortException("too short for c, a, ci fields")
|
|
self.cField = self.telegram[4]
|
|
self.address = self.telegram[5]
|
|
self.ciField = self.telegram[6]
|
|
|
|
def getJSON(self):
|
|
j = {'c': self.cField, 'a': self.address, 'ci': self.ciField}
|
|
return j
|
|
|
|
|
|
class LongFrame(ControlFrame):
|
|
def __init__(self, telegram, devices=[]):
|
|
super(LongFrame, self).__init__(telegram)
|
|
self.devices = devices
|
|
if self.devices is not None:
|
|
for device in self.devices:
|
|
if not isinstance(device, Device):
|
|
raise InvalidDevicesStructureException()
|
|
|
|
class FixedDataHeader(object):
|
|
def __init__(self, data):
|
|
self.data = data
|
|
|
|
def parse(self):
|
|
self.identNr = MeterbusTypeConversion.bcd(self.data[:4])
|
|
self.manufacturer = MeterbusTypeConversion.manufCode(self.data[4:6])
|
|
self.version = self.data[6]
|
|
self.medium = MeterbusTypeConversion.mediumCode(self.data[7])
|
|
self.accessNo = self.data[8]
|
|
self.status = self.data[9]
|
|
self.signature = [s for s in self.data[10:]]
|
|
if self.signature[0] != 0 or self.signature[1] != 0:
|
|
raise InvalidFrameException("signature should be zero")
|
|
#print("FixedDataHeader: " + str(self.getJSON()))
|
|
|
|
def getJSON(self):
|
|
j = {'identNr': self.identNr, 'manufacturer': self.manufacturer, 'version': self.version, 'medium': self.medium,
|
|
'accessNo': self.accessNo, 'status': self.status, 'signature': self.signature}
|
|
return j
|
|
|
|
class DataInformationElement(object):
|
|
def __init__(self, data, comment):
|
|
self.data = data
|
|
self.comment = comment
|
|
self.consumed = 0
|
|
|
|
@classmethod
|
|
def create(cls, data, comment):
|
|
if data[0] != 0x0f:
|
|
return LongFrame.DataInformationBlock(data, comment)
|
|
else:
|
|
return LongFrame.ManufacturerSpecificDataBlock(data, comment)
|
|
|
|
def getJSON(self):
|
|
j = {'comment': self.comment}
|
|
return j
|
|
|
|
|
|
class ManufacturerSpecificDataBlock(DataInformationElement):
|
|
def parse(self):
|
|
self.dif = self.data[0]
|
|
self.mdh = [ m for m in self.data[1:]]
|
|
self.consumed = len(self.data)
|
|
return self.consumed
|
|
|
|
def getJSON(self):
|
|
superJSON = super(LongFrame.ManufacturerSpecificDataBlock, self).getJSON()
|
|
j = {'dif': self.dif, 'mdh': self.mdh}
|
|
j.update(superJSON)
|
|
return j
|
|
|
|
class DataInformationBlock(DataInformationElement):
|
|
DATA_FIELD_CODES = (
|
|
('No Data', 0, None),
|
|
('8 Bit Integer', 1, lambda x: x[0]),
|
|
('16 Bit Integer', 2, lambda x: (x[1] << 8) + x[0]),
|
|
('24 Bit Integer', 3, lambda x: (x[2] << 16) + (x[1] << 8) + x[0]),
|
|
('32 Bit Integer', 4, lambda x: (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]),
|
|
('32 Bit Real', 4, lambda x: struct.unpack('f', str(bytearray(x)))[0]),
|
|
('48 Bit Integer', 6, lambda x: (x[5] << 40) + (x[4] << 32) + (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]),
|
|
('64 Bit Integer', 8, lambda x: (x[7] << 56) + (x[6] << 48) + (x[5] << 40) + (x[4] << 32) + (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]),
|
|
('Selection for Readout', 0, None),
|
|
('2 Digit BCD', 1, lambda x: MeterbusTypeConversion.bcd(x)),
|
|
('4 Digit BCD', 2, lambda x: MeterbusTypeConversion.bcd(x)),
|
|
('6 Digit BCD', 3, lambda x: MeterbusTypeConversion.bcd(x)),
|
|
('8 Digit BCD', 4, lambda x: MeterbusTypeConversion.bcd(x)),
|
|
('variable length', -1, None),
|
|
('12 Digit BCD', 6, lambda x: MeterbusTypeConversion.bcd(x)),
|
|
('Special Function', 0, None),
|
|
)
|
|
VIF_CODES = (
|
|
(0b01111000, 0b00000000, 'Energy', 'Wh', 0b00000111, lambda d: 10**(d-3)),
|
|
(0b01111000, 0b00001000, 'Energy', 'J', 0b00000111, lambda d: 10**d),
|
|
(0b01111000, 0b00010000, 'Volume', 'm**3', 0b00000111, lambda d: 10**(d-6)),
|
|
(0b01111000, 0b00011000, 'Mass', 'kg', 0b00000111, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b00100000, 'On Time', 'TimeCalc', 0b00000011, None),
|
|
(0b01111100, 0b00100100, 'Operating Time', 'TimeCalc', 0b00000011, None),
|
|
(0b01111000, 0b00101000, 'Power', 'W', 0b00000111, lambda d: 10**(d-3)),
|
|
(0b01111000, 0b00110000, 'Power', 'J/h', 0b00000111, lambda d: 10**d),
|
|
(0b01111000, 0b00111000, 'Volume Flow', 'm**3/h', 0b00000111, lambda d: 10**(d-6)),
|
|
(0b01111000, 0b01000000, 'Volume Flow ext.', 'm**3/min', 0b00000111, lambda d: 10**(d-7)),
|
|
(0b01111000, 0b01001000, 'Volume Flow ext.', 'm**3/s', 0b00000111, lambda d: 10**(d-9)),
|
|
(0b01111000, 0b01010000, 'Mass Flow', 'kg/h', 0b00000111, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b01011000, 'Flow Temperature', 'Celsius', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b01011100, 'Return Temperature', 'Celsius', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b01100000, 'Temperature Diff.', 'Kelvin', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b01100100, 'Extern. Temp', 'Celsius', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b01101000, 'Pressure', 'bar', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111110, 0b01101100, 'Time Point', 'TimePointCalc', 0b00000001,None),
|
|
(0b01111111, 0b01101110, 'Units for H.C.A.', '', 0, None),
|
|
(0b01111111, 0b01101111, 'Reserved', '', 0, None),
|
|
(0b01111100, 0b01110000, 'Averaging Duration', 'TimeCalc', 0b00000011, None),
|
|
(0b01111100, 0b01110100, 'Actuality Duration', 'TimeCalc', 0b00000011, None),
|
|
(0b01111111, 0b01111000, 'Fabrication Number', '', 0, None),
|
|
(0b01111111, 0b01111001, 'Enhanced Ident.', '', 0, None),
|
|
(0b01111111, 0b01111010, 'Bus Address', '', 0, None),
|
|
(0b11111111, 0b11111011, 'Extensions 1', 'Table8.4.4b', 0, None),
|
|
(0b11111111, 0b11111101, 'Extensions 2', 'Table8.4.4a', 0, None),
|
|
)
|
|
|
|
TABLE844a = (
|
|
(0b01111100, 0b00000000, 'Credit', 'Currency Unit', 0b00000011, lambda d: 10*(d-3)),
|
|
(0b01111100, 0b00000100, 'Debit', 'Currency Unit', 0b00000011, lambda d: 10*(d-3)),
|
|
(0b01111111, 0b00001000, 'Access Number', '', 0, None),
|
|
(0b01111111, 0b00001001, 'Medium', '', 0, None),
|
|
(0b01111111, 0b00001010, 'Manufacturer', '', 0, None),
|
|
(0b01111111, 0b00001011, 'Parameter set ident','', 0, None),
|
|
(0b01111111, 0b00001100, 'Model / Version', '', 0, None),
|
|
(0b01111111, 0b00001101, 'Hardware Version', '', 0, None),
|
|
(0b01111111, 0b00001110, 'Firmware Version', '', 0, None),
|
|
(0b01111111, 0b00001111, 'Software Version', '', 0, None),
|
|
(0b01111111, 0b00010000, 'Customer Location', '', 0, None),
|
|
(0b01111111, 0b00010001, 'Customer', '', 0, None),
|
|
(0b01111111, 0b00010010, 'Access Code User', '', 0, None),
|
|
(0b01111111, 0b00010011, 'Access Code Operator','', 0, None),
|
|
(0b01111111, 0b00010100, 'Access Code System Operator','', 0, None),
|
|
(0b01111111, 0b00010101, 'Access Code Developer','', 0, None),
|
|
(0b01111111, 0b00010110, 'Password', '', 0, None),
|
|
(0b01111111, 0b00010111, 'Error Flags', '', 0, None),
|
|
(0b01111111, 0b00011000, 'Error Mask', '', 0, None),
|
|
(0b01111111, 0b00011001, 'Reserved', '', 0, None),
|
|
(0b01111111, 0b00011010, 'Digital Output', '', 0, None),
|
|
(0b01111111, 0b00011011, 'Digital Input', '', 0, None),
|
|
(0b01111111, 0b00011100, 'Baudrate', 'Baud', 0, None),
|
|
(0b01111111, 0b00011101, 'response delay time', '', 0, None),
|
|
(0b01111111, 0b00011110, 'retry', '', 0, None),
|
|
(0b01111111, 0b00011111, 'Reserved', '', 0, None),
|
|
(0b01111111, 0b00100000, 'First storage', '', 0, None),
|
|
(0b01111111, 0b00100001, 'Last storage', '', 0, None),
|
|
(0b01111111, 0b00100010, 'Size of storage blk', '', 0, None),
|
|
(0b01111111, 0b00100011, 'Reserved', '', 0, None),
|
|
(0b01111100, 0b00100100, 'Storage interval', '', 0b00000011, lambda d: d),
|
|
(0b01111111, 0b00101000, 'Storage interval month', '', 0, None),
|
|
(0b01111111, 0b00101001, 'Storage interval years', '', 0, None),
|
|
(0b01111111, 0b00101010, 'Reserved', '', 0, None),
|
|
(0b01111111, 0b00101011, 'Reserved', '', 0, None),
|
|
(0b01111111, 0b00101100, 'Duration since last read', '', 0b00000011, lambda d: d),
|
|
(0b01111111, 0b00110000, 'Start of tariff', '', 0, None),
|
|
(0b01111111, 0b00110000, 'Duration of tariff', '', 0b00000011, lambda d: d),
|
|
(0b01111111, 0b00110100, 'Period of tariff', '', 0b00000011, lambda d: d),
|
|
(0b01111111, 0b00111010, 'dimensionless', '', 0, None),
|
|
(0b01111111, 0b00111011, 'Reserved', '', 0, None),
|
|
(0b01111100, 0b00111100, 'Reserved', '', 0, None),
|
|
(0b01110000, 0b01000000, 'Voltage', 'V', 0b00001111, lambda d: 10**(d-9)),
|
|
(0b01110000, 0b01010000, 'Current', 'A', 0b00001111, lambda d: 10**(d-12)),
|
|
(0b01111111, 0b01100000, 'Reset Counter', '', 0, None),
|
|
(0b01111111, 0b01100001, 'Cumulation Counter', '', 0, None),
|
|
(0b01111111, 0b01100010, 'Control Signal', '', 0, None),
|
|
(0b01111111, 0b01100011, 'Day of week', '', 0, None),
|
|
(0b01111111, 0b01100100, 'Week number', '', 0, None),
|
|
(0b01111111, 0b01100101, 'Time point of day change', '', 0, None),
|
|
(0b01111111, 0b01100110, 'State of parameter activation', '', 0, None),
|
|
(0b01111111, 0b01100111, 'Special supplier info', '', 0, None),
|
|
(0b01111100, 0b01101000, 'Duration since last cumulation', '', 0b00000011, lambda d: d),
|
|
(0b01111100, 0b01101100, 'Operating time battery', '', 0b00000011, lambda d: d),
|
|
(0b01111111, 0b01110000, 'Date/time of bat. change', '', 0, None)
|
|
)
|
|
|
|
TABLE844b = (
|
|
(0b01111110, 0b00000000, 'Energy', 'MWh', 0b00000001, lambda d: 10**(d-1)),
|
|
(0b01111110, 0b00000010, 'Reserved', '', 0, None),
|
|
(0b01111100, 0b00000100, 'Reserved', '', 0, None),
|
|
(0b01111110, 0b00001000, 'Energy', 'GJ', 0b00000001, lambda d: 10**(d-1)),
|
|
(0b01111110, 0b00001010, 'Reserved', '', 0, None),
|
|
(0b01111100, 0b00001100, 'Reserved', '', 0, None),
|
|
(0b01111110, 0b00010000, 'Volume', 'm**3', 0b00000001, lambda d: 10**(d+2)),
|
|
(0b01111110, 0b00010010, 'Reserved', '', 0, None),
|
|
(0b01111100, 0b00010100, 'Reserved', '', 0, None),
|
|
(0b01111110, 0b00011000, 'Mass', 't', 0b00000001, lambda d: 10**(d+2)),
|
|
(0b01111111, 0b00100001, 'Volume', 'feet**3', 0, lambda d: 0.1),
|
|
(0b01111111, 0b00100010, 'Volume', 'am. gallon', 0, lambda d: 0.1),
|
|
(0b01111111, 0b00100011, 'Volume', 'am. gallon', 0, lambda d: 1),
|
|
(0b01111111, 0b00100100, 'Volume flow', 'am. gallon/min', 0, lambda d: 0.001),
|
|
(0b01111111, 0b00100101, 'Volume flow', 'am. gallon/min', 0, lambda d: 1),
|
|
(0b01111111, 0b00100110, 'Volume flow', 'am. gallon/h', 0, lambda d: 1),
|
|
(0b01111111, 0b00100111, 'Reserved', '', 0, None),
|
|
(0b01111110, 0b00101000, 'Power', 'MW', 0b00000001, lambda d: 10**(d-1)),
|
|
(0b01111110, 0b00101010, 'Reserved', '', 0, None),
|
|
(0b01111100, 0b00101100, 'Reserved', '', 0, None),
|
|
(0b01111110, 0b00110000, 'Power', 'GJ/h', 0b00000001, lambda d: 10**(d-1)),
|
|
(0b01111100, 0b01011000, 'Flow Temperature', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b01011100, 'Return Temperature', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b01100000, 'Temperature Diff.', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b01100100, 'Extern. Temp.', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111000, 0b01101000, 'Reserved', '', 0, None),
|
|
(0b01111100, 0b01110000, 'Cold/Warm Temp.Limit', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111100, 0b01110100, 'Cold/Warm Temp.Limit', 'Celsius', 0b00000011, lambda d: 10**(d-3)),
|
|
(0b01111000, 0b01111000, 'cumul. count max power', 'W', 0b00000111, lambda d: 10**(d-3))
|
|
)
|
|
|
|
|
|
TIME_UNITS = ('seconds', 'minutes', 'hours', 'days')
|
|
TIME_POINT_UNITS = ('date', 'time & date')
|
|
|
|
@classmethod
|
|
def extVifCalc(cls, table, vife):
|
|
retDescr = 'unknown'
|
|
retUnit = 'unknown'
|
|
retFactor = 1
|
|
|
|
for vifeCode in table:
|
|
# print vifeCode
|
|
(mask, value, descr, unit, factorMask, factorFunc) = vifeCode
|
|
if (vife & mask) == value:
|
|
if factorFunc is None:
|
|
retFactor = 1
|
|
else:
|
|
factorHelper = vife & factorMask
|
|
retFactor = factorFunc(factorHelper)
|
|
retDescr = descr
|
|
retUnit = unit
|
|
break
|
|
return (retDescr, retUnit, retFactor)
|
|
|
|
|
|
@classmethod
|
|
def vifCalc(cls, vif, vife):
|
|
retDescr = 'unknown'
|
|
retUnit = 'unknown'
|
|
retFactor = 1
|
|
for vifCode in cls.VIF_CODES:
|
|
(mask, value, descr, unit, factorMask, factorFunc) = vifCode
|
|
if (vif & mask) == value:
|
|
if unit == 'TimeCalc':
|
|
retDescr = descr
|
|
retUnit = cls.TIME_UNITS[vif & factorMask]
|
|
elif unit == 'TimePointCalc':
|
|
retDescr = descr
|
|
retUnit = cls.TIME_POINT_UNITS[vif & factorMask]
|
|
elif unit == 'Table8.4.4a':
|
|
(retDescr, retUnit, retFactor) = cls.extVifCalc(cls.TABLE844a, vife[0])
|
|
elif unit == 'Table8.4.4b':
|
|
(retDescr, retUnit, retFactor) = cls.extVifCalc(cls.TABLE844b, vife[0])
|
|
else:
|
|
retDescr = descr
|
|
retUnit = unit
|
|
if factorFunc is not None:
|
|
factorHelper = vif & factorMask
|
|
retFactor = factorFunc(factorHelper)
|
|
break
|
|
# print("vifCalc: %s %s %s %s" % (retDescr, retRange, retUnit, rangeHelper))
|
|
return (retDescr, retUnit, retFactor)
|
|
|
|
|
|
def parse(self):
|
|
self.dif = self.data[self.consumed]
|
|
self.consumed += 1
|
|
self.dife = []
|
|
if self.dif & 0x80 != 0:
|
|
while True:
|
|
curDife = self.data[self.consumed]
|
|
self.consumed += 1
|
|
self.dife.append(curDife)
|
|
if curDife & 0x80 == 0:
|
|
break
|
|
self.vif = self.data[self.consumed]
|
|
self.consumed += 1
|
|
self.vife = []
|
|
if self.vif & 0x80 != 0:
|
|
while True:
|
|
curVife = self.data[self.consumed]
|
|
self.consumed += 1
|
|
self.vife.append(curVife)
|
|
if curVife & 0x80 == 0:
|
|
break
|
|
self.vifData = []
|
|
if self.vif & 0x7f == 0x7c:
|
|
vifDataLength = self.data[self.consumed]
|
|
self.consumed += 1
|
|
self.vifData = [v for v in self.data[self.consumed:self.consumed+vifDataLength]]
|
|
self.consumed += vifDataLength
|
|
userDataLength = LongFrame.DataInformationBlock.DATA_FIELD_CODES[self.dif&0x0f][1]
|
|
self.userData = [u for u in self.data[self.consumed:self.consumed+userDataLength]]
|
|
self.consumed += userDataLength
|
|
|
|
dataConvFunc = LongFrame.DataInformationBlock.DATA_FIELD_CODES[self.dif & 0x0f][2]
|
|
if dataConvFunc is None:
|
|
self.rawValue = 0
|
|
else:
|
|
self.rawValue = dataConvFunc(self.userData)
|
|
self.dataType = LongFrame.DataInformationBlock.DATA_FIELD_CODES[self.dif & 0x0f][0]
|
|
(self.quantity, self.unit, self.factor) = LongFrame.DataInformationBlock.vifCalc(self.vif, self.vife)
|
|
self.value = self.rawValue * self.factor
|
|
|
|
return self.consumed
|
|
|
|
|
|
def getJSON(self):
|
|
superJSON = super(LongFrame.DataInformationBlock, self).getJSON()
|
|
j = {'dif': self.dif, 'dife': self.dife, 'vif': self.vif, 'vife': self.vife,
|
|
'vifData': self.vifData, 'userData':self.userData,
|
|
'dataType': self.dataType, 'rawValue':self.rawValue,
|
|
'value': self.value,
|
|
'quantity': self.quantity, 'factor': self.factor, 'unit': self.unit,
|
|
'comment': self.comment
|
|
}
|
|
j.update(superJSON)
|
|
return j
|
|
|
|
|
|
|
|
def parse2(self):
|
|
super(LongFrame, self).parse2()
|
|
if self.payloadLength < 3 + 12:
|
|
raise PayloadTooShortException("too short for fixed data header")
|
|
self.fixedDataHeader = LongFrame.FixedDataHeader(self.telegram[7:19])
|
|
self.fixedDataHeader.parse()
|
|
device = None
|
|
for d in self.devices:
|
|
if d.address == self.address:
|
|
#print("device found")
|
|
device = d
|
|
break
|
|
if device is not None:
|
|
self.comment, self.category = device.comment, device.category
|
|
else:
|
|
self.comment, self.category = '-', '-'
|
|
self.dib = []
|
|
consumed = 0
|
|
dibIndex = 0
|
|
#print("Telegram length: %d" % len(self.telegram))
|
|
while True:
|
|
if device is not None:
|
|
try:
|
|
comment = device.dataItems[dibIndex]
|
|
except IndexError:
|
|
raise DeviceItemsMismatchException()
|
|
else:
|
|
comment = '-'
|
|
dibIndex += 1
|
|
curDib = LongFrame.DataInformationElement.create(self.telegram[(19 + consumed):-2], comment)
|
|
consumed += curDib.parse()
|
|
self.dib.append(curDib)
|
|
#print("PayloadLength: %d, Consumed: %d" % (self.payloadLength, consumed))
|
|
#print("DIB: %s" % str(curDib.getJSON()))
|
|
# 3 for the fields, 12 for the fixed data header, 2 for checksum and stop code, 1 off since consumed is an index and no length
|
|
if (consumed + 3 + 12 + 2 - 1) >= self.payloadLength:
|
|
break
|
|
|
|
|
|
def getJSON(self):
|
|
superJSON = super(LongFrame, self).getJSON()
|
|
j = {'comment': self.comment, 'category': self.category, 'header': self.fixedDataHeader.getJSON(), 'dib': [dib.getJSON() for dib in self.dib]}
|
|
j.update(superJSON)
|
|
return j
|
|
|
|
class Telegram(object):
|
|
def __init__(self, devices=[]):
|
|
self.devices = devices
|
|
|
|
def fromHexString(self, hexString):
|
|
self.hexString = hexString
|
|
octetList = [int(o, 16) for o in self.hexString.split(' ')]
|
|
self.telegram = bytearray(octetList)
|
|
|
|
def toHexString(self):
|
|
return str([hex(b) for b in self.telegram])
|
|
|
|
def parse(self):
|
|
if self.telegram[0] == 0x68 and self.telegram[1] == 0x03:
|
|
self.frame = ControlFrame(self.telegram)
|
|
elif self.telegram[0] == 0x68:
|
|
self.frame = LongFrame(self.telegram, self.devices)
|
|
elif self.telegram[0] == 0x10:
|
|
self.frame = ShortFrame(self.telegram)
|
|
elif self.telegram[0] == 0xe5:
|
|
self.frame = SingleCharacter(self.telegram)
|
|
else:
|
|
raise InvalidStartCharException()
|
|
|
|
self.frame.parse()
|
|
|
|
def getJSON(self):
|
|
j = {'frameType': self.frame.__class__.__name__, 'frame': self.frame.getJSON()}
|
|
return j
|
|
|
|
|
|
|