pyUBX icon indicating copy to clipboard operation
pyUBX copied to clipboard

*_GET-message types confuse parseUBXPayload

Open thasti opened this issue 7 years ago • 1 comments

I wrote a simple script trying to query some settings of a receiver. However, after sending (for example) a UBX.CFG.PRT_GET message, the UBXManager fails to parse the response correctly.

I traced this error down already to some extent: The root cause is that both UBX.CFG.PRT_GET and UBX.CFG.PRT have the same _id. When initMessageClass initially fills the _lookup-dictionary of UBX.CFG, duplicate keys are partially discarded, only the last key-value is stored, leaving only UBX.CFG.PRT_GET in there.

When parseUBXPayload then tries to look up the subclass from the _lookup-dictionary with .get(msgid), only UBX.CFG.PRT_GET is present.

The issue can be worked around by changing the order of PRT and PRT_GET in the source code (same applies for TP5 and TP5_GET, but explicitly excluding _GET-messages from the list of parseable messages might be a better idea.

Interesting side effect: The Exception message wrongly states "Message not fully consumed while parsing a UBX.CFG.TP5_GET!", even though this should read UBX.CFG.PRT_GET. I did not track down what causes this, but it threw me off first while looking for the root cause of the problem.

Test code:

import serial
import time
import UBX
from UBXManager import UBXManager

ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=None)
manager = UBXManager(ser, debug=True)
manager.start()

# query port settings - response triggers bug
time.sleep(1)
prt = UBX.CFG.PRT_GET(b'\x01')
manager.send(prt.serialize())

thasti avatar Oct 19 '18 21:10 thasti

@thasti If I understand correctly the "addGet" decorator creates a "Get" version of UBXMessage with given _class and _id, so the best way is to pass payload to the UBXMessage init. I managed to do it with:

  1. extended version of addGet decorator, that inits Get class with payload
def addGetExt(cls):
    """Decorator that adds a Get function to the subclass."""
    class Get(UBXMessage):
        def __init__(self,payload=b''):
            # this only works because class and module have the same name!
            _class = eval(cls.__module__)._class
            UBXMessage.__init__(self, _class, cls._id, payload)
    setattr(cls, "Get", Get)
    return cls

#usage
msg = UBX.CFG.PRT.Get(payload=b'\x03')

  1. modified decorator to accept payload - this is rather static solution, but works for specific configuration
def addGetPayload(payload):
    def addGet(cls):
        """Decorator that adds a Get function to the subclass."""
        class Get(UBXMessage):
            def __init__(self):
                # this only works because class and module have the same name!
                _class = eval(cls.__module__)._class
                UBXMessage.__init__(self, _class, cls._id, payload)
        setattr(cls, "Get", Get)
        return cls
    return addGet

this decorator then is used:

@addGetPayload(payload=b'\x03')
class PRT:

In both cases you'd need to modify decorators for classes in UBX, remove _GET subclasses and import new decorator functions in UBX/CFG (see import lines). Please see if it works for you.

oscarmezyk avatar Oct 29 '18 21:10 oscarmezyk