-
Notifications
You must be signed in to change notification settings - Fork 34
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
Plumb through new "matched bluetooth vehicle" from the phone to the server and to the trip #1062
Comments
We need to make some design decisions before implementing this feature. Decision 1: Do we need to store this as a separate object, or should we treat it as a statemachine transition? I think that is not the best option. Some challenges with that approach:
The third is not a great argument, since the visit transitions may also not trigger an FSM transition if the geofence has already triggered. But I think that the second is really compelling. So let's go with a separate object. Decision 2: Should the separate object be a Decision 3: What should the data structure look like?
|
For decision 3, would this be the structure of the stored I thought this would only include the bluetooth beacon information. And I thought the vehicle information would be in the dynamic config. Something like this: {
...
"vehicle_identities": [
{
"value": "e_car_1234",
"bluetooth_ids": ["00:1A:2B:3C:4D:5E"],
"name": "Leaf A",
"baseMode":"E_CAR",
"met_equivalent":"IN_VEHICLE",
"kgCo2PerKm": 0.08216,
"vehicle_info": {
"make": "Nissan",
"model": "Leaf",
"year": 2018,
"license": "AAA 1234"
}
},
{
"value": "e_car_5678",
"bluetooth_ids": ["00:1A:2B:3C:4D:5F"],
"name": "Leaf B",
"baseMode":"E_CAR",
"met_equivalent":"IN_VEHICLE",
"kgCo2PerKm": 0.08216,
"vehicle_info": {
"make": "Nissan",
"model": "Leaf",
"year": 2020,
"license": "BBB 5678"
}
}
],
...
} |
If we are going to support BLE and Bluetooth classic (maybe in the future), and these have different properties, what about having 2 different types: |
That could also work. Per my conversation with @the-bay-kay, since we have to have the full list of UUIDs to "register" them, it would be easy enough to have all the information in the Let us think through the various options and their pros and cons. Must happen native code must receive a list of BLE beacons so that it can create the appropriate "regions". The list must come from the dynamic config so that it can be different for each fleet.
Question 2: What do the background objects look like?
For the decisions: @JGreenlee do you have a preference? |
Also, I had a question around must happen (aka native code must receive a list of BLE beacons so that it can create the appropriate "regions"). From the iOS docs, it looks like we can have multiple beacons with the same UUID and different major and minor values https://developer.apple.com/documentation/corelocation/determining_the_proximity_to_an_ibeacon_device?language=objc
... The most important point to me here is the part which says "your app’s beacons". Since the beacons are really intended for use with OpenPATH, maybe we can configure them all with the same UUID, that is hardcoded (or put into an app-level config) into OpenPATH. Then we only need to listen for one UUID, note that the major and minor values can be wildcards in the scan. This might also be good for long-term scalability, so that we don't run into the maximum number of geofences we can create. At least for location-based regions on iOS, this limit seems to be 20.
I did not find a similar number of beacon-based regions, but the message above says "Core Location" and this SO post claims that the limit also applies to beacon regions Further, the example for how to use the beacons @louisg1337, is it possible for us to experiment with multiple beacons configured with the same UUID? If we leave the major and minor keys as blank while registering the region, does the callback return them? Or do we have to start scanning to get that information? It is fairly clear that the major and minor values will show up if we start scanning. We can't test this with the current UI since the hardcoded values have the major and minor key defined. Fortunately I made a change last night that allows us to put new entries into the list. Or of course, you can just edit the current app to remove major and minor keys. We need to answer this first thing because it will affect everything downstream If we are going to search for a hardcoded (or app-level configured) UUID, then the native code does not need a list of UUIDs from the dynamic config, which means that we have a strong vote for the second option for Question 2. |
After seeing the approaches for Q2 laid out, I don't have a strong preference either. One reason I might express a slight preference for (2) is that it allows the native code to be agnostic of vehicles. It would only have to worry about beacons, so the native code would be a bit simpler and more generic.
Would we necessarily have to do this? What if the trip object just had |
- Creates a new set of fields at the botton of the page for the UUID, identifier, major and minor fields - Adds a new "Add" button - When the "Add" button is pressed, we add a new UUID entry to the default list - After the new entry is added, we clear out the old values to prepare for a new entry Testing done: Added three new entries. They were displayed in the list e-mission/e-mission-docs#1062 (comment)
The plugin that we use requires both an identifier and a UUID. This is because the call to create a beacon used to require both a UUID and an identifier. However, that call is now deprecated. We should not be using Even in the old days, the identifier was static and hardcoded
Note also that the UUID in the new method is of type NSUUID, which is a 128-bit value created to conform to RFC 4122. It cannot be an arbitrary string. So from the example above, we must use Since the identifier is still required in the plugin, I am going to use a hardcoded value for it, Note also that the way to avoid specifying the major and minor values is to use |
e-mission/e-mission-docs#1062 (comment) Testing done: - Added values with and without a major and minor value e-mission/e-mission-docs#1062 (comment)
I am working on a PR to simulate BLE scans e-mission/e-mission-phone#1140 |
Instead of the statically coded name/identifier This involved changing the text size so that we could see the UUID I wonder if we should make the UUID the subtitle and the major:minor the title Testing done: e-mission/e-mission-docs#1062 (comment)
It's going to be a bit tricky to know how to display the matches to see if we have minor and major values without running the code, since there is not much documentation (from the iOS level up) around what this may look like. Let's just stringify the result and display it for now. |
I'm not quite sure, but I can definitely test this out especailly since I have 2 beacons now. I also really do like this idea of using one UUID and variable major/minor values a lot better than creating X amount of regions. The latter seems a bit wasteful resource wise, and it also complicates things quite a bit. Jack also made a great point here how we may need to change up our implementation a bit, but it doesn't seem like too crazy of a pivot. I'll give all of this a go and update accordingly as I get some answers! |
Quick follow up to my comment above, I did two tests.
With that in mind, looks like a fixed UUID and variable major/minor values could be the way to go! |
We don't know what the result will look like when there are multiple beacons with the same UUID, so we just stringify and display the result. We cannot test this without having an actual BLE beacon, so I have added a "fake callback button to simulate a callback" Changes: - stringify the result in `didDetermineStateForRegion` and pass it in to `setRangeStatus` - Change the BluetoothCard to display the `device.result` as Card.Content - Add a new button to fake a callback for a device by getting the delegate and invoking the `didDetermineStateForRegion` method on it with the device - Remove the existing result in the device before invoking the callback to avoid nested results Testing done: e-mission/e-mission-docs#1062 (comment)
Given these results, I think that the answers to the questions above are: We can have a single hardcoded value for e-mission/OpenPATH
Question 2: What do the background objects look like?
@JGreenlee you also need to change the dynamic config to have major/minor values instead of the |
Here is an updated config proposal. I changed I added {
...
"vehicle_identities": [
{
"value": "e_car_1234",
"bluetooth_major_minor": ["dfc0:1234"],
"name": "Leaf A",
"baseMode":"E_CAR",
"met_equivalent":"IN_VEHICLE",
"kgCo2PerKm": 0.08216,
"vehicle_info": {
"type": "car",
"make": "Nissan",
"model": "Leaf",
"engine": "BEV",
"year": 2018,
"license": "AAA 1234"
}
},
{
"value": "e_car_5678",
"bluetooth_major_minor": ["dfc0:5678"],
"name": "Leaf B",
"baseMode":"E_CAR",
"met_equivalent":"IN_VEHICLE",
"kgCo2PerKm": 0.08216,
"vehicle_info": {
"type": "car",
"make": "Nissan",
"model": "Leaf",
"engine": "BEV",
"year": 2020,
"license": "BBB 5678"
}
}
],
...
} |
It is so cool that this deployment name lent itself so well to hex 😄 |
To pick this up again, with the proposed changes, the new background object will have a key of
While creating raw sections, we will use the values for section segmentation. Exact algorithm TBD, but at a high level, if we see a change in BLE beacons, it is likely a change from one vehicle to another or from a vehicle to walking. Design decision: should we fill in the vehicle values right here, or fill in the major:minor key here and fill in the full value for the cleaned section? @JGreenlee As an aside, I think we should standardize on camelCase or under_score_case. Right now, I think we should also have a summary of the values in the confirmed and composite trips, similar to the current I think that is about it. I am now starting with adding the new data type and plumbing it through to the server. Since we do not generate these objects in the native code yet, I will simulate them using the UI. After that is done, I just need to figure out an initial algorithm for There will be a ton of tuning that will need to happen with real world testing next month to address all the weird corner cases that will undoubtedly show up. |
Design decision These feels foundational enough that I am going to create a new data type instead of storing it as JSON. This will allow us to read the fields from the object directly instead of parsing through a JSON object and dealing with exceptions. We will use the object in the FSM, so this seems fairly important. |
Ah, I did it that way to match the fields in the label options spec. https://github.com/e-mission/nrel-openpath-deploy-configs/blob/main/label_options/example-study-label-options.json That inconsistency has been around for a while, even before label options were customizable. There are also more cases of this elsewhere in the dynamic config, and some places where a third one, "kebab-case" is used. I don't know an easy way to fix it. I think we would have to implement a temporary backwards compat patch on the phone to convert local configs to one standard case, regardless of what case the source used. Then we could update all the configs on GitHub to use the standard case. And remove the temporary patch after we're sure everyone has gotten it. For now, I think we should just decide what 'case' we want to end up with and use that for new fields going forward. So for the |
Ah, that is a good enough reason then. Note that the label options are also read in python now, to support custom labels in the public dashboard. Let's keep it unchanged for historical reasons and deal with unifying it later. Much later. When the rest of the code is so polished that we have nothing else to polish. |
- Move the major and minor boxes to the same row to take up less space - Split the fake callbacks into enter, exit and range - generalize `fakeMonitorCallback` to work for both enter and exit - do not call rangeCallback directly from monitor callback since there are now separate buttons - for the range callback, use the device major and minor values if they exist - for the range callback, reset monitor and range results to avoid duplicates This is the first step for testing the data models defined in: e-mission/e-mission-docs#1062 (comment)
I think there should be an additional field in the config spec to differentiate programs that will only track Bluetooth-sensed trips vs. programs that will still track all trips and just include Bluetooth sensing as a supplement to the existing tracking. The "fleet" version of OpenPATH is first scenario, but I think we want to leave the door open for the second scenario too. {
...
"tracking": {
"bluetooth_only": true
},
...
} In total, {
...
"vehicle_identities": [
{
"value": "e_car_1234",
"bluetooth_major_minor": ["dfc0:1234"],
"name": "Leaf A",
"baseMode":"E_CAR",
"met_equivalent":"IN_VEHICLE",
"kgCo2PerKm": 0.08216,
"vehicle_info": {
"type": "car",
"make": "Nissan",
"model": "Leaf",
"engine": "BEV",
"year": 2018,
"license": "AAA 1234"
}
},
{
"value": "e_car_5678",
"bluetooth_major_minor": ["dfc0:5678"],
"name": "Leaf B",
"baseMode":"E_CAR",
"met_equivalent":"IN_VEHICLE",
"kgCo2PerKm": 0.08216,
"vehicle_info": {
"type": "car",
"make": "Nissan",
"model": "Leaf",
"engine": "BEV",
"year": 2020,
"license": "BBB 5678"
}
}
],
"tracking": {
"bluetooth_only": true
},
...
} |
I have now created separate buttons for simulating bluetooth transitions. I now have a quick set of design decisions to make. How do I generate and save the values? There are a couple of options:
I am currently leaning towards (2) because:
|
So far, I have primarily been testing this on iOS. I tested on android before pushing the changes to simulate the data storage and transitions, and found a few differences. I have accounted for some of the differences in the code, but we may need to either unify the plugin or do some additional cleanup later.
|
Consistent with the plans in: e-mission/e-mission-docs#1062 (comment) add support for storing simulated BLE responses and transitions in the usercache Concretely: - when there is a monitor entry event, we store a `bluetooth_ble` entry with `REGION_ENTER` - when there is a range event, we store a `bluetooth_ble` entry with `RANGE_UPDATE`. if we receive three consecutive range updates within 5 minutes, we generate a `BLE_BEACON_FOUND` transition - when there is a monitor exit event, we store a `bluetooth_ble` entry with `REGION_EXIT` and generate a `BLE_BEACON_LOST` transition Note that this has some fixes found while testing android: e-mission/e-mission-docs#1062 (comment) In addition, the callbacks exposed that the format of the range callback that we were using earlier was incorrect. The `beacons` array is at the same level as the `region` Testing done on both iOS and android: - Scan for BLE beacons - Region enter - Range (3-4 times); see `ble_found` transition - Region exit; see `ble_lost` transition
@shankari Are they i) decimal integers or ii) strings of hexadecimal digits ? |
@JGreenlee decimal integers The raw iOS values return |
Trying out the native wrapper classes with a hack
and am running into some very weird behavior. The first save (of |
Removing the for loop, we crash while exiting the
I bet this is because of the inheritance from |
Confirmed that just after changing Also need to convert the UUID to a string instead of NSUUID so that it can be serialized. |
…etooth code The data format is very similar to the beacon object in iOS. We consciously patterned the object to be similar to `CLBeacon` since the alt-beacon library for android is an effort to be compatible with iOS to the extent possible. Our only changes are to change the fields in iOS to be more easily serializable e-mission/e-mission-docs#1062 (comment) e-mission/e-mission-docs#1062 (comment) Testing done: After adding a hack to read/write messages on every ble_found transition, e-mission/e-mission-phone#1144 (comment) was able to verify that both read and write worked properly Android: ``` sqlite> select * from userCache where type == "sensor-data"; 1713026512.887||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026512887E9} ``` ``` 04-13 09:45:21.365 18332 18332 I TripDiaryStateMachineRcvr: TripDiaryStateMachineReciever onReceive(android.app.ReceiverRestrictedContext@9e3bd75, Intent { act=local.transition.ble_beacon_found flg=0x10 pkg=edu.berkeley.eecs.emission cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMachineReceiver }) called 04-13 09:45:21.385 18332 18332 I TripDiaryStateMachineService: Service created. Initializing one-time variables! 04-13 09:45:21.396 18332 18332 D TripDiaryStateMachineService: service started with flags = 0 startId = 1 action = local.transition.ble_beacon_found 04-13 09:45:21.404 18332 18332 D TripDiaryStateMachineService: after reading from the prefs, the current state is local.state.waiting_for_trip_start 04-13 09:45:21.424 18332 18332 D BuiltinUserCache: Added value for key statemachine/transition at time 1.713026721405E9 04-13 09:45:21.432 18332 18332 D TripDiaryStateMachineService: handleAction(local.state.waiting_for_trip_start, local.transition.ble_beacon_found) called 04-13 09:45:21.448 18332 18332 D BuiltinUserCache: Added value for key background/battery at time 1.713026721433E9 04-13 09:45:21.464 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.713026721449E9 04-13 09:45:21.479 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.713026721465E9 04-13 09:45:21.493 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.71302672148E9 04-13 09:45:21.520 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.713026721493E9 04-13 09:45:21.532 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.71302672152E9 04-13 09:45:21.546 18332 18332 D BuiltinUserCache: Added value for key background/bluetooth_ble at time 1.713026721533E9 04-13 09:45:21.548 18332 18332 I System.out: [BLE native] Found 5 entries 04-13 09:45:21.548 18332 18332 I System.out: [BLE native] First entry is edu.berkeley.eecs.emission.cordova.tracker.wrapper.BluetoothBLE@afc8f12 last entry is edu.berkeley.eecs.emission.cordova.tracker.wrapper.BluetoothBLE@b0c96e3 04-13 09:45:21.548 18332 18332 I System.out: [BLE native] while handling transition local.transition.ble_beacon_found ``` ``` sqlite> select * from userCache where type == "sensor-data"; 1713026512.887||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026512887E9} 1713026710.433||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026710432E9} 1713026712.809||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026712808E9} 1713026721.433||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026721433E9} 1713026721.449||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"REGION_ENTER","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"0e27c613-ff78-486c-b523-950776777d16"} 1713026721.465||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"d8635e19-4133-493c-b2fd-c04fdd920c6a"} 1713026721.48||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"5efe9e8b-ce30-4417-80e3-79ce9ff53ca9"} 1713026721.493||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"d9283862-2926-4a0c-b377-0e80718e6525"} 1713026721.52||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"cef2f3df-30c5-49d5-8707-2f234530ef18"} 1713026721.533||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"accuracy":100.0,"eventType":"RANGE_UPDATE","major":4538,"minor":1256,"proximity":"ProximityNear","rssi":10,"ts":1.713026721E9,"uuid":"326876e5-9869-407d-976c-2de438bec330"} 1713026800.123||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026800121E9} 1713026800.788||America/Los_Angeles|sensor-data|background/battery||{"android_health":"GOOD","android_plugged":"UNKNOWN","android_technology":"Li-ion","android_temperature":250,"android_voltage":250,"battery_level_pct":100.0,"battery_status":4,"ts":1.713026800788E9} ``` iOS ``` $ sqlite3 /Users/kshankar/Library/Developer/CoreSimulator/Devices/42B6F8A1-A925-4CAD-A48A-4835D69595ED/data/Containers/Data/Application/13D8F42C-921C-4754-9420-89C4350CE63F/Library/LocalDatabase/userCacheDB SQLite version 3.39.5 2022-10-14 20:58:05 Enter ".help" for usage hints. sqlite> select * from userCache where type == "sensor-data"; sqlite> ``` ``` 2024-04-13 09:34:56.566596-0700 emission[17352:9891606] In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP 2024-04-13 09:34:56.566796-0700 emission[17352:9891606] DEBUG: In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP 2024-04-13 09:34:56.568786-0700 emission[17352:9891606] data has 92 bytes, str has size 92 2024-04-13 09:34:56.570995-0700 emission[17352:9891606] data has 69 bytes, str has size 69 2024-04-13 09:35:12.781616-0700 emission[17352:9891606] data has 177 bytes, str has size 177 2024-04-13 09:35:12.784409-0700 emission[17352:9891606] data has 176 bytes, str has size 176 2024-04-13 09:35:12.786659-0700 emission[17352:9891606] data has 177 bytes, str has size 177 2024-04-13 09:35:12.789160-0700 emission[17352:9891606] data has 177 bytes, str has size 177 2024-04-13 09:35:12.791407-0700 emission[17352:9891606] data has 176 bytes, str has size 176 2024-04-13 09:35:12.793945-0700 emission[17352:9891606] data has 177 bytes, str has size 177 2024-04-13 09:35:17.900501-0700 emission[17352:9891606] [BLE native] Found 5 entries 2024-04-13 09:35:17.900666-0700 emission[17352:9891606] [BLE native] First entry is <BluetoothBLE: 0x6000010b8c80>, last entry is <BluetoothBLE: 0x6000010ba3f0> 2024-04-13 09:35:17.900773-0700 emission[17352:9891606] [BLE native] while handling transition T_BLE_BEACON_FOUND 2024-04-13 09:35:20.434450-0700 emission[17352:9891606] Got unexpected transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP, ignoring ``` ``` sqlite> select * from userCache where type == "sensor-data"; 1713026112.78185||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"REGION_ENTER","ts":1713026111.2200561,"uuid":"35355A74-E587-4F09-B114-D6718E925DC0","proximity":"ProximityNear","accuracy":100} 1713026112.78457||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.784344,"uuid":"3448C47C-BE3B-40B8-A11F-4BD3CC1C2F63","proximity":"ProximityNear","accuracy":100} 1713026112.78698||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.7865958,"uuid":"A2778875-CDF8-4AA8-A2E1-0639C9B4B159","proximity":"ProximityNear","accuracy":100} 1713026112.78947||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.7891002,"uuid":"EE5129DA-6D64-4B25-BA68-3FB3D0FE6BD6","proximity":"ProximityNear","accuracy":100} 1713026112.79173||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.791352,"uuid":"5B6A86F3-BE4D-498E-A3FD-0CAB6039914E","proximity":"ProximityNear","accuracy":100} 1713026112.7941||America/Los_Angeles|sensor-data|background/bluetooth_ble||{"major":4538,"minor":1256,"rssi":10,"eventType":"RANGE_UPDATE","ts":1713026112.7938981,"uuid":"71E73F8E-4138-4346-8E5F-D05E045A6D69","proximity":"ProximityNear","accuracy":100} ```
These are the server side changes related to e-mission/e-mission-docs#1062 The changes are fairly straightforward, and consistent with https://github.com/e-mission/e-mission-docs/blob/2665b39e1335ea04896b6944a4a065e7887b6cdc/docs/dev/back/adding_a_new_data_type.md?plain=1#L4 Concretely: - we add a new `bluetoothble` wrapper - we add references to it to `entry.py` and `builtin_timeseries.py` - we add formatters for both android and iOS A new wrinkle this time is that we are modifying the FSM, so there are also new transitions. Those needed to be added to the enums in the transition wrapper, and to the maps in the formatters so that the enums could be created properly. Bonus fix: check for the `None` transition properly on android and iOS to avoid spurious errors ``` >>> broken_transition_example = {'_id': ObjectId('661b129fc271a44bb612b464'), 'metadata': {'time_zone': 'America/Los_Angeles', 'plugin': 'none', 'write_ts': 1713050268.574551, 'platform': 'ios', 'read_ts': 0, 'key': 'statemachine/transition', 'type': 'message'}, 'user_id': UUID('f1aaae55-fc42-4527-bf7f-33f84d7c8c2f'), 'data': {'currState': 'STATE_ONGOING_TRIP', 'transition': None, 'ts': 1713050268.574418}} >>> broken_transition_example_entry = ad.AttrDict(broken_transition_example) >>> enufit.format(broken_transition_example_entry) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/kshankar/Desktop/data/e-mission/gis_branch_tests/emission/net/usercache/formatters/ios/transition.py", line 64, in format data.transition = transition_map[entry.data.transition].value KeyError: None ----- fixed code ------ >>> importlib.reload(enufit) <module 'emission.net.usercache.formatters.ios.transition' from '/Users/kshankar/Desktop/data/e-mission/gis_branch_tests/emission/net/usercache/formatters/ios/transition.py'> >>> enufit.format(broken_transition_example_entry) AttrDict({'_id': ObjectId('661b129fc271a44bb612b464'), 'user_id': UUID('f1aaae55-fc42-4527-bf7f-33f84d7c8c2f'), 'metadata': AttrDict({'time_zone': 'America/Los_Angeles', 'plugin': 'none', 'write_ts': 1713050268.574551, 'platform': 'ios', 'read_ts': 0, 'key': 'statemachine/transition', 'type': 'message', 'write_local_dt': LocalDate({'year': 2024, 'month': 4, 'day': 13, 'hour': 16, 'minute': 17, 'second': 48, 'weekday': 5, 'timezone': 'America/Los_Angeles'}), 'write_fmt_time': '2024-04-13T16:17:48.574551-07:00'}), 'data': AttrDict({'curr_state': 2, 'transition': None, 'ts': 1713050268.574551, 'local_dt': AttrDict({'year': 2024, 'month': 4, 'day': 13, 'hour': 16, 'minute': 17, 'second': 48, 'weekday': 5, 'timezone': 'America/Los_Angeles'}), 'fmt_time': '2024-04-13T16:17:48.574551-07:00'})}) ``` Testing done: Used the corresponding changes in e-mission/e-mission-phone#1144 to simulate BLE as follows: - Region exit - A few range updates until the `ble_beacon_found` transition was generated - Turned on location mocking from the android and iOS simulators, and manually generated the start trip transition on android - Clicked "range update" at random times during the simulated trip - BLE beacon lost from the UI - Turn off location mocking - Force end trip transition Testing Results: Android: ``` START 2024-04-13 17:36:02.096342 POST /usercache/put END 2024-04-13 17:36:02.313529 POST /usercache/put ebc13f1b-671b-4094-bce6-fed342da7e9c 0.2171182632446289 START 2024-04-13 17:36:02.583812 POST /usercache/get END 2024-04-13 17:36:02.591868 POST /usercache/get ebc13f1b-671b-4094-bce6-fed342da7e9c 0.007989168167114258 ``` ``` >>> edb.get_usercache_db().count_documents({"metadata.key": "background/bluetooth_ble"}) 57 >>> edb.get_timeseries_db().count_documents({"metadata.key": "background/bluetooth_ble"}) 0 ``` ``` 2024-04-13 17:37:57,635:DEBUG:140704655566784:write_ts = 1713054811.255 2024-04-13 17:37:57,635:DEBUG:140704655566784:module_name = emission.net.userca che.formatters.android.bluetooth_ble 2024-04-13 17:37:57,636:DEBUG:140704655566784:write_ts = 1713054811.294 2024-04-13 17:37:57,636:DEBUG:140704655566784:module_name = emission.net.userca che.formatters.android.bluetooth_ble 2024-04-13 17:37:57,636:DEBUG:140704655566784:write_ts = 1713054811.316 2024-04-13 17:37:57,636:DEBUG:140704655566784:module_name = emission.net.userca che.formatters.android.bluetooth_ble 2024-04-13 17:37:57,637:DEBUG:140704655566784:write_ts = 1713054811.339 2024-04-13 17:37:57,637:DEBUG:140704655566784:module_name = emission.net.userca che.formatters.android.bluetooth_ble 2024-04-13 17:37:57,637:DEBUG:140704655566784:write_ts = 1713054811.369 ``` ``` >>> edb.get_usercache_db().count_documents({"metadata.key": "background/bluetooth_ble"}) 0 >>> edb.get_timeseries_db().count_documents({"metadata.key": "background/bluetooth_ble"}) 57 ``` iOS ``` START 2024-04-13 16:17:50.707151 POST /usercache/put START 2024-04-13 16:17:50.737703 POST /usercache/get END 2024-04-13 16:17:50.763880 POST /usercache/get f1aaae55-fc42-4527-bf7f-33f84d7c8c2f 0.026064157485961914 END 2024-04-13 16:17:51.340867 POST /usercache/put f1aaae55-fc42-4527-bf7f-33f84d7c8c2f 0.6329052448272705 ``` ``` >>> edb.get_usercache_db().count_documents({"metadata.key": "background/bluetooth_ble"}) 74 ``` ``` DEBUG:root:module_name = emission.net.usercache.formatters.ios.bluetooth_ble DEBUG:root:write_ts = 1713050204.075974 DEBUG:root:module_name = emission.net.usercache.formatters.ios.bluetooth_ble DEBUG:root:write_ts = 1713050204.077563 DEBUG:root:module_name = emission.net.usercache.formatters.ios.bluetooth_ble DEBUG:root:write_ts = 1713050204.078974 DEBUG:root:module_name = emission.net.usercache.formatters.ios.bluetooth_ble DEBUG:root:write_ts = 1713050207.32417 DEBUG:root:module_name = emission.net.usercache.formatters.ios.bluetooth_ble DEBUG:root:write_ts = 1713050207.326033 ``` ``` >>> edb.get_usercache_db().count_documents({"metadata.key": "background/bluetooth_ble"}) 0 >>> edb.get_timeseries_db().count_documents({"metadata.key": "background/bluetooth_ble"}) 74 >>> edb.get_timeseries_error_db().count_documents({"metadata.key": "background/bluetooth_ble"}) 0 ```
I think that the plumbing support works fairly well, so I'm going to review and try to merge at least the android implementation. We should be able to test that using the UI simulator as well. After I am done with that, I can try to merge the phone display changes and,if it works, potentially push out to staging. |
One challenge with merging in the android changes (and potentially the iOS changes down the road), is that the transition names are different. We should merge to a separate branch so that we can pick unified names and test before merging. Also, at least the android version is not saving anything; we will need to fix that before the UI can work. The UI seems fairly basic and straightforward and supports multiple matching beacons. It just picks the beacon that occurs most frequently, so that should not be too bad. |
Starting with the android changes: here are the new transitions added:
The middle one maps nicely to our I don't think we need a separate transition for
And with that, I don't think we actually even need So the changes needed for the android version are:
|
The background/bluetooth_ble data type has "major" and "minor" as decimal integers. (e-mission/e-mission-docs#1062 (comment)) But in the vehicle_identities spec, we have major:minor pairs as hexadecimal strings. So we will need to convert. decimalToHex handles this and allows us to add padding, ensuring the converted major and minor are always 4 hex characters. (so 1 becomes "0001", 255 becomes "00ff", etc.)
While saving the values, I noticed that the alt-beacon values are not a direct match to the ibeacon values. Current assumptions are:
other values are copied over directly. I do not have a beacon yet (will order one tomorrow) so I don't have a way to test this. But I will at least make sure that it compiles before deploying to staging for others to test. |
Conflict was due to the newly added transitions Per e-mission/e-mission-docs#1062 (comment) resolved conflict by removing transitions from this PR, and changing `beacon_found` to `ble_beacon_found`
Consistent with e-mission/e-mission-docs#1062 (comment) - removed the newly added transitions - made them consistent with the plumbed through values - simplified the tracking logic in `TripDiaryStateMachineService` - minor refactoring to pull the isFleet check into an instance variable that we can reuse without having to read it every time Testing done: Non-fleet case - Start sending location - Not seeing any locations - Manually exit geofence ``` 04-13 22:51:45.962 23297 23297 I TripDiaryStateMachineRcvr: TripDiaryStateMachineReciever onReceive(android.app.ReceiverRestrictedContext@a7c085e, Intent { act=local.transition.exited_geofence flg=0x10 pkg=edu.berkeley.eecs.emission cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.TripDiaryStateMachineReceiver }) called 04-13 22:51:46.118 23297 23297 D TripDiaryStateMachineService: handleAction(local.state.waiting_for_trip_start, local.transition.exited_geofence) called 04-13 22:51:46.160 23297 23297 D TripDiaryStateMachineService: Geofence exit in non-fleet mode, starting location tracking ``` - Stop sending locations - Manually end trip ``` 04-13 22:54:41.792 23297 23297 D TripDiaryStateMachineService: handleAction(local.state.ongoing_trip, local.transition.stopped_moving) completed, waiting for async operations to complete ``` Fleet case - Start sending location - Not seeing any locations ``` $ grep LocationChangeIntentService /tmp/logcat.log | tail 04-13 22:54:32.555 23297 32018 D LocationChangeIntentService: FINALLY! Got location update, intent is Intent { cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.LocationChangeIntentService (has extras) } ``` - Manually exit geofence ``` 04-13 23:07:58.760 23297 23297 D TripDiaryStateMachineService: handleAction(local.state.waiting_for_trip_start, local.transition.exited_geofence) called 04-13 23:07:58.798 23297 23297 D TripDiaryStateMachineService: Geofence exit in fleet mode, checking for beacons before starting location tracking 04-13 23:07:58.807 23297 23297 D TripDiaryStateMachineService: handleAction(local.state.waiting_for_trip_start, local.transition.exited_geofence) completed, waiting for async operations to complete $ grep LocationChangeIntentService /tmp/logcat.log | tail 04-13 22:54:32.555 23297 32018 D LocationChangeIntentService: FINALLY! Got location update, intent is Intent { cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.LocationChangeIntentService (has extras) } ``` - Generate BLE event ``` 04-13 23:11:26.244 23297 23297 D TripDiaryStateMachineService: handleAction(local.state.waiting_for_trip_start, local.transition.ble_beacon_found) called 04-13 23:11:26.293 23297 23297 D TripDiaryStateMachineService: TripDiaryStateMachineReceiver handleTripStart(local.transition.ble_beacon_found) called 04-13 23:11:26.319 23297 23297 D TripDiaryStateMachineService: Found beacon in fleet mode, starting location tracking 04-13 23:11:26.387 23297 23297 D ActivityRecognitionActions: Starting activity recognition with interval = 30000 04-13 23:11:26.490 23297 23297 D TripDiaryStateMachineService: handleAction(local.state.waiting_for_trip_start, local.transition.ble_beacon_found) completed, waiting for async operations to complete 04-13 23:11:26.568 23297 23297 D TripDiaryStateMachineService: newState after handling action is local.state.ongoing_trip $ grep LocationChangeIntentService /tmp/logcat.log | tail 04-13 23:20:58.573 23297 14437 D LocationChangeIntentService: FINALLY! Got location update, intent is Intent { cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.LocationChangeIntentService (has extras) } $ grep LocationChangeIntentService /tmp/logcat.log | tail 04-13 23:22:28.651 23297 15241 D LocationChangeIntentService: FINALLY! Got location update, intent is Intent { cmp=edu.berkeley.eecs.emission/.cordova.tracker.location.LocationChangeIntentService (has extras) } ``` - Stop sending locations - Manually end trip ``` 04-13 23:23:24.649 23297 23297 D TripDiaryStateMachineService: handleAction(local.state.ongoing_trip, local.transition.stopped_moving) called 04-13 23:23:24.921 23297 23297 D TripDiaryStateMachineService: newState after handling action is local.state.waiting_for_trip_start 04-13 23:23:24.958 23297 23297 D TripDiaryStateMachineService: newState saved in prefManager is local.state.waiting_for_trip_start 04-13 23:22:54.173 23297 23297 D LocationChangeIntentService: onDestroy called 04-13 23:22:54.173 23297 23297 D LocationChangeIntentService: onDestroy called 04-13 23:22:54.173 23297 23297 D LocationChangeIntentService: onDestroy called ```
After my fixes to e-mission/e-mission-data-collection#219 I believe that the android side should work with demo quality. @JGreenlee if you pull the data collection plugin from the |
…d non-fleet cases The previous code had the non-fleet versions commented out. However, when we push this to production, we need to support both fleet and non-fleet versions. Making sure we keep the original functionality for the fleet case. Also, consistent with e-mission/e-mission-docs#1062 (comment) and e-mission/e-mission-docs#1062 (comment) used the standard transitions from the data model instead NOTE: that the `isFleet` is currently hardcoded, we need to read it properly from the dynamic config Testing done: 2024-04-14 14:47:18.722084-0700 emission[53427:11554764] In TripDiaryStateMachine, received transition T_EXITED_GEOFENCE in state STATE_WAITING_FOR_TRIP_START 2024-04-14 14:47:18.728881-0700 emission[53427:11554764] Got transition T_EXITED_GEOFENCE in state STATE_WAITING_FOR_TRIP_START with fleet mode, checking for beacons before starting location tracking 2024-04-14 14:50:45.818623-0700 emission[53427:11554764] In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_WAITING_FOR_TRIP_START 2024-04-14 14:50:45.823268-0700 emission[53427:11554764] Got transition T_BLE_BEACON_FOUND in state STATE_WAITING_FOR_TRIP_START with fleet mode, starting location tracking 2024-04-14 14:51:53.498788-0700 emission[53427:11554764] DEBUG: In TripDiaryStateMachine, received transition T_BLE_BEACON_LOST in state STATE_ONGOING_TRIP START 2024-04-14 14:51:53.685614 POST /usercache/put START 2024-04-14 14:51:53.723916 POST /usercache/get END 2024-04-14 14:51:53.779754 POST /usercache/get 4c152d72-a66d-4472-93f4-62b07c9e110f 0.055525779724121094 END 2024-04-14 14:51:54.061900 POST /usercache/put 4c152d72-a66d-4472-93f4-62b07c9e110f 0.3760108947753906 ``` 2024-04-14 14:57:32.309087-0700 emission[53427:11554764] DEBUG: In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_WAITING_FOR_TRIP_START 2024-04-14 14:57:32.313790-0700 emission[53427:11554764] Got transition T_BLE_BEACON_FOUND in state STATE_WAITING_FOR_TRIP_START with fleet mode, starting location tracking 2024-04-14 14:57:32.314013-0700 emission[53427:11554764] started fine location tracking with accuracy = -1 and distanceFilter = 1 2024-04-14 14:57:32.316945-0700 emission[53427:11554764] In TripDiaryStateMachine, received transition T_TRIP_STARTED in state STATE_WAITING_FOR_TRIP_START 2024-04-14 14:57:32.323449-0700 emission[53427:11554764] DEBUG: Moved from STATE_WAITING_FOR_TRIP_START to STATE_ONGOING_TRIP ``` ``` 2024-04-14 15:21:38.026872-0700 emission[53427:11554764] Got transition T_BLE_BEACON_FOUND in state STATE_WAITING_FOR_TRIP_START with fleet mode, starting location tracking 2024-04-14 15:28:31.763608-0700 emission[53427:11554764] Got transition T_TRIP_END_DETECTED in state STATE_ONGOING_TRIP with fleet mode, still seeing beacon but no location updates, ignoring 2024-04-14 15:29:27.951348-0700 emission[53427:11554764] DEBUG: In TripDiaryStateMachine, received transition T_BLE_BEACON_LOST in state STATE_ONGOING_TRIP 2024-04-14 15:29:27.977814-0700 emission[53427:11554764] In TripDiaryStateMachine, received transition T_END_TRIP_TRACKING in state STATE_ONGOING_TRIP 2024-04-14 15:29:27.986602-0700 emission[53427:11554764] In TripDiaryStateMachine, received transition T_TRIP_ENDED in state STATE_ONGOING_TRIP 2024-04-14 15:29:28.033050-0700 emission[53427:11554764] DEBUG: Moved from STATE_ONGOING_TRIP to STATE_WAITING_FOR_TRIP_START ``` ``` 2024-04-14 15:40:45.930239-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_EXITED_GEOFENCE in state STATE_WAITING_FOR_TRIP_START 2024-04-14 15:40:45.938188-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_TRIP_STARTED in state STATE_WAITING_FOR_TRIP_START 2024-04-14 15:40:45.968094-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_TRIP_STARTED in state STATE_ONGOING_TRIP 2024-04-14 15:40:45.978604-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_TRIP_RESTARTED in state STATE_ONGOING_TRIP 2024-04-14 15:40:45.987830-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_TRIP_RESTARTED in state STATE_ONGOING_TRIP 2024-04-14 15:41:12.876066-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP 2024-04-14 15:41:22.486777-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP 2024-04-14 15:41:24.861993-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP 2024-04-14 15:41:27.220213-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_BLE_BEACON_FOUND in state STATE_ONGOING_TRIP 2024-04-14 15:41:31.110132-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_BLE_BEACON_LOST in state STATE_ONGOING_TRIP 2024-04-14 15:41:37.788507-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_BLE_BEACON_LOST in state STATE_ONGOING_TRIP 2024-04-14 15:41:37.796704-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_BLE_BEACON_LOST in state STATE_ONGOING_TRIP 2024-04-14 15:41:53.319772-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_TRIP_END_DETECTED in state STATE_ONGOING_TRIP 2024-04-14 15:41:53.871614-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition (null) in state STATE_ONGOING_TRIP 2024-04-14 15:41:55.577755-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_END_TRIP_TRACKING in state STATE_ONGOING_TRIP 2024-04-14 15:41:55.583780-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_TRIP_ENDED in state STATE_ONGOING_TRIP 2024-04-14 15:41:56.022684-0700 emission[65361:11644200] In TripDiaryStateMachine, received transition T_DATA_PUSHED in state STATE_WAITING_FOR_TRIP_START ```
Consistent with e-mission/e-mission-docs#1062 (comment) store the BLE scan results so that we can use them while matching the mapping While storing the changes, I realized that the region enter and exit calls returned a CLBeaconRegion object and the range scans returned a CLRegion object. e-mission#220 (comment) This needed us to change the wrapper class as well: - Add a new initFromCLBeaconRegion that only copies non-range values and sets default values for the range values (proximity, accuracy and rssi) - Change the existing initFromCLBeacon to not need an event type, since it is only called for the scans, and the event type is always `RANGE_UPDATE` in that case. Testing done: - Compiled - Launched the app - Note that we cannot test this code without a physical beacon, but it is very straightforward and compiles. The only reason why it may fail is if values are not filled out in unexpected ways.
The current implementation on android starts ranging for beacons after a geofence exit and gives up after 4 retries. This is known to work when the beacon is available at the start of the trip - e.g. walking to work with a beacon tracks, walking to work without a beacon does not. But this may not work when the beacon is not at the start location. For example, if the geofence triggers as soon as a user leaves their work building, and then they have to walk several minutes to get to their car, the ranging will timeout and the trip will be missed. This is a bit less likely because our geofence radius is set to ~ half a block, but we still don't want to miss trips. We should really handle this with monitoring versus ranging, but for now, we will at least bump up the duration for which the ranging happens. The range callback is roughly once a minute https://github.com/AltBeacon/android-beacon-library/blob/7af8419c404329ac7dc6001917b5962d2cd53a11/lib/src/main/java/org/altbeacon/beacon/RangeNotifier.java#L39 ``` /** * Called once per second to give an estimate of the mDistance to visible beacons * @param beacons a collection of <code>Beacon<code> objects that have been seen in the past second * @param region the <code>Region</code> object that defines the criteria for the ranged beacons */ public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region); ``` So let's scan 5 * 60 times (roughly 5 minutes) before giving up This is the last change for e-mission/e-mission-docs#1062 before we move to staging
Per @the-bay-kay, things are not crashing. Going to merge this set of changes and get it on to staging to test. |
The plumbing seems to work well enough for an "alpha" version. We may need to make changes as we polish, but closing this issue for now. |
@JGreenlee @louisg1337 @the-bay-kay for visibility and feedback
The text was updated successfully, but these errors were encountered: