thin-edge / thin-edge.io

The open edge framework for lightweight IoT devices
https://thin-edge.io
Apache License 2.0
221 stars 54 forks source link

Support receiving custom fragments in operations #1718

Closed reubenmiller closed 9 months ago

reubenmiller commented 1 year ago

Is your feature request related to a problem? Please describe.

Operations with custom fragments are not sent to thin-edge and thus not accessible by thin-edge plugins or child device adapters.

This is very limiting when it comes to trying to design new solutions to solve problems where the relevant information cannot be encoded into the fixed operation fragments (e.g. in the case of the c8y_Firmware operation, .name, .version and .url)

Describe the solution you'd like

Subscribe to device operations using the devicecontrol/notifications topic rather than the s/ds topic.

When an operation is created in Cumulocity IoT, the device subscribed to the above MQTT topic, will receive the full json payload of the operation, including any additional custom fragments (which the user assigned during the creation of the operation). The full operation can then be passed on to any interested component.

By allowing custom fragments in operations, it allows for a greater flexibility for plugins and child devices to be able to solve some complex problems without requiring data schema changes within thin-edge or on Cumulocity IoT.

Advantages

Limitations

Use cases

Here are some of the use-cases where having custom fragments on operation would be useful:

  1. Adding scheduling information to the c8y_Firmware operation which is used to control when it should be applied to a child device (controlled by the child device)

    The scheduling information (e.g. when should the firmware payload be applied to the device) can be encoded into the c8y_Firmware operation under a custom fragment, and this information could be forwarded to the child device which is listening for child firmware operation messages. The child device can then control when the firmware will actually be applied to the child device. The control is left up to the child device (adapter) providing maximum flexibility.

    An alternative approach to scheduling would be from the cloud side, however whilst the creation of the operation can be controlled, the cloud does not have influence over when the operation will be received (due to network conditions, device is off etc.).

Describe alternatives you've considered

No other solution has been considered to date.

Additional context

Generally custom fragments are not added to an operations created by the in-built user interface, however Cumulocity IoT is a very open platform, so operations can be created by the user via the REST API which allows a very open data schema.

Information about the Cumulocity operation topic can be found on the website

An example of the

  1. Add the following rule to the c8y mosquitto bridge configuration

    file: /etc/tedge/mosquitto-conf/c8y-bridge.conf

    topic devicecontrol/notifications in 2 c8y/ ""
  2. Restart the mosquitto service

    systemctl restart mosquitto
  3. Start a local mqtt client (on the device) to listen to all mqtt info

    tedge mqtt sub "c8y/devicecontrol/notifications"
  4. Create a custom operation using the Cumulocity IoT REST api

    c8y operations create --device 22519994 --description "test operation" --template "{customInfo: {value:'do something'}}"

Example payloads (as received on the local MQTT broker)

Example 1: Operation topic/payload for the main device:

// Topic: c8y/devicecontrol/notifications
// Payload: (pretty printed)
{
    "delivery": {
        "log": [],
        "time": "2023-02-08T06:51:19.350Z",
        "status": "PENDING"
    },
    "agentId": "22519994",
    "creationTime": "2023-02-08T06:51:19.318Z",
    "deviceId": "22519994",
    "id": "522559",
    "status": "PENDING",
    "description": "test operation",
    "customInfo": {
        "value": "do something"
    },
    "externalSource": {
        "externalId": "raspberrypi_001",
        "type": "c8y_Serial"
    }
}

Example 2: Operation topic/payload for a child device:

// Topic: c8y/devicecontrol/notifications
// Payload: (pretty printed)
{
    "delivery": {
        "log": [],
        "time": "2023-02-09T07:25:19.406Z",
        "status": "PENDING"
    },
    "agentId": "22519994",
    "creationTime": "2023-02-09T07:25:19.348Z",
    "deviceId": "21524809",
    "id": "527022",
    "status": "PENDING",
    "description": "Restart device",
    "c8y_Restart": {},
    "externalSource": {
        "externalId": "raspberrypi_001_child01",
        "type": "c8y_Serial"
    }
}
didier-wenzek commented 1 year ago

Subscribe to device operations using the devicecontrol/notifications topic rather than the s/ds topic.

It might be necessary - at least temporarily, to bridge both topics as many examples are subscribing to c8y/s/ds.

Possible to support parallel processing of operations of the same type.

I guess this is now feasible because of the operation id provided over devicecontrol/notifications. However, if we have no way to notify operation progress beyond 501/502/503 messages, I don't see how this can work. If there is a REST API for operation progress, this can be used.

Operation topic/payload for the main device Operation topic/payload for a child device

I notice no specific difference between the two examples. Is the target specified by externalSource/externalId ?

reubenmiller commented 1 year ago

Subscribe to device operations using the devicecontrol/notifications topic rather than the s/ds topic.

It might be necessary - at least temporarily, to bridge both topics as many examples are subscribing to c8y/s/ds.

Possible to support parallel processing of operations of the same type.

I guess this is now feasible because of the operation id provided over devicecontrol/notifications. However, if we have no way to notify operation progress beyond 501/502/503 messages, I don't see how this can work. If there is a REST API for operation progress, this can be used.

Yes there is REST api to update the status of the operations which we can use instead of the smart rest messages. This would allow us perfect control over everything, though only with the downside that we have to keep requesting a token which adds a bit of load on the platform.

Operation topic/payload for the main device Operation topic/payload for a child device

I notice no specific difference between the two examples. Is the target specified by externalSource/externalId ?

Yes the externalSource/externalId is used to define which target the operation is related to.

rina23q commented 10 months ago

Done by #2482

The focus is only replacing the input topic of c8y operations from c8y/s/ds (SmartREST) to c8y/devicecontrol/notifications. The output messages, i.e. operation status update to executing/successful/failed remain as SmartREST.

gligorisaev commented 10 months ago

QA has thoroughly checked the feature and here are the results:

reubenmiller commented 10 months ago

Due to #2545 the related PR was reverted.

reubenmiller commented 10 months ago

This ticket is blocked by https://github.com/thin-edge/thin-edge.io/issues/2192

rina23q commented 10 months ago

Unfortunately the code change on https://github.com/thin-edge/thin-edge.io/pull/2482 had to be reverted. However, I keep the branch in my fork. If we need to use the code again, someone needs to create a new branch and cherry-pick the commits from my branch. Cherry-pick creates a new commit hash, therefore, new commits can be merged again.

rina23q commented 9 months ago

Since https://github.com/thin-edge/thin-edge.io/issues/2192 unblocked this ticket, I merged a new PR #2596.

gligorisaev commented 9 months ago

QA has thoroughly checked the feature and here are the results: