Skip to content
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

feat: Add ADR for device functions #644

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

iain-anderson
Copy link
Member

Signed-off-by: Iain Anderson [email protected]

If your build fails due to your commit message not passing the build checks, please review the guidelines here: https://github.com/edgexfoundry/edgex-docs/blob/main/.github/Contributing.md

PR Checklist

Please check if your PR fulfills the following requirements:

  • Changes have been rendered and validated locally using mkdocs-material (see edgex-docs README)

{
"name": "Parameter name",
"description": "description of what the parameter controls",
"type": "Any of the usual EdgeX data types"
Copy link

Choose a reason for hiding this comment

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

Here are two fields on device function parameters to consider:

  • defaultValue (optional): Contains a default value if the parameter isn't supplied.
  • range (optional): min/max values for numeric types.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added these (but used maximum / minimum for consistency with device resources)

Copy link

Choose a reason for hiding this comment

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

Min/max makes sense. Thanks.

cloudxxx8
cloudxxx8 previously approved these changes Nov 30, 2021
Copy link
Member

@cloudxxx8 cloudxxx8 left a comment

Choose a reason for hiding this comment

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

LGTM

{
"name": "Name by which the function is accessed",
"description": "Readable description of the function",
"attributes": { device-specific parameters which select this function },
Copy link

Choose a reason for hiding this comment

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

I'm not clear what attributes are...are they device resources?

Copy link
Member Author

Choose a reason for hiding this comment

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

They're like the attributes in device resources - they tell the device service implementation how to call the required function
This could be something like a pathname, or a register specification and may include a mapping of the named parameters to some device specific identifier

Copy link

Choose a reason for hiding this comment

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

I see.

"type": "Any of the usual EdgeX data types"
}
],
"out":
Copy link

@ghost ghost Dec 3, 2021

Choose a reason for hiding this comment

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

Does "out" mean return value? I see (from below)...it does mean return value.

Copy link
Member Author

Choose a reason for hiding this comment

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

Added comment to clarify

Copy link
Member

Choose a reason for hiding this comment

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

I think "input" and "output" are clearer.

Copy link

@dmocek dmocek Dec 8, 2021

Choose a reason for hiding this comment

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

Or maybe instead of a single "parameters" attribute with "in/input" and "out/output", it should be a "parameters" attribute containing an array of function parameters and a separate "returns" attribute with an array of return values...like a Command Response, but for functions.

"parameters":
        [
          {
            "name": "Parameter name",
            "description": "(optional) description of what the parameter controls",
            "type": "Any of the usual EdgeX data types",
            "defaultValue": "(optional) value to use if param is not supplied",
            "maximum": "(optional) for numerics, maximum allowed value",
            "minimum": "(optional) for numerics, minimum allowed value"
          }
        ]
}
"returns":
       [
          {
            "name": "Name of returned value",
            "description": "(optional) description of what the value indicates",
            "type": "Any of the usual EdgeX data types"
          }
        ]
    }

Copy link
Member Author

Choose a reason for hiding this comment

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

Applied the above but used returnValues

@iain-anderson
Copy link
Member Author

Note from Device Service WG:
It may be better to support asynchronous operation. The initial request would if successful return a request ID, and a second endpoint would be used to query the status of a request using this ID

Copy link
Member

@farshidtz farshidtz left a comment

Choose a reason for hiding this comment

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

The underlying issue seem to be a limitation on how profiles are defined, rather than what the REST API allows. I suggest separating the two topics, even if they are currently technically coupled. A REST API can be modeled to receive a request and invoke the appropriate operation, whether it is reading/setting of resources or invoking commands/functions. What is important is that REST semantics (method, path, status code, headers, etc) map to the underlying operation.

Overloading the REST API to expose a dedicate POST endpoint for direct function calls, without any control on what the function does, is very similar to SOAP over HTTP: https://en.wikipedia.org/wiki/SOAP#Example_message_(encapsulated_in_HTTP)

I believe if we have a list of requirements on what functions need to be called, we can try to model them as REST->resources/commands.

"type": "Any of the usual EdgeX data types"
}
],
"out":
Copy link
Member

Choose a reason for hiding this comment

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

I think "input" and "output" are clearer.


```
{
"deviceFunctions":
Copy link
Member

Choose a reason for hiding this comment

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

Won't this be confusing since there is also a "deviceCommands" field?

Examples could include perform a self-test, go into standby mode for an hour, invalidate an access key.

These all sound like commands to me.

Copy link

Choose a reason for hiding this comment

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

I agree that it's a bit confusing. As it stands now, DeviceCommands define access to reads and writes for multiple simultaneous device resources whereas DeviceFunctions don't have anything to do with device resources. Maybe DeviceCommands should be DeviceResourceCommands.

Copy link
Member Author

Choose a reason for hiding this comment

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

Personally think DeviceCommands should be ResourceGroups but we are where we are


`api/v2/device-funtion/<device-name>/<function-name>`

This shold accept POST requests with parameters sent in a JSON (or CBOR) payload
Copy link
Member

Choose a reason for hiding this comment

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

The HTTP methods in REST have dedicated semantics (unlike SOAP). The function may do operations that are beyond the defined semantics of POST (e.g. removal of resources).

There are many references for REST design, here is one that is specific to IoT: https://tools.ietf.org/id/draft-keranen-t2trg-rest-iot-05.html#rfc.section.3.5.3

@iain-anderson
Copy link
Member Author

Re-expressed access to the functionality in MessageBus terms rather than REST

@iain-anderson iain-anderson marked this pull request as ready for review December 15, 2021 15:25
Comment on lines 61 to 65
Note: the `attributes` structure is analagous to `attributes` in a `deviceResource`. Each device service should document and implement a scheme of required attributes that will allow for selection of the relevant funtion. The function's `name` is intended for UI and logging purposes and should not be used for actual function selection on the device.

**Define MessageBus topics on which function call requests and replies are to be made**

`edgex/function-calls/device/[profile-name]/[device-name]/[function-name]`
Copy link
Collaborator

@bnevis-i bnevis-i Dec 16, 2021

Choose a reason for hiding this comment

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

On line 61 it is stated that name is only used for UI and logging, but then on line 65, name is used as part of the MQTT topic name.

Copy link
Member Author

Choose a reason for hiding this comment

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

removed comment on function names


`edgex/function-responses/device/[profile-name]/[device-name]/[function-name]`

The device service will provide responses to function calls on this topic. The payload will be
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are messages sent with guaranteed delivery, or can messages be thrown away if the device is not there to receive them?

Copy link
Member Author

Choose a reason for hiding this comment

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

QoS is configurable in the main messagebus options

}
```

or if a call fails
Copy link
Collaborator

Choose a reason for hiding this comment

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

What if a response is never sent or received?

|--------|--------
| 0 | The operation was successful
| 1 | Parameters were missing, out of range or non-parsable
| 2 | The Device is DOWN or DISABLED
Copy link
Collaborator

Choose a reason for hiding this comment

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

What component is determining that a device is down or disabled and sending the response?

| 0 | The operation was successful
| 1 | Parameters were missing, out of range or non-parsable
| 2 | The Device is DOWN or DISABLED
| 3 | No such device or function
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ditto. Who is implementing the dead letter functionality?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added text on the SDKs responsibilities

Comment on lines 69 to 73
{
requestId: "184b894f-a7b7-4d6c-b400-99961d462419",
parameters: { (a map of parameter values keyed by parameter name) }
}
```
Copy link
Collaborator

@bnevis-i bnevis-i Dec 16, 2021

Choose a reason for hiding this comment

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

Perhaps we can use an existing specification and add the requestId to it?

https://en.wikipedia.org/wiki/JSON-RPC#Version_2.0
https://www.jsonrpc.org/specification

Copy link
Member

Choose a reason for hiding this comment

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

+1000000

We should at least explore prior art before we invent something new...

Copy link
Member Author

Choose a reason for hiding this comment

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

Have changed this so that it is using the same style of message body as proposed for N/S messaging

@bnevis-i
Copy link
Collaborator

Note from Device Service WG:
It may be better to support asynchronous operation. The initial request would if successful return a request ID, and a second endpoint would be used to query the status of a request using this ID

Is the protocol not already defined to operate asynchronously? Fire-and-forget with a requestId in one direction, and a fire-and-forget response in the other with the same requestId?

@iain-anderson
Copy link
Member Author

Note from Device Service WG:
It may be better to support asynchronous operation. The initial request would if successful return a request ID, and a second endpoint would be used to query the status of a request using this ID

Is the protocol not already defined to operate asynchronously? Fire-and-forget with a requestId in one direction, and a fire-and-forget response in the other with the same requestId?

It is now :) Previously the ADR specified a REST endpoint for invoking functions

Copy link
Contributor

@TomBrennan-Eaton TomBrennan-Eaton left a comment

Choose a reason for hiding this comment

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

It seems to me that this could be aligned with the ADR for North-South command messaging. Wouldn't the command service be normally in the loop when these functions are actuated?
The requestId here is the correlation-id there.

Formatting fixes and language clarification
Add parameter range limits and defaults

Signed-off-by: Iain Anderson <[email protected]>
Specify asynchronous operation via MessageBus rather than synchronous REST

Signed-off-by: Iain Anderson <[email protected]>
Clarify parameter specification, add error table, move ADR to no. 0021

Signed-off-by: Iain Anderson <[email protected]>
Remove contradictory text on function names, add SDK responsibilities

Signed-off-by: Iain Anderson <[email protected]>
Copy link
Member

@tonyespy tonyespy left a comment

Choose a reason for hiding this comment

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

The approach in this ADR is certainly one way to solve the problem of making RPC-like calls to device/sensors managed by EdgeX device services. Although it's not called out explicitly, it seems this version of the ADR limits this functionality to message bus usage only (i.e. you can't invoke device functions via REST), so I'd suggest making that more obvious, and given the overlap between this ADR and the North/South Messaging ADR, I would add a References section with a link it. Also, as I explained in my review of that ADR, I feel like we don't have our story straight with respect to how we would provide similar functionality to the API Gateway with respect to access control of functionality provided via message bus.

Although I didn't comment on the initial version, it proposed a similar approach over REST. Should this ADR mention that this approach was examined and rejected for some reason?

I also recall suggesting that we re-consider gRPC, especially given the fact that it's become an (optional) requirement for services that will leverage the new Spiffe/Spire-based token provider (per the Delayed Services ADR), and given Kong's support for proxying gRPC. Did you give any serious consideration to this approach?

{
"name": "Parameter name",
"description": "(optional) description of what the parameter controls",
"type": "Any of the usual EdgeX data types",
Copy link
Member

Choose a reason for hiding this comment

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

Does this include array, binary, and object types?

Copy link
Member Author

Choose a reason for hiding this comment

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

Don't see why not

}
```

Note: the `attributes` structure is analagous to `attributes` in a `deviceResource`. Each device service should document and implement a scheme of required attributes that will allow for selection of the relevant funtion.
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure what the purpose of attributes are in this context? If extra data is required to make a function call, isn't this the purpose of parameters? I also don't understand what you mean by "a scheme of required attributes that will allow for selection of relevant function"?

sp: funtion

I


## Decision

**Add a new section to device profiles describing functions**
Copy link
Member

Choose a reason for hiding this comment

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

It seems odd to describe functions in device profiles. Typically what we're trying to accomplish is allow use of some sort of functional protocol to communicate with devices, and the protocol itself doesn't change for different instances of the class of devices supported by the device service. For instance, the original device-camera service only supports ONVIF (which is a SOAP-based protocol), likewise our RFID device service only supports LLRP. I suppose if a service supported more than one function-based protocol, then defining the functions in profiles makes sense, but I think this is the exception, not the norm.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes for protocols like ONVIF which define available operations at the protocol level. But for eg a Modbus device where you may have a function "Reset latching alarms" which is invoked by writing a zero into register 0x16, that ought to be defined in a device profile.


**Define MessageBus topics on which function call requests and replies are to be made**

`edgex/function-calls/device/[profile-name]/[device-name]/[function-name]`
Copy link
Member

Choose a reason for hiding this comment

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

Why include the profile name? It's not included as a parameter in the standard core/device services command API.

Also would it make sense to model our topic names using the conventions we use for REST which prefix parameters with a name (e.g. edgex/function-calls/device/[device-name]/function/[function-name])?

Should the topic also somehow indicate that this message is destined for core command and/or a specific device instance?

Are these functions meant to proxied by core command (per the North/South Messaging ADR)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Have changed these to reflect the content of the N/S messaging ADR

Comment on lines 69 to 73
{
requestId: "184b894f-a7b7-4d6c-b400-99961d462419",
parameters: { (a map of parameter values keyed by parameter name) }
}
```
Copy link
Member

Choose a reason for hiding this comment

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

+1000000

We should at least explore prior art before we invent something new...

| Status | Meaning
|--------|--------
| 0 | The operation was successful
| 1 | Parameters were missing, out of range or non-parsable
Copy link
Member

Choose a reason for hiding this comment

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

Does this include other errors with the request format?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added a separate code for other format errors

@iain-anderson
Copy link
Member Author

It seems to me that this could be aligned with the ADR for North-South command messaging. Wouldn't the command service be normally in the loop when these functions are actuated? The requestId here is the correlation-id there.

I've changed the topic naming and message format to be more aligned with the N-S messaging ADR. Request and correlation IDs are still separate; the lifetime of a correlation id may span multiple requests

Align with north-south messaging ADR (edgexfoundry#23)

Signed-off-by: Iain Anderson <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ADR Architecture Decision Record documentation levski fall 2022 release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants