Efficiently sending JSON to target over AirVantage

Hey all,

I’m working on syncing config data to our targets. Let’s suppose I have some JSON object with a total of n primitive properties (i.e all keys return data of type number or string, no nested objects). For example, take this TypeScript interface.

export interface Sensor {
  [...]
  key: string
  type: number;
  enabled: boolean;
  [...]
}

I see 2 ways to achieve this:

  1. Split it into key val pairs (e.g (sensor.pressure.type, 1)), and use n “send” operations on the backend, and n “subscriptions” with corresponding handlers on the hardware. Now suppose you have an array of m of these objects in total that you want to synchronize with the hardware. This means you have m*n operations to send, and m*n subscriptions to manage. From my perspective, this seems like a lot of overhead to handle a single JSON object.

  2. Convert the JSON object to a string (e.g JSON.stringify) and use le_avdata_GetString. Unfortunately it seems the max string size is 255 bytes, so this could become limiting pretty fast (for reference the mangOH MQTT client supports 1000 byte payloads and could theoretically handle much more).

Any input on this is greatly appreciated! If any of my assumptions are incorrect please let me know.

Cheers!

Hi @nvd, I guess you want to read or write to a root node using airVantage. You can achieve this using the following rest api (sample).

{
“systems” : {
“uids” : [“XXXX”]
},
“settings”: [
{“key”: “assetData.deviceConfig.1”, “value”: 1},
{“key”: “assetData.deviceConfig.2”, “value”: 2},
{“key”: “assetData.deviceConfig.3”, “value”: 3},
{“key”: “assetData.deviceConfig.4”, “value”: 4},
{“key”: “assetData.deviceConfig.5”, “value”: 5},
{“key”: “assetData.deviceConfig.6”, “value”: 6},
{“key”: “assetData.deviceConfig.7”, “value”: 7},
{“key”: “assetData.deviceConfig.8”, “value”: 8},
{“key”: “assetData.deviceConfig.9”, “value”: 9},
{“key”: “assetData.deviceConfig.10”, “value”: 10}
{“key”: “assetData.deviceConfig.xxxx”, “value”: xxxx}
],
“reboot”: false
}

This will result in a write at node deviceConfig in your app named assetData. You can also do a read operation in similar fashion. The device will send a CBOR payload for queries on root node.

Hey @prushp,

Thanks for the advice. This is a nice way to handle it from the server perspective, but on the target device, how do I effectively “subscribe” to that payload?

Cheers

From the above example: on the target side you have an app assetData which creates those resources deviceConfigX (string / bool / integer / float). On receiving the data targeting deviceconfig, avc will decode it and update all the resources. You can have handlers registered from your app to get notified if a resource value gets updated. You can use le_avdata_AddResourceEventHandler() to register your callback handler.

Thanks for the suggestion @prushp. How would I go about adding a change handler for a bunch of related properties?

For example, suppose I have the Sensor object described in my first post. I see 2 ways of doing this:

a) Have a subscription for each key in the object, and then somehow manage to sync them up
b) I assume that if one asset path changes value, they’ve all changed value

Does this seem like a fair solution or am I missing something here?

Edit: Does le_avdata_AddResourceEventHandler support wildcard paths (e.g assetData.config.*)? I could see this helping.

We don’t support wild card paths. But you can add a handler registered to a root node such as assetData.config. You will be notified if anything changes underneath config. This change was made recently - Legato 18.04.

Hey @prushp,

That’s very good to know and definitely makes my life a lot easier. I’m going to try this again and report back.

Thanks!

Hey @prushp, one more question about this: do I need to be creating a resource at each path? If so, can I do this from the REST API instead of from the target device? I’ve built up the layers in our backend to transform our interfaces into an AirVantage friendly interface (e.g [{key, value}]), but the request seems to be failing somewhere. I also added another node to the resource path in the hopes that it will still work with le_avdata_AddResourceEventHandler() (e.g le_avdata_AddResourceEventHandler("/syncService/sensorConfig/digital1").

Thanks for the help!

Hey all,

I spent sometime programatically generating resource paths on the target. So far it’s been working great for boolean/integer resources, but I keep getting this error for strings:

Edit:

Still having issues after removing the _ separator (as suggested by @prushp) :

Looks like the string issue was related to my payload. Sorry for the confusion.

Hello all,

I’ve recently started using le_avdata_SetNamespace such that I can share asset data across apps (e.g /digitalService/sensorConfig/digital1_enabled instead of /sensorConfig/digital1_enabled). Unfortunately this seems to be causing some issues with my event handler.

Previously, a call to le_avdata_AddResourceEventHandler("/sensorConfig", handler, NULL) would handle changes to /sensorConfig/* properties, but with the global namespace change, handler is never called.

I tried changing to le_avdata_AddResourceEventHandler("/digitalService/sensorConfig", handler, NULL) and le_avdata_AddResourceEventHandler("/digitalService", handler, NULL) but this does not seem to work either.

Alternatively, I could change what’s being sent from our backend (e.g send /sensorConfig/digital1_enabled instead of /digitalService/sensorConfig/digital1_enabled, but this does introduce a breaking change to our solution.

Quick update: I changed what was being sent from the server as described above and it seems to work. Does this mean it’s a requirement to have a path with only 2 separators (/) in order to watch changes on a node?