seems to work

This commit is contained in:
hg
2015-06-12 15:14:38 +02:00
parent d64e8bff98
commit 88e36b0170
5 changed files with 133 additions and 59 deletions

View File

@ -12,6 +12,12 @@ import MeterbusTypeConversion
from MeterbusLibExceptions import * from MeterbusLibExceptions import *
import struct import struct
class Device(object):
def __init__(self, address, comment, dataItems):
self.address = address
self.comment = comment
self.dataItems = dataItems
class Frame(object): class Frame(object):
def __init__(self, startCharacter, frameLength, firstPayload, telegram): def __init__(self, startCharacter, frameLength, firstPayload, telegram):
self.telegram = telegram self.telegram = telegram
@ -88,6 +94,14 @@ class ControlFrame(Frame):
class LongFrame(ControlFrame): class LongFrame(ControlFrame):
def __init__(self, telegram, devices=[]):
super(LongFrame, self).__init__(telegram)
self.devices = devices
if self.devices != None:
for device in self.devices:
if not isinstance(device, Device):
raise InvalidDevicesStructureException()
class FixedDataHeader(object): class FixedDataHeader(object):
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data
@ -102,6 +116,7 @@ class LongFrame(ControlFrame):
self.signature = [s for s in self.data[10:]] self.signature = [s for s in self.data[10:]]
if self.signature[0] != 0 or self.signature[1] != 0: if self.signature[0] != 0 or self.signature[1] != 0:
raise InvalidFrameException("signature should be zero") raise InvalidFrameException("signature should be zero")
#print("FixedDataHeader: " + str(self.getJSON()))
def getJSON(self): def getJSON(self):
j = {'identNr': self.identNr, 'manufacturer': self.manufacturer, 'version': self.version, 'medium': self.medium, j = {'identNr': self.identNr, 'manufacturer': self.manufacturer, 'version': self.version, 'medium': self.medium,
@ -109,24 +124,34 @@ class LongFrame(ControlFrame):
return j return j
class DataInformationElement(object): class DataInformationElement(object):
def __init__(self, data): def __init__(self, data, comment):
self.data = data self.data = data
self.comment = comment
self.consumed = 0 self.consumed = 0
@classmethod @classmethod
def create(cls, data): def create(cls, data, comment):
if data[0] != 0x0f: if data[0] != 0x0f:
return LongFrame.DataInformationBlock(data) return LongFrame.DataInformationBlock(data, comment)
else: else:
return LongFrame.ManufacturerSpecificDataBlock(data) return LongFrame.ManufacturerSpecificDataBlock(data, comment)
def getJSON(self):
j = {'comment': self.comment}
return j
class ManufacturerSpecificDataBlock(DataInformationElement): class ManufacturerSpecificDataBlock(DataInformationElement):
def parse(self): def parse(self):
self.dif = self.data[0] self.dif = self.data[0]
self.mdh = self.data[1:] self.mdh = [ m for m in self.data[1:]]
self.consumed = len(self.data)
return self.consumed
def getJSON(self): def getJSON(self):
superJSON = super(LongFrame.ManufacturerSpecificDataBlock, self).getJSON()
j = {'dif': self.dif, 'mdh': self.mdh} j = {'dif': self.dif, 'mdh': self.mdh}
j.update(superJSON)
return j return j
class DataInformationBlock(DataInformationElement): class DataInformationBlock(DataInformationElement):
@ -136,7 +161,7 @@ class LongFrame(ControlFrame):
('16 Bit Integer', 2, lambda x: (x[1] << 8) + 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]), ('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 Integer', 4, lambda x: (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]),
('32 Bit Real', 4, lambda x: struct.unpack('f', x.getAll())[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]), ('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]), ('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), ('Selection for Readout', 0, None),
@ -153,24 +178,24 @@ class LongFrame(ControlFrame):
(0b01111000, 0b00001000, 'Energy', 'J', 0b00000111, lambda d: 10**d), (0b01111000, 0b00001000, 'Energy', 'J', 0b00000111, lambda d: 10**d),
(0b01111000, 0b00010000, 'Volume', 'm**3', 0b00000111, lambda d: 10**(d-6)), (0b01111000, 0b00010000, 'Volume', 'm**3', 0b00000111, lambda d: 10**(d-6)),
(0b01111000, 0b00011000, 'Mass', 'kg', 0b00000111, lambda d: 10**(d-3)), (0b01111000, 0b00011000, 'Mass', 'kg', 0b00000111, lambda d: 10**(d-3)),
(0b01111100, 0b00100000, 'On Time', 'TimeCalc', 0b00000011, lambda d: d), (0b01111100, 0b00100000, 'On Time', 'TimeCalc', 0b00000011, None),
(0b01111100, 0b00100100, 'Operating Time', 'TimeCalc', 0b00000011, lambda d: d), (0b01111100, 0b00100100, 'Operating Time', 'TimeCalc', 0b00000011, None),
(0b01111000, 0b00101000, 'Power', 'W', 0b00000111, lambda d: 10**(d-3)), (0b01111000, 0b00101000, 'Power', 'W', 0b00000111, lambda d: 10**(d-3)),
(0b01111000, 0b00110000, 'Power', 'J/h', 0b00000111, lambda d: 10**d), (0b01111000, 0b00110000, 'Power', 'J/h', 0b00000111, lambda d: 10**d),
(0b01111000, 0b00111000, 'Volume Flow', 'm**3/h', 0b00000111, lambda d: 10**(d-6)), (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, 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, 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)), (0b01111000, 0b01010000, 'Mass Flow', 'kg/h', 0b00000111, lambda d: 10**(d-3)),
(0b01111100, 0b01011000, 'Flow Temperature', '<EFBFBD>C', 0b00000011, lambda d: 10**(d-3)), (0b01111100, 0b01011000, 'Flow Temperature', 'Celsius', 0b00000011, lambda d: 10**(d-3)),
(0b01111100, 0b01011100, 'Return Temperature', '<EFBFBD>C', 0b00000011, lambda d: 10**(d-3)), (0b01111100, 0b01011100, 'Return Temperature', 'Celsius', 0b00000011, lambda d: 10**(d-3)),
(0b01111100, 0b01100000, 'Temperature Diff.', 'K', 0b00000011, lambda d: 10**(d-3)), (0b01111100, 0b01100000, 'Temperature Diff.', 'Kelvin', 0b00000011, lambda d: 10**(d-3)),
(0b01111100, 0b01100100, 'Extern. Temp', '<EFBFBD>C', 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)), (0b01111100, 0b01101000, 'Pressure', 'bar', 0b00000011, lambda d: 10**(d-3)),
(0b01111110, 0b01101100, 'Time Point', 'TimePointCalc', 0b00000001,lambda d: d), (0b01111110, 0b01101100, 'Time Point', 'TimePointCalc', 0b00000001,None),
(0b01111111, 0b01101110, 'Units for H.C.A.', '', 0, None), (0b01111111, 0b01101110, 'Units for H.C.A.', '', 0, None),
(0b01111111, 0b01101111, 'Reserved', '', 0, None), (0b01111111, 0b01101111, 'Reserved', '', 0, None),
(0b01111100, 0b01110000, 'Averaging Duration', 'TimeCalc', 0b00000011, lambda d: d), (0b01111100, 0b01110000, 'Averaging Duration', 'TimeCalc', 0b00000011, None),
(0b01111100, 0b01110100, 'Actuality Duration', 'TimeCalc', 0b00000011, lambda d: d), (0b01111100, 0b01110100, 'Actuality Duration', 'TimeCalc', 0b00000011, None),
(0b01111111, 0b01111000, 'Fabrication Number', '', 0, None), (0b01111111, 0b01111000, 'Fabrication Number', '', 0, None),
(0b01111111, 0b01111001, 'Enhanced Ident.', '', 0, None), (0b01111111, 0b01111001, 'Enhanced Ident.', '', 0, None),
(0b01111111, 0b01111010, 'Bus Address', '', 0, None), (0b01111111, 0b01111010, 'Bus Address', '', 0, None),
@ -258,13 +283,13 @@ class LongFrame(ControlFrame):
(0b01111110, 0b00101010, 'Reserved', '', 0, None), (0b01111110, 0b00101010, 'Reserved', '', 0, None),
(0b01111100, 0b00101100, 'Reserved', '', 0, None), (0b01111100, 0b00101100, 'Reserved', '', 0, None),
(0b01111110, 0b00110000, 'Power', 'GJ/h', 0b00000001, lambda d: 10**(d-1)), (0b01111110, 0b00110000, 'Power', 'GJ/h', 0b00000001, lambda d: 10**(d-1)),
(0b01111100, 0b01011000, 'Flow Temperature', '<EFBFBD>F', 0b00000011, lambda d: 10**(d-3)), (0b01111100, 0b01011000, 'Flow Temperature', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
(0b01111100, 0b01011100, 'Return Temperature', '<EFBFBD>F', 0b00000011, lambda d: 10**(d-3)), (0b01111100, 0b01011100, 'Return Temperature', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
(0b01111100, 0b01100000, 'Temperature Diff.', '<EFBFBD>F', 0b00000011, lambda d: 10**(d-3)), (0b01111100, 0b01100000, 'Temperature Diff.', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
(0b01111100, 0b01100100, 'Extern. Temp.', '<EFBFBD>F', 0b00000011, lambda d: 10**(d-3)), (0b01111100, 0b01100100, 'Extern. Temp.', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
(0b01111000, 0b01101000, 'Reserved', '', 0, None), (0b01111000, 0b01101000, 'Reserved', '', 0, None),
(0b01111100, 0b01110000, 'Cold/Warm Temp.Limit', '<EFBFBD>F', 0b00000011, lambda d: 10**(d-3)), (0b01111100, 0b01110000, 'Cold/Warm Temp.Limit', 'Fahrenheit', 0b00000011, lambda d: 10**(d-3)),
(0b01111100, 0b01110100, 'Cold/Warm Temp.Limit', '<EFBFBD>C', 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)) (0b01111000, 0b01111000, 'cumul. count max power', 'W', 0b00000111, lambda d: 10**(d-3))
) )
@ -276,52 +301,50 @@ class LongFrame(ControlFrame):
def extVifCalc(cls, table, vife): def extVifCalc(cls, table, vife):
retDescr = 'unknown' retDescr = 'unknown'
retUnit = 'unknown' retUnit = 'unknown'
retRange = 'unknown' retFactor = 1
for vifeCode in table: for vifeCode in table:
# print vifeCode # print vifeCode
(mask, value, descr, unit, rangeMask, rangeFunc) = vifeCode (mask, value, descr, unit, factorMask, factorFunc) = vifeCode
if (vife & mask) == value: if (vife & mask) == value:
if rangeFunc == None: if factorFunc == None:
retRange = 1 retFactor = 1
else: else:
rangeHelper = vife & rangeMask factorHelper = vife & factorMask
retRange = rangeFunc(rangeHelper) retFactor = factorFunc(factorHelper)
retDescr = descr retDescr = descr
retUnit = unit retUnit = unit
break break
return (retDescr, retUnit, retRange) return (retDescr, retUnit, retFactor)
@classmethod @classmethod
def vifCalc(cls, vif, vife): def vifCalc(cls, vif, vife):
retDescr = 'unknown' retDescr = 'unknown'
retUnit = 'unknown' retUnit = 'unknown'
retRange = 1 retFactor = 1
for vifCode in cls.VIF_CODES: for vifCode in cls.VIF_CODES:
(mask, value, descr, unit, rangeMask, rangeFunc) = vifCode (mask, value, descr, unit, factorMask, factorFunc) = vifCode
if (vif & mask) == value: if (vif & mask) == value:
if unit == 'TimeCalc': if unit == 'TimeCalc':
retDescr = descr retDescr = descr
retUnit = cls.TIME_UNITS[vif & rangeMask] retUnit = cls.TIME_UNITS[vif & factorMask]
retRange = None
elif unit == 'Table8.4.4a':
(retDescr, retUnit, retRange) = cls.extVifCalc(cls.TABLE844a, vife[0])
elif unit == 'Table8.4.4b':
(retDescr, retUnit, retRange) = cls.extVifCalc(cls.TABLE844b, vife[0])
elif unit == 'TimePointCalc': elif unit == 'TimePointCalc':
retDescr = descr retDescr = descr
retUnit = cls.TIME_POINT_UNITS[vif & rangeMask] retUnit = cls.TIME_POINT_UNITS[vif & factorMask]
retRange = None 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: else:
retDescr = descr retDescr = descr
retUnit = unit retUnit = unit
if rangeFunc != None: if factorFunc != None:
rangeHelper = vif & rangeMask factorHelper = vif & factorMask
retRange = rangeFunc(rangeHelper) retFactor = factorFunc(factorHelper)
break break
# print("vifCalc: %s %s %s %s" % (retDescr, retRange, retUnit, rangeHelper)) # print("vifCalc: %s %s %s %s" % (retDescr, retRange, retUnit, rangeHelper))
return (retDescr, retUnit, retRange) return (retDescr, retUnit, retFactor)
def parse(self): def parse(self):
@ -355,19 +378,28 @@ class LongFrame(ControlFrame):
self.userData = [u for u in self.data[self.consumed:self.consumed+userDataLength]] self.userData = [u for u in self.data[self.consumed:self.consumed+userDataLength]]
self.consumed += userDataLength self.consumed += userDataLength
self.value = LongFrame.DataInformationBlock.DATA_FIELD_CODES[self.dif & 0x0f][2](self.userData) dataConvFunc = LongFrame.DataInformationBlock.DATA_FIELD_CODES[self.dif & 0x0f][2]
if dataConvFunc == None:
self.rawValue = 0
else:
self.rawValue = dataConvFunc(self.userData)
self.dataType = LongFrame.DataInformationBlock.DATA_FIELD_CODES[self.dif & 0x0f][0] 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.quantity, self.unit, self.factor) = LongFrame.DataInformationBlock.vifCalc(self.vif, self.vife)
self.value = self.rawValue * self.factor
return self.consumed return self.consumed
def getJSON(self): def getJSON(self):
superJSON = super(LongFrame.DataInformationBlock, self).getJSON()
j = {'dif': self.dif, 'dife': self.dife, 'vif': self.vif, 'vife': self.vife, j = {'dif': self.dif, 'dife': self.dife, 'vif': self.vif, 'vife': self.vife,
'vifData': self.vifData, 'userData':self.userData, 'vifData': self.vifData, 'userData':self.userData,
'dataType': self.dataType, 'value':self.value, 'dataType': self.dataType, 'rawValue':self.rawValue,
'value': self.value,
'quantity': self.quantity, 'factor': self.factor, 'unit': self.unit, 'quantity': self.quantity, 'factor': self.factor, 'unit': self.unit,
'comment': self.comment
} }
j.update(superJSON)
return j return j
@ -378,11 +410,27 @@ class LongFrame(ControlFrame):
raise PayloadTooShortException("too short for fixed data header") raise PayloadTooShortException("too short for fixed data header")
self.fixedDataHeader = LongFrame.FixedDataHeader(self.telegram[7:19]) self.fixedDataHeader = LongFrame.FixedDataHeader(self.telegram[7:19])
self.fixedDataHeader.parse() self.fixedDataHeader.parse()
device = None
for d in self.devices:
if d.address == self.address:
print("device found")
device = d
break
if device != None:
self.comment = device.comment
else:
self.comment = '-'
self.dib = [] self.dib = []
consumed = 0 consumed = 0
dibIndex = 0
#print("Telegram length: %d" % len(self.telegram)) #print("Telegram length: %d" % len(self.telegram))
while True: while True:
curDib = LongFrame.DataInformationElement.create(self.telegram[(19 + consumed):-2]) if device != None:
comment = device.dataItems[dibIndex]
else:
comment = '-'
dibIndex += 1
curDib = LongFrame.DataInformationElement.create(self.telegram[(19 + consumed):-2], comment)
consumed += curDib.parse() consumed += curDib.parse()
self.dib.append(curDib) self.dib.append(curDib)
#print("PayloadLength: %d, Consumed: %d" % (self.payloadLength, consumed)) #print("PayloadLength: %d, Consumed: %d" % (self.payloadLength, consumed))
@ -391,15 +439,16 @@ class LongFrame(ControlFrame):
if (consumed + 3 + 12 + 2 - 1) >= self.payloadLength: if (consumed + 3 + 12 + 2 - 1) >= self.payloadLength:
break break
def getJSON(self): def getJSON(self):
superJSON = super(LongFrame, self).getJSON() superJSON = super(LongFrame, self).getJSON()
j = {'header': self.fixedDataHeader.getJSON(), 'dib': [dib.getJSON() for dib in self.dib]} j = {'comment': self.comment, 'header': self.fixedDataHeader.getJSON(), 'dib': [dib.getJSON() for dib in self.dib]}
j.update(superJSON) j.update(superJSON)
return j return j
class Telegram(object): class Telegram(object):
def __init__(self): def __init__(self, devices=[]):
pass self.devices = devices
def fromHexString(self, hexString): def fromHexString(self, hexString):
self.hexString = hexString self.hexString = hexString
@ -413,7 +462,7 @@ class Telegram(object):
if self.telegram[0] == 0x68 and self.telegram[1] == 0x03: if self.telegram[0] == 0x68 and self.telegram[1] == 0x03:
self.frame = ControlFrame(self.telegram) self.frame = ControlFrame(self.telegram)
elif self.telegram[0] == 0x68: elif self.telegram[0] == 0x68:
self.frame = LongFrame(self.telegram) self.frame = LongFrame(self.telegram, self.devices)
elif self.telegram[0] == 0x10: elif self.telegram[0] == 0x10:
self.frame = ShortFrame(self.telegram) self.frame = ShortFrame(self.telegram)
elif self.telegram[0] == 0xe5: elif self.telegram[0] == 0xe5:

View File

@ -33,3 +33,9 @@ class PayloadTooShortException(MeterbusLibException):
class MediumConversionException(MeterbusLibException): class MediumConversionException(MeterbusLibException):
pass pass
class InvalidDevicesStructureException(MeterbusLibException):
pass
class DeviceItemsMismatchException(MeterbusLibException):
pass

View File

@ -13,6 +13,7 @@ def bcd(data):
for c in reversed(data): for c in reversed(data):
v += str((c & 0xf0) >> 4) v += str((c & 0xf0) >> 4)
v += str(c & 0x0f) v += str(c & 0x0f)
v = int(v)
return v return v
def manufCode(data): def manufCode(data):

View File

@ -53,7 +53,7 @@ class TestFrameParsing(unittest.TestCase):
self.assertEqual(telegram.frame.cField, 0x08); self.assertEqual(telegram.frame.cField, 0x08);
self.assertEqual(telegram.frame.address, 0x53); self.assertEqual(telegram.frame.address, 0x53);
self.assertEqual(telegram.frame.ciField, 0x72); self.assertEqual(telegram.frame.ciField, 0x72);
self.assertEqual(telegram.frame.fixedDataHeader.identNr, "17001300") self.assertEqual(telegram.frame.fixedDataHeader.identNr, 130017)
self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "FIN") self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "FIN")
self.assertEqual(telegram.frame.fixedDataHeader.version, 36) self.assertEqual(telegram.frame.fixedDataHeader.version, 36)
self.assertEqual(telegram.frame.fixedDataHeader.medium, "Electrity") self.assertEqual(telegram.frame.fixedDataHeader.medium, "Electrity")
@ -79,7 +79,7 @@ class TestFrameParsing(unittest.TestCase):
self.assertEqual(telegram.frame.cField, 0x08); self.assertEqual(telegram.frame.cField, 0x08);
self.assertEqual(telegram.frame.address, 0x50); self.assertEqual(telegram.frame.address, 0x50);
self.assertEqual(telegram.frame.ciField, 0x72); self.assertEqual(telegram.frame.ciField, 0x72);
self.assertEqual(telegram.frame.fixedDataHeader.identNr, "81140111") self.assertEqual(telegram.frame.fixedDataHeader.identNr, 11011481)
self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "FIN") self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "FIN")
self.assertEqual(telegram.frame.fixedDataHeader.version, 22) self.assertEqual(telegram.frame.fixedDataHeader.version, 22)
self.assertEqual(telegram.frame.fixedDataHeader.medium, "Electrity") self.assertEqual(telegram.frame.fixedDataHeader.medium, "Electrity")
@ -92,7 +92,7 @@ class TestFrameParsing(unittest.TestCase):
self.assertEqual(telegram.frame.cField, 0x08); self.assertEqual(telegram.frame.cField, 0x08);
self.assertEqual(telegram.frame.address, 0x30); self.assertEqual(telegram.frame.address, 0x30);
self.assertEqual(telegram.frame.ciField, 0x72); self.assertEqual(telegram.frame.ciField, 0x72);
self.assertEqual(telegram.frame.fixedDataHeader.identNr, "45714300") self.assertEqual(telegram.frame.fixedDataHeader.identNr, 437145)
self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "HYD") self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "HYD")
self.assertEqual(telegram.frame.fixedDataHeader.version, 37) self.assertEqual(telegram.frame.fixedDataHeader.version, 37)
self.assertEqual(telegram.frame.fixedDataHeader.medium, "Water") self.assertEqual(telegram.frame.fixedDataHeader.medium, "Water")
@ -106,7 +106,7 @@ class TestFrameParsing(unittest.TestCase):
self.assertEqual(telegram.frame.cField, 0x08); self.assertEqual(telegram.frame.cField, 0x08);
self.assertEqual(telegram.frame.address, 0x40); self.assertEqual(telegram.frame.address, 0x40);
self.assertEqual(telegram.frame.ciField, 0x72); self.assertEqual(telegram.frame.ciField, 0x72);
self.assertEqual(telegram.frame.fixedDataHeader.identNr, "43605200") self.assertEqual(telegram.frame.fixedDataHeader.identNr, 526043)
self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "ACW") self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "ACW")
self.assertEqual(telegram.frame.fixedDataHeader.version, 20) self.assertEqual(telegram.frame.fixedDataHeader.version, 20)
self.assertEqual(telegram.frame.fixedDataHeader.medium, "Gas") self.assertEqual(telegram.frame.fixedDataHeader.medium, "Gas")
@ -119,7 +119,7 @@ class TestFrameParsing(unittest.TestCase):
self.assertEqual(telegram.frame.cField, 0x08); self.assertEqual(telegram.frame.cField, 0x08);
self.assertEqual(telegram.frame.address, 0x21); self.assertEqual(telegram.frame.address, 0x21);
self.assertEqual(telegram.frame.ciField, 0x72); self.assertEqual(telegram.frame.ciField, 0x72);
self.assertEqual(telegram.frame.fixedDataHeader.identNr, "00000000") self.assertEqual(telegram.frame.fixedDataHeader.identNr, 0)
self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "@@@") self.assertEqual(telegram.frame.fixedDataHeader.manufacturer, "@@@")
self.assertEqual(telegram.frame.fixedDataHeader.version, 1) self.assertEqual(telegram.frame.fixedDataHeader.version, 1)
self.assertEqual(telegram.frame.fixedDataHeader.medium, "Other") self.assertEqual(telegram.frame.fixedDataHeader.medium, "Other")
@ -178,9 +178,9 @@ class TestFrameParsing(unittest.TestCase):
telegram.parse() telegram.parse()
def test_bcd(self): def test_bcd(self):
self.assertEqual(MeterbusTypeConversion.bcd([0x99]), '99') self.assertEqual(MeterbusTypeConversion.bcd([0x99]), 99)
self.assertEqual(MeterbusTypeConversion.bcd([0x12, 0x34]), '1234') self.assertEqual(MeterbusTypeConversion.bcd([0x12, 0x34]), 3412)
self.assertEqual(MeterbusTypeConversion.bcd([0x12, 0x34, 0x56, 0x78]), '12345678') self.assertEqual(MeterbusTypeConversion.bcd([0x12, 0x34, 0x56, 0x78]), 78563412)
def test_manufCode(self): def test_manufCode(self):
self.assertEqual(MeterbusTypeConversion.manufCode([0x77, 0x04]), 'ACW') self.assertEqual(MeterbusTypeConversion.manufCode([0x77, 0x04]), 'ACW')

View File

@ -8,11 +8,29 @@ Created on 11.06.2015
import MeterbusLib import MeterbusLib
import json import json
devices = []
device_1phase_electric = MeterbusLib.Device(0x53, "1 Phase Electric", ["Energy total", "Energy partial", "Voltage", "Current", "Power", "img. Power"])
devices.append(device_1phase_electric)
inputOkLongFrame_1phase_electric = "68 38 38 68 " + "08 53 72 " + "17 00 13 00 2E 19 24 02 D6 00 00 00 " + "8C 10 04 01 02 00 00 " + "8C 11 04 01 02 00 00 " + "02 FD C9 FF 01 E4 00 " + "02 FD DB FF 01 03 00 " + "02 AC FF 01 01 00 " + "82 40 AC FF 01 FA FF " + "20 16" inputOkLongFrame_1phase_electric = "68 38 38 68 " + "08 53 72 " + "17 00 13 00 2E 19 24 02 D6 00 00 00 " + "8C 10 04 01 02 00 00 " + "8C 11 04 01 02 00 00 " + "02 FD C9 FF 01 E4 00 " + "02 FD DB FF 01 03 00 " + "02 AC FF 01 01 00 " + "82 40 AC FF 01 FA FF " + "20 16"
device_3phase_electric = MeterbusLib.Device(0x50, "3 Phase Electric", ["Energy T1 total", "Energy T1 partial", "Energy T2 total", "Energy T2 partial",
"Voltage phase 1", "Current phase 1", "Power phase 1", "img. Power phase 1",
"Voltage phase 2", "Current phase 2", "Power phase 2", "img. Power phase 2",
"Voltage phase 3", "Current phase 3", "Power phase 3", "img. Power phase 3",
"converter ratio", "Power total", "img. Power total", "tariff"
])
devices.append(device_3phase_electric)
inputOkLongFrame_3phase_electric = "68 92 92 68 08 50 72 81 14 01 11 2E 19 16 02 88 00 00 00 8C 10 04 58 43 86 00 8C 11 04 58 43 86 00 8C 20 04 00 00 00 00 8C 21 04 00 00 00 00 02 FD C9 FF 01 E4 00 02 FD DB FF 01 5A 00 02 AC FF 01 D2 00 82 40 AC FF 01 00 00 02 FD C9 FF 02 DF 00 02 FD DB FF 02 0F 00 02 AC FF 02 21 00 82 40 AC FF 02 FD FF 02 FD C9 FF 03 E3 00 02 FD DB FF 03 04 00 02 AC FF 03 02 00 82 40 AC FF 03 F4 FF 02 FF 68 00 00 02 AC FF 00 F5 00 82 40 AC FF 00 F1 FF 01 FF 13 00 F4 16" inputOkLongFrame_3phase_electric = "68 92 92 68 08 50 72 81 14 01 11 2E 19 16 02 88 00 00 00 8C 10 04 58 43 86 00 8C 11 04 58 43 86 00 8C 20 04 00 00 00 00 8C 21 04 00 00 00 00 02 FD C9 FF 01 E4 00 02 FD DB FF 01 5A 00 02 AC FF 01 D2 00 82 40 AC FF 01 00 00 02 FD C9 FF 02 DF 00 02 FD DB FF 02 0F 00 02 AC FF 02 21 00 82 40 AC FF 02 FD FF 02 FD C9 FF 03 E3 00 02 FD DB FF 03 04 00 02 AC FF 03 02 00 82 40 AC FF 03 F4 FF 02 FF 68 00 00 02 AC FF 00 F5 00 82 40 AC FF 00 F1 FF 01 FF 13 00 F4 16"
device_thermometer = MeterbusLib.Device(0x21, "Thermometer", ["Uptime Seconds", "Uptime Minutes", "Uptime Hours", "Uptime Days",
"Temperature 1", "Temperature 2", "Temperature 3", "Temperature 4",
"rawdata"
])
devices.append(device_thermometer)
inputOkLongFrame_thermometer = "68 61 61 68 08 21 72 00 00 00 00 00 00 01 00 7F 00 00 00 01 24 02 01 25 30 01 26 10 02 27 07 00 05 67 43 B6 DA 3D 05 67 DC 90 50 BD 05 67 AA E8 AA 41 05 67 AF 57 BA 41 0F 77 C0 00 00 7F 27 00 00 0A 00 00 00 00 00 00 00 01 00 00 00 A2 C3 7F 3F A5 BA 7F 3F 85 A7 7F 3F E7 F9 7F 3F CD CC CC 3D E8 03 00 00 02 16"
inputOkLongFrame_water = "68 46 46 68 08 30 72 45 71 43 00 24 23 25 07 F6 00 00 00 0C 13 97 95 43 00 8C 10 13 00 00 00 00 0B 3B 00 00 00 0B 26 14 60 01 02 5A AC 00 04 6D 14 0E EB 16 4C 13 96 41 33 00 CC 10 13 00 00 00 00 42 6C DF 1C 42 EC 7E FF 1C 99 16"
inputOkLongFrame_gas = "68 56 56 68 08 40 72 43 60 52 00 77 04 14 03 FF 10 00 00 0C 78 76 03 01 10 0D 7C 08 44 49 20 2E 74 73 75 63 0A 30 30 30 30 30 30 30 30 30 30 04 6D 06 0E EB 16 02 7C 09 65 6D 69 74 20 2E 74 61 62 A3 09 04 13 98 AE 04 00 04 93 7F 4E 01 00 00 44 13 FC A5 04 00 0F 01 00 1F 51 16"
telegram = MeterbusLib.Telegram() telegram = MeterbusLib.Telegram(devices)
telegram.fromHexString(inputOkLongFrame_3phase_electric) telegram.fromHexString(inputOkLongFrame_thermometer)
telegram.parse() telegram.parse()
print(json.dumps(telegram.getJSON(), indent=2)) print(json.dumps(telegram.getJSON(), indent=2))