spacemanspiff2007 / SmlLib

A library for the SML (Smart Message Language) protocol
GNU General Public License v3.0
27 stars 9 forks source link

smllib.sml_frame.InvalidBufferPos: Start pos bigger than buffer: 333 > 333. #8

Closed eng1neer-com closed 2 years ago

eng1neer-com commented 2 years ago

Hi,

thanks for creating this awesome library. I have been using it for quite some time now and it works really well. Unfortunately there is one issue which pops up every few hours if the library is used for continuous telegram reading of my electric meter.

Error: smllib.sml_frame.InvalidBufferPos: Start pos bigger than buffer: 333 > 333. sml_frame.py", line 37, in get_value (occurs while calling "sml_frame.get_obis()")

I tried to investigate why it only occurs for certain telegrams but not all of them.

It seems that it is due to a bug in the get_obis() function in this line: while (start := self.bytes.find(b'\x77\x07\x01', start + 1)) != -1:

This line seems to be searching for OBIS list entries. But if the 'value' entry of some of the OBIS register itself (e.g. the total energy register) ends with value 0x7707 this will lead to the issue since the next byte after the register is usually a 0x01. In combination a new OBIS is detected by mistake which will screw up the array.

I created the pull request #7 as an example of how I fixed it locally. Basically I am just adding up all the length byte nibbles of each of the OBIS entries and add this as an offset for the next OBIS occurence. That way I prevent a false detection of a new OBIS.

How to reproduce the issue

I attached a few example telegrams: telegrams_to_replicate_issue.txt usual_data: regular telegram which is working fine not_working: telegram which caused the issue fixed_data: issue fixed by replacing the total energy register value 0x7707 with zeros (The telegrams are slightly modified. I removed private data and updated the CRC)

Example code (as in your readme) using the "not_working" telegram

stream = SmlStreamReader()
stream.add(a2b_hex(not_working))
sml_frame = stream.get_frame()
if sml_frame is None:
    print('Bytes missing')

# Shortcut to extract all values without parsing the whole frame
obis_values = sml_frame.get_obis()

# return all values but slower
parsed_msgs = sml_frame.parse_frame()
for msg in parsed_msgs:
    # prints a nice overview over the received values
    print(msg.format_msg())
spacemanspiff2007 commented 2 years ago

Thank you for your issue and the excellent analysis with test data. I'll provide a fix, soon.