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

Consider adding Thing operations for pushing data from Thing to consumer #2057

Closed
hspaay opened this issue Nov 16, 2024 · 15 comments
Closed
Labels
needs-triage Automatically added to new issues. TF should triage them with proper labels

Comments

@hspaay
Copy link

hspaay commented Nov 16, 2024

Use-case 1: hiveot is digital twin Hub where things Thing push their property values, events, TD itself and action status updates to the digital twin. In turn the digital twin Hub pushes this to the consumer.
Use-case 2: unlike http, some protocols are uni-directional by their nature. Results of requests are passed asynchronously. websockets (@benfrancis) and mqtt are examples of this. For example, the result of the operation to read properties needs an operation to send the result back to the requestor.

In order to support developers of Things to push their information to a consumer, the following operations are needed:

  • updatemultipleproperties - the Thing instructs the consumer to update one or more property values.
  • publishmultipleevents - the Thing informs the consumer that event has been (re)published. Possible the result of read (multiple) events.
  • updatetd - the Thing instructs the consumer that a TD has been updated
  • updateactionstatus - the Thing instruct the consumer that an action status has been updated.

It would be good to standardize these operations in the TD so they don't have to be re-invented in the protocol bindings.

@github-actions github-actions bot added the needs-triage Automatically added to new issues. TF should triage them with proper labels label Nov 16, 2024
@lu-zero
Copy link
Contributor

lu-zero commented Nov 16, 2024

You are describing special cases of an observe pattern.

One of the topic we will discuss is how to describe relationships across affordances, that would potentially support your use-cases by bounding a property or an action to matching events.

@hspaay
Copy link
Author

hspaay commented Nov 16, 2024

You are describing special cases of an observe pattern.

Yes. My point is that the TD defines operations for consumers to request observing properties (and subscribing to events), but not the vocabularty for the Thing to push an observed property or subscribed event. This is left to the protocol binding itself.
There is no reason the TD can't also standardize at least the operation names to push data from the Thing to the observer. While the message envelope that wraps the data and carries metadata is protocol specific, the operation to do so is not.

One of the topic we will discuss is how to describe relationship...

That would certainly be good to hash out. Not the subject of this issue though.

@lu-zero
Copy link
Contributor

lu-zero commented Nov 16, 2024

Yes. My point is that the TD defines operations for consumers to request observing properties (and subscribing to events), but not the vocabularty for the Thing to push an observed property or subscribed event. This is left to the protocol binding itself.

If it is a detail of the protocol and should stay a detail of the protocol. An operation is always invoked by the consumer over a servient through a form anyway.

There is no reason the TD can't also standardize at least the operation names to push data from the Thing to the observer. While the message envelope that wraps the data and carries metadata is protocol specific, the operation to do so is not.

It would be redundant since the event affordance exists and covers exactly pushing data from the servient to the consumer.

What you want is a way to tell the consumer that which is the event to subscribe to to observe changes on the Thing state and how the event content maps. And that's exactly one of the topics for WoT 2 :)

@hspaay
Copy link
Author

hspaay commented Nov 16, 2024

Thank you for the effort to explain it. I'm pretty sure that i'm not getting it though.

It would be redundant since the event affordance exists and covers exactly pushing data from the servient to the consumer.

This looks like magic to me. Do you have an example that specifies how a consumer can recognize a message pushed by the servient as an event? Maybe this will enlighten me.

Here is an example of the issue from my perspective:

I'm an implementer of a protocol binding. Lets say I'm working on implementating event subscription. The TD-1.1 spec defines the 'subscribeallevents' operation and the protocol binding defines how this request is encoded into a message for the protocol. This makes sense to me.
But then an event occurs on a Thing. That has to be pushed to the consumer. The protocol binding wraps the event data in a message envelope for the protocol, websocket or mqtt.
How does the consumer know this message is an event?

Option 1: The protocol binding defines its own messages and contains a message-type for 'this is an event'. This seems to me what you're referring to. There is no standardization of vocabulary between protocol bindings. Each does their own thing, even though the operation is the same.

Option 2: The TD defines the operation for 'publishing an event'. The protocol binding can include this in its message envelope. This approach standardizes the vocabulary of publishing an event.

From a Thing point of view publishing an event is an operation. Why standardize operations for the consumer but not for the Thing? In my book its the same thing, just in the opposite direction.

Hopefully this clarifies the reason for creating this issue.

@lu-zero
Copy link
Contributor

lu-zero commented Nov 17, 2024

Thank you for the effort to explain it. I'm pretty sure that i'm not getting it though.

It would be redundant since the event affordance exists and covers exactly pushing data from the servient to the consumer.

This looks like magic to me. Do you have an example that specifies how a consumer can recognize a message pushed by the servient as an event? Maybe this will enlighten me.

Here is an example of the issue from my perspective:

I'm an implementer of a protocol binding. Lets say I'm working on implementating event subscription. The TD-1.1 spec defines the 'subscribeallevents' operation and the protocol binding defines how this request is encoded into a message for the protocol. This makes sense to me. But then an event occurs on a Thing. That has to be pushed to the consumer. The protocol binding wraps the event data in a message envelope for the protocol, websocket or mqtt. How does the consumer know this message is an event?

The all operations imply that the consumer reads all the affordances of that kind and the affordances do have a DataSchema (e.g. EventAffordance::data).

Note

The data schema for each of the property meta-interactions is constructed by combining the data schemas of each PropertyAffordance instance in a single ObjectSchema instance, where the properties Map of the ObjectSchema instance contains each data schema of the PropertyAffordances identified by the name of the corresponding PropertyAffordances instance.

This part should be probably expanded.

Option 1: The protocol binding defines its own messages and contains a message-type for 'this is an event'. This seems to me what you're referring to. There is no standardization of vocabulary between protocol bindings. Each does their own thing, even though the operation is the same.

Not really, you deliver the payload according to the derived DataSchema and that's it.

Option 2: The TD defines the operation for 'publishing an event'. The protocol binding can include this in its message envelope. This approach standardizes the vocabulary of publishing an event.

The operations tell the consumer how to ask something to a servient, not the other way round.

From a Thing point of view publishing an event is an operation. Why standardize operations for the consumer but not for the Thing? In my book its the same thing, just in the opposite direction.

From a servient point of view an operation is a protocol-specific verb that has to match a route expressed in a form.

The Thing description tells the consumer how to interact via Forms and Affordances, with the DataSchema expressing how the information is exchanged.

@hspaay
Copy link
Author

hspaay commented Nov 17, 2024

I clearly don't explain my issue very well. I've written a consumer that receives events and reads properties from a Servient. It has a UI that presents this information with help of the TD dataschema. This works quite well using the HTTP protocol binding with a modified SSE sub-protocol binding for server push (that is a separate story). The TD of the Thing defines Thing level operations that are used to read the properties. There is no issue there.

Now I'm working on adding the Websocket and MQTT protocol bindings. Lets assume that the servient supports all three protocols.

From a servient point of view an operation is a protocol-specific verb that has to match a route expressed in a form.

For some reason I have the idea that the operation verb (eg 'readproperty') is the same for all protocol bindings so that the application can use it to identify the Form needed to execute the operation. A servient supporting multiple protocols would use the same verb for each supported protocol.
Once a consumer it finds the form, it interprets the form to know how to invoke the operation. The developer of a consumed thing would only need to know one set of verbs, instead of a separate set for each protocol binding.
Is this wrong?

This is not the issue I'm raising but might be related. From the servient point of view an operation is needed to perform a push of data asynchronously. The Websocket draft specification uses 'propertyReading' for this example. My point is about standardization of these verbs across all protocol bindings.

I have the feeling we're going in circles so maybe it would be better to discuss this in one of the weekly WoT meetings. (@egekorkan) . After the plugfest of course.

Update 1: a thought just occurred that maybe the protocol specific method of pushing data from servient to consumer is supposed to be described in the 'observeproperty' (and readproperty, etc) operation. If so, I complete overlooked this critical bit of specification.

Update 2: the websocket specification does not include any information in the Form of the observeproperty operation on how to receive observed property values. Instead, it defines a separate operation named 'propertyreading' and mentions in the spec that it should be used to push data from the servient to the consumer. Now I'm wondering if this method of returning data should be part of the forms of the observeproperty, readproperty, etc, operations. In that case there is no need for a separate operation. (i'm probably inducing a headache somewhere now, mine included)

@lu-zero
Copy link
Contributor

lu-zero commented Nov 17, 2024

I clearly don't explain my issue very well. I've written a consumer that receives events and reads properties from a Servient. It has a UI that presents this information with help of the TD dataschema. This works quite well using the HTTP protocol binding with a modified SSE sub-protocol binding for server push (that is a separate story). The TD of the Thing defines Thing level operations that are used to read the properties. There is no issue there.

Now I'm working on adding the Websocket and MQTT protocol bindings. Lets assume that the servient supports all three protocols.

From a servient point of view an operation is a protocol-specific verb that has to match a route expressed in a form.

For some reason I have the idea that the operation verb (eg 'readproperty') is the same for all protocol bindings so that the application can use it to identify the Form needed to execute the operation. A servient supporting multiple protocols would use the same verb for each supported protocol. Once a consumer it finds the form, it interprets the form to know how to invoke the operation. The developer of a consumed thing would only need to know one set of verbs, instead of a separate set for each protocol binding. Is this wrong?

It is wrong. You have a default verb mapped to an operation, declared in the binding protocol, that can be overridden in the form. So you have to implement the protocol binding to know which is the default verb AND know which is the binding-specific vocabulary term that overrides it. (e.g. cov:method and the default mapping for CoAP or htv:methodName and the defaults for HTTP).

This is not the issue I'm raising but might be related. From the servient point of view an operation is needed to perform a push of data asynchronously. The Websocket draft specification uses 'propertyReading' for this example. My point is about standardization of these verbs across all protocol bindings.

The proposed solution for your issue has the problem that the servient does not consume a description of the consumer so it is impossible to go in that direction.

Regarding having a vocabulary term in TD to hold the information about the verbs, I'm one of the proponents, but it is still very protocol-specific.

Update 1: a thought just occurred that maybe the protocol specific method of pushing data from servient to consumer is supposed to be described in the 'observeproperty' (and readproperty, etc) operation. If so, I complete overlooked this critical bit of specification.

I'm afraid you hadn't read yet how the binding protocols work ^^;

Update 2: the websocket specification does not include any information in the Form of the observeproperty operation on how to receive observed property values. Instead, it defines a separate operation named 'propertyreading' and mentions in the spec that it should be used to push data from the servient to the consumer. Now I'm wondering if this method of returning data should be part of the forms of the observeproperty, readproperty, etc, operations. In that case there is no need for a separate operation. (i'm probably inducing a headache somewhere now, mine included)

A websocket can be used for jsonrpc, socket-io and such. You probably read a specific proposal for yet another protocol over websocket. I suggest you to start with the ratified protocol bindings.

@hspaay
Copy link
Author

hspaay commented Nov 17, 2024

It is wrong.

Well that is pretty clear. Thank you :)

I suggest you to start with the ratified protocol bindings.

I was referring to the WoT protocol binding for websockets. Not the websocket protocol itself. I'm only aware of the strawman proposal from Ben Francis.

So to conclude (hopefully I got this right now):

  1. There is no standardized vocabulary for operations shared amongst protocol bindings. Each binding can use different verbs.
  2. Servient and consumer must use the verbs for operations the protocol bindings specify, not the verbs defined in the TD-1.1 (which seem to be for the http binding)
  3. The Forms for operations do not specify the details on how a observed/subscribed value is pushed from servient to consumer returned. This is internal to the protocol binding and not visible in the TD Forms.

Thanks for clarifying this @lu-zero

@hspaay hspaay closed this as completed Nov 17, 2024
@egekorkan egekorkan reopened this Nov 17, 2024
@egekorkan
Copy link
Contributor

egekorkan commented Nov 17, 2024

I am not sure if the question is answered and since I saw some points to be clarified, I have reopened it.

There is no standardized vocabulary for operations shared amongst protocol bindings. Each binding can use different verbs.

That is the operations (see here). Each protocol binding must map a subset of these operations to their implementation in the protocol, e.g. mapping subscribeevent to SUBSCRIBE in MQTT. That tells the Consumer that if they are interested in that event's data, it should execute a subscribeevent operation, which is subscribing to a topic in the MQTT broker.

The Forms for operations do not specify the details on how a observed/subscribed value is pushed from servient to consumer returned. This is internal to the protocol binding and not visible in the TD Forms.

That is correct. There should be no need to describe this from my point of view.

This is not the issue I'm raising but might be related. From the servient point of view an operation is needed to perform a push of data asynchronously.

The operations in the TD are meant for the Consumer. An operation for the Thing is not in the scope of WoT atm. In your case, if there is a data being pushed to the Consumer, the Consumer must have subscribed to it somehow.

Now I'm working on adding the Websocket and MQTT protocol bindings. Lets assume that the servient supports all three protocols.

Let's say you have 3 TDs, each with one event and only one protocol for that event, which are HTTP, WS and MQTT. The form of that event in each TD has subscribeevent operation. In HTTP, there is also "subprotocol":"longpoll", in MQTT there is "mqv:controlPacket": "subscribe", in WS depending on the subprotocol there has to be one as well. The Consumer should be able to build the correct message packet based on everything that is contained in the form plus the binding knowledge.

If you think that your question is answered, feel free to close it again. The WoT CG meetings can be used for getting such questions answered but please note that they are not WG meetings, which require membership for continuous participation.

Note: There were similar confusions in the past. See #1329 and #1330

@hspaay
Copy link
Author

hspaay commented Nov 18, 2024

The form of that event in each TD has subscribeevent operation. In HTTP, there is also "subprotocol":"longpoll", in MQTT there is "mqv:controlPacket": "subscribe",

🤯 Oooohhh. It finally clicked. The TD operations are standardized, the form maps them to the operation names used in the subprotocol. They are two different things. Rather obvious on hindsight but somehow I developed a blind spot here. Duh! Thank you! 🙏

The operations in the TD are meant for the Consumer. An operation for the Thing is not in the scope of WoT atm. In your case, if there is a data being pushed to the Consumer, the Consumer must have subscribed to it somehow.

Yes, I see your point. The Digital Twin Hub is just a consumer of the Thing, even though Things connect to the Hub (actually this is not a hard restriction, a wot binding can find and connect to Things as well). So yeah no need for push type vocabulary ... probably ;). Now I need to wrap my implementation brain around doing this.

The only remaining question I have is how does a consumer (or the Hub) get an updated TD?
The digital twin directory needs to discover them somehow. In the hiveot case a Thing connects to the digital twin Hub so the Hub doesn't do the discovery.
Currently the Hub defines an action for updating a TD, but this requires the Thing to be aware of the action, which seems wrong. I've also used a 'td' event, published by the Thing, to signal a refresh of the TD which is slightly less evil. Neither solutions seem to be a true WoT compatible approach. Which begs the question, what approach would be WoT compatible?

Thanks again for the eye opener @egekorkan and @lu-zero for not giving up on me :)

@lu-zero
Copy link
Contributor

lu-zero commented Nov 18, 2024

The only remaining question I have is how does a consumer (or the Hub) get an updated TD?

The TDs should be close to immutable, you may look into wot-discovery since it should cover also that. Probably you'll quickly hit #2054

The digital twin directory needs to discover them somehow. In the hiveot case a Thing connects to the digital twin Hub so the Hub doesn't do the discovery.

The mDNS-SD discovery is my favourite way (also part of wot-discovery)

Currently the Hub defines an action for updating a TD, but this requires the Thing to be aware of the action, which seems wrong. I've also used a 'td' event, published by the Thing, to signal a refresh of the TD which is slightly less evil. Neither solutions seem to be a true WoT compatible approach. Which begs the question, what approach would be WoT compatible?

Conceptually updating a TD means that your Thing somehow changed, wot-discovery deals with that as well at least up to a point. You have to populate Thing::version and Thing::id so the consumer can figure out if, when and how the description changed.

@egekorkan
Copy link
Contributor

The only remaining question I have is how does a consumer (or the Hub) get an updated TD?

This is decoupled from the TD specification by design. As @lu-zero pointed out, you should look into the mechanisms in the Discovery spec: https://www.w3.org/TR/wot-discovery/

For example, the Discoverer can subscribe to TD updates on the TD Directory and each time the Thing or another entity updates the TD, the Discoverer will get an update.

@hspaay
Copy link
Author

hspaay commented Nov 18, 2024

I'll look into it. Thank you both for your feedback.
This issue can be closed now.

@hspaay hspaay closed this as completed Nov 18, 2024
@benfrancis
Copy link
Member

Just to add to/re-iterate what @lu-zero and @egekorkan have said, a Thing Description describes the operations that can be performed on a Thing, not on a Consumer.

I think you're going to have a tough time creating Thing Descriptions where the Thing is an HTTP client and the Consumer is an HTTP server, because to bind operations to a protocol there need to be URL endpoints to put in forms. This client/server flip is something I'd like to explore for the Web Thing Protocol WebSocket sub-protocol, but it's going to be tricky to describe in a Thing Description.

Regarding the point about updating Thing Descriptions, see w3c/wot-discovery#464

@hspaay
Copy link
Author

hspaay commented Nov 19, 2024

I think you're going to have a tough time creating Thing Descriptions where the Thing is an HTTP client and the Consumer is an HTTP server

Well, it being tough never stopped me before. Most things worth doing are tough, blah blah 😉 Not sure what movie that was from but I couldn't resist. Eh eh.

Seriously though, this is the approach hiveot is taking. The intent is to offer an alternative to the model that all Things must run a server to be used. The WoT/TD design seems to be pushing this model and I'd like to offer a counter point as I think it is a security nightmare in the making. At the same time I'll work with it for practical reasons.

The discussion on this is a wee bit out of scope but this is where my questions come from. In my book Things shouldn't have to run a (network) server.

hiveot will include a protocol binding for WoT devices just as it does for zwave, 1-wire, etc. Consumers can use hiveot as a (digital twin) Thing hub. So the intent is to fit seamlessly into the WoT ecosystem.

At the same time if you're paranoid about security you can use Things that connect directly to the (discovered) Hub and use a push model (so they dont run network servers). These Things don't exist yet but then again this is a chicken and egg problem. It needs a specification and a Hub to support it. Hopefully this can be considered in WoT 2.0 but in the meantime I will try to work with what is available at the moment.

I hope this clarifies some of the questions I'm raising. And again, I very much appreciate your willingness to help out and point me in the right direction.
The discovery links you all gave are also helpful here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-triage Automatically added to new issues. TF should triage them with proper labels
Projects
None yet
Development

No branches or pull requests

4 participants