Skip to content

Fix broken class matching on receive #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 5, 2021

Conversation

alexwhittemore
Copy link
Contributor

Restructured match_prefixes to correctly match sequence number field
Added insurance that sequence number comes first to do so
Also caught the (macOS) case where adapter has no address (WIP)
Fixes #15

The macOS address bit fixes #14 but maybe ought to actually implement @dhalbert's suggestion of hashing the UUID-style address rather than just smashing zeros into it.

Restructured match_prefixes to correctly match sequence number field
Added insurance that sequence number comes first to do so
Also caught the (macOS) case where adapter has no address (WIP)
Fixes adafruit#15
@alexwhittemore
Copy link
Contributor Author

Well that was an adventure.

@kattni kattni requested a review from a team January 4, 2021 23:22
Copy link
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@tannewt tannewt merged commit 4fd93eb into adafruit:master Jan 5, 2021
adafruit-adabot added a commit to adafruit/Adafruit_CircuitPython_Bundle that referenced this pull request Jan 8, 2021
Updating https://github.com/adafruit/Adafruit_CircuitPython_ADS1x15 to 2.2.5 from 2.2.4:
  > Merge pull request adafruit/Adafruit_CircuitPython_ADS1x15#62 from WizardTim/WizardTim-continuous-fix-1

Updating https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel to 6.0.1 from 6.0.0:
  > Merge pull request adafruit/Adafruit_CircuitPython_NeoPixel#100 from funkfinger/master

Updating https://github.com/adafruit/Adafruit_CircuitPython_BLE_BroadcastNet to 0.10.2 from 0.10.1:
  > Merge pull request adafruit/Adafruit_CircuitPython_BLE_BroadcastNet#16 from alexwhittemore/master

Updating https://github.com/adafruit/Adafruit_CircuitPython_Bundle/circuitpython_library_list.md to NA from NA:
  > Added the following libraries: Adafruit_CircuitPython_OAuth2
@tannewt
Copy link
Member

tannewt commented Jan 26, 2021

Looks like move_to_end isn't available on CircuitPython (#18). What board did you test this on?

@alexwhittemore
Copy link
Contributor Author

alexwhittemore commented Jan 26, 2021

Oh damn - I was SENDING with a bluefruit sense, BUT I was receiving (where this code is relevant) on a RasPi.

Edit: No, that doesn't seem right, because the whole point is to ensure proper order when SENDING. I'll keep looking and see what the idea was.

@alexwhittemore
Copy link
Contributor Author

Now that I've thought about this a bit more - I recall I TRIED to ensure byte order on the sending side by ensuring that the sequence number field was the first set, but that didn't work for some reason I don't recall - something like, in one spot, if the sequence number was already set, it wouldn't get updated.

I'm thinking it's possible that if this bug IS only on the sending side, I might have switched it up and LISTENED with the feather, and advertised with the Pi so I could interactively debug it.

@alexwhittemore
Copy link
Contributor Author

I DID just verify that sending fails on a feather sense. I think I was actually debugging this before on my mac, not even on raspberry pi.

Anywho, I'm really not sure what to do about it - the sequence number field has to be at the start, or else prefix matching will fail. Or the prefix could be relaxed, but I think that means other advertisements will potentially get flagged as AdafruitSensorMeasurements?

@tannewt
Copy link
Member

tannewt commented Jan 26, 2021

Could you sort it with code instead of a function call?

@alexwhittemore
Copy link
Contributor Author

Actually, it looks like OrderedDict is implemented in pure python, so I suppose I can just monkey-patch in the OEM method right here. I'll give it a test.

@alexwhittemore
Copy link
Contributor Author

Hmm. I'm not sure what's going on with CircuitPython's OrderedDict (can't search for it on github because fork, so I have no clue what it looks like) but in any event, 1) it looks like CP's python implementation doesn't actually support monkeypatching, which I guess is probably a memory thing? 2) OrderedDict is missing self.__map and self.__root which are required for ordering in the first place. It's a little as if OrderedDict throws away all the stuff that makes an OrderedDict ordered?

For background, this is futzing around on a feather REPL:

>>> foo = OrderedDict({1:"first", 8:"eighth"})
>>> foo.__root
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'OrderedDict' object has no attribute '__root'
>>> foo.__map
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'OrderedDict' object has no attribute '__map'
>>> def printfoo(self):
...     print(self)
... 
>>> OrderedDict.printfoo = printfoo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'type' object cannot assign attribute 'printfoo'

When I tried to add the following to broadcastnet:

def move_to_end(self, key, last=True):
    '''Move an existing element to the end (or beginning if last is false).
    Raise KeyError if the element does not exist.
    '''
    link = self.__map[key]
    link_prev = link.prev
    link_next = link.next
    soft_link = link_next.prev
    link_prev.next = link_next
    link_next.prev = link_prev
    root = self.__root
    if last:
        last = root.prev
        link.prev = last
        link.next = root
        root.prev = soft_link
        last.next = link
    else:
        first = root.next
        link.prev = root
        link.next = first
        first.prev = soft_link
        root.next = link

and

    def __bytes__(self):
        """The raw packet bytes."""
        # Must reorder the ManufacturerData contents so the sequence number field is always first.
        # Necessary to ensure that match_prefixes works right to reconstruct on the receiver.
        move_to_end(self.data_dict[255].data, 3, last=False)
        return super().__bytes__()

(so not monkey patching, just private access) I get

code.py output:
This is BroadcastNet sensor: db966a80fab2
<AdafruitSensorMeasurement temperature=23.0 battery_voltage=4176 >
Traceback (most recent call last):
  File "code.py", line 36, in <module>
  File "/lib/adafruit_ble_broadcastnet.py", line 37, in broadcast
  File "adafruit_ble/__init__.py", line 183, in start_advertising
  File "/lib/adafruit_ble_broadcastnet.py", line 196, in __bytes__
  File "/lib/adafruit_ble_broadcastnet.py", line 68, in move_to_end
AttributeError: 'OrderedDict' object has no attribute '__map'

@tannewt
Copy link
Member

tannewt commented Jan 27, 2021

I think you've found that OrderedDict is implemented in a different way in MicroPython. MicroPython isn't based on CPython's code and is also licensed differently as well. (So be careful mixing code.)

The easier approach would be to sort the keys before encoding them in bytes. (IIRC the sequence number is the lowest of them.)

@alexwhittemore
Copy link
Contributor Author

Fair point about mixed licensing.

My point about sorting keys at all is that it seems the only practical way to sort them up at the library level is to tightly control the order in which they're added, but ensuring the sequence number is always added first by, for instance, setting it to Null or something in the constructor breaks other aspects of the library design (I forget exactly how).

@tannewt
Copy link
Member

tannewt commented Jan 28, 2021

I don't understand why you need the dictionary ordered when all you are about is that they are encoded in order. (I think you'd change: https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/master/adafruit_ble/advertising/__init__.py#L57-L72 to sort the keys first.)

@alexwhittemore
Copy link
Contributor Author

Oh - well, because that code isn't in this library. I guess you're suggesting monkey-patch in a modified adafruit_ble.advertising.encode_data() to serve the purposes of BroadcastNet? Given my experience before trying to monkey patch in circuitpython - is that even possible? If it is, it would break the case where we load BroadcastNet but then try to transmit a non-broadcastnet advertisement (I think) - unless I'm missing your idea.

@tannewt
Copy link
Member

tannewt commented Jan 29, 2021

No, I'm suggesting you change the BLE library so it consistently orders keys. Much simpler than monkey patching.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants