Open RoboPhred opened 4 years ago
Just an idea: You can have a property called "pastEvents"
as an array type and the items can like this:
// pastEvents property
{
"type": "array",
"items": {
"type": "string", // or more like the date time information
"@type":"EventAffordance"
}
}
I am not very sure since this would mean that you would expect the "forms"
of events in the items as well. Maybe you should use "td:hasNotificationSchema"
. Any opinions @vcharpenay ?
Providing the events through a property is workable, and this technique could also cover the missing ActionRequest concept from the Mozilla WOT spec.
It would be nice to have a stronger contract for this though, possibly by a "gethistoricalevents" op form. This seems to be similar in scope to "readmultipleproperties" as an optional extension onto the affoardance.
We have discussed this as well but for us this is specific for an implementation and should not be integrated into the standard. Given further interest from other implementations and members, it can be in the future.
You can have a property called "pastEvents"
I always try to restrict the use of properties for actual physical properties of a device which cause a change of state in the real world, and avoid using them to represent things like internal firmware settings or data logs. Using properties for event logs or action queues feels like a hack to shoehorn an API/protocol that can't currently be expressed via the Thing Description into the TD Information Model.
The HTTP Protocol Binding currently describes how to get and set properties and invoke actions, but not how to receive events or cancel or query the state of an action request.
I think this behaviour could be defined for HTTP and WebSockets through an extended HTTP Protocol Binding or default profile, and a WebSocket sub-protocol.
See also, section 2.5 of the WG Charter.
Complex Interactions
Many use cases require the implementation of complex interactions that span multiple protocol transactions. An example would be the initiation, monitoring, and possible cancellation of ongoing actions. Another would be managing and accessing historical queues for events and actions.
In fact such complex interactions can be implemented with the more basic features already included in the WoT Thing Description and in particular can be supported in a self-describing way with hypermedia controls. Hypermedia controls can also be used to clarify what remediary actions can be taken when an error occurs.
However, there are many possible ways to implement and use such complex interactions. To better support the goals of interoperability, it would be useful to precisely define the most common complex interactions.
This work item will document the behavior of complex interactions, including:
- The ability to initiate, monitor, and cancel ongoing actions.
- Support for action and event queues.
- Error recovery via hypermedia responses.
The action queue are indeed something we are working on. I was not aware of the event queue statement of the working charter, so this is my bad. Just a question though, how does the Consumer use the information provided in the past events?
Let's not mix the data model of a specific Thing with the general WoT interaction model. It's not clear whether the use case is a) to fetch past Event interactions (and require a WoT mechanism for that) b) or to fetch data since the last read in a time series.
An Event interaction is meant to model a subscription based transient notification about a state change. Data, unless very small and sparse, should be transmitted by other means.
I would not use Event interactions to represent/convey time series. Depending on network conditions are sampling frequency + data size, it would be a race. Modeling these with Action interactions seem more suitable, as it could be adapted better to the use case.
We could do proofs of concept with these and see what would be worth standardizing as WoT mechanism for handling streams.
I agree we should check if Actions need refinement. (We also discussed aspects of synchronicity vs atomicity in action sequences/transactions.)
how does the Consumer use the information provided in the past events?
Events are obviously tricky to map onto HTTP in general because it has no built-in push mechanism (e.g. like CoAP does). Therefore, in its REST API the Mozilla WebThings implementation provides events using a GET on the URL of an event resource which can be polled at intervals if necessary.
This GET request could just return the latest event, but then the client may miss some events. The response to the GET request therefore contains an array of recent events with timestamps, so the client can figure out what events have been emitted since the last GET. This is obviously an imperfect solution because it still requires the resource to be polled often enough to receive every event, or for the server to keep track of which clients have received which events. This is why there is also a separate push mechanism in the WebSocket API to receive events as they are emitted, but the event resource in the REST API provides a fallback for clients which don't support WebSockets.
There are of course other potential solutions to receiving events over HTTP like long-polling and Server Sent Events, but none of them are perfect and currently the HTTP Protocol Binding in the Thing Description specification doesn't specify any one solution as a default.
It's not clear whether the use case is a) to fetch past Event interactions (and require a WoT mechanism for that) b) or to fetch data since the last read in a time series.
Events can contain data. The format of these data is described by the data
member of the EventAffordance.
I would not use Event interactions to represent/convey time series. Depending on network conditions are sampling frequency + data size, it would be a race. Modeling these with Action interactions seem more suitable, as it could be adapted better to the use case.
Rather than discuss this abstractly, here are some concrete examples of events in Mozilla's WoT capability schemas which have been mapped onto real devices:
None of these events could be modelled as an action as far as I can tell, because they are events emitted by a device, not actions invoked upon the device.
The more common way to model a time series of data would be through properties (e.g. streaming temperature values from a temperature property over a WebSocket). But events can contain data in some cases too.
In my case, the use case is to fetch past interactions for display. For example, imagine a TD-speaking website using the HTTP binding wants to show the times when a motion detector has detected motion.
For an example of this out in the home automation world, OpenHab can provide a timeline of events. Home Assistant also provides timelines, graphs, and event counters (as seen in the first image of the readme).
This information is useful largely for its historical readings, as it would not be particularly useful if the user could only be told of motion detection that has happened while the website is open.
Admittedly, a property that contains an array of previous values could work here, but knowledge of historical event values is a useful construct. I feel it would be preferable to implement this as a concern of the event, but alternatively an extension that lets me describe a property as offering up historical data for a given event would also be acceptable. In the latter case, my library would auto-generate such properties for each event.
My justification for having this be part of EventAffordance is that, to represent events as a semantic concept, it should provide not only the ability to observe events but also to know of past occurrences of the event.
Pushing this off on a property does seem a misuse of the concepts, as things like a list of previous motion detection cannot be said to be a part of the state of a motion detector. And while temperature and heating/cooling status can be said to be part of an air handler's state, having a property be an array of timestamps and previous states again seems closer to an event concern than one of state.
Here is an event log from the WebThings UI:
This information is useful largely for its historical readings, as it would not be particularly useful if the user could only be told of motion detection that has happened while the website is open.
So we'd need a new interaction construct that would be able to describe a series of past Event interactions (presumably configured during subscribe).
That would make event subscription the "meta-action" to configure past events delivery, which could happen in two (perhaps configurable) ways,
For this we'd need to modify the contract of subscribeevent
and specify that Events might be delivered in arrays.
From the discussion, it seems that the use case is indeed present. In the new TD version we are planning to not introduce any breaking changes, so changing what subscribeevent
means would not be ideal. Some ideas:
op
value for the events like @RoboPhred mentioned in the comment above. The problem here would be how to choose a limit on the history but it can be done via uriVariables. The default mapping for this would be an HTTP GET request in case of HTTP. Also, there would be an assertion that this data will be always an array. While we do this already with readallproperties
, where it is specified to be always an array, I only partly like this solution since it is quite prescriptive, i.e. it constrains how the payload can look like. This also doesn't solve the getting the history of all events that Mozilla Webthings support, for which we would need a meta interaction like getallhistoricalevents
. Furthermore, how would this be indicated outside of the forms? For all the other op
value, we have something in the interaction level that gives the hint that one form will contain the operation, i.e. "readOnly":"false"
gives the hint that there will be "op":"writeproperty"
in one of the forms. This is not a must though.readallproperties
. The problem would be linking this to an event in the interactions and I guess it would be more messy.In the new TD version we are planning to not introduce any breaking changes, so changing what subscribeevent means would not be ideal.
Adding options to subscribeevent
would not be a break... without options it would work the same way as now. But indeed a separate op could be better.
Treating this as a new op seems to be the best option in my opinion.
I can add to the conversation around using subscribeevent though. From the viewpoint of a subscription, this reminds me of hot and cold observables, used in rx / rxjs. In short:
In this case, coldStart
/ supportsColdStart
might make a good option or hint property name to opt-in to receiving previous events when making a new subscription. This would still allow the subscription to continue to receive future events as well.
I still believe a new op might better serve this though. While I do intend to both receive past events and subscribe to new ones at the same time, it might be better to keep the two ideas separate. I am not fully convinced either way through.
The problem of the data format for a readhistoricalevents op is one I don't have answers for. For one, we would need to somehow pass along timestamp data, which might not come over when the event is in its subscription form. This means either wrapping each event in a timestamped envelope similar to the current Mozilla WOT implementation, or allowing the TD to specify a different schema for historical events. A third option could just be to leave events in the same data format, but trust that the event will provide the timestamp on its own, in both subscribe and historical mode.
A problem with both approaches is that devices presumably exist indefinitely, so the historical action list is unbounded. There should be a way to both limit the number of items the client asks for, and to explain to the client the limits on the device itself. Both of these could be done through either a hard count on the number of historical events, or by the age of the event. It may even be advisable to provide both limits on the same event (IE: TD declares an event supporting the previous 100 events over the past 2 weeks).
There should be a way to both limit the number of items the client asks for, and to explain to the client the limits on the device itself.
There could be subscription options to tell the client prefs, and server limitations could just manifest in how the events/data is delivered.
I still believe a new op might better serve this though.
A separate op would work if someone first subscribes to an Event and then would fetch event history - otherwise it's a race. I guess the separate op would deliver the events in bulk.
I am thinking we'd need that bulk delivery mechanism even for normal Events if we wanted to support reporting high frequency data, in order to avoid network+processing roundtrips, then expose as an iterable, rather than firing a local event every time - it all depends on reporting rate, sampling rate, sample size, network conditions and the capabilities of the client and server. In theory, with subscribe options (including hot/cold subscriptions) and possibility of bulk delivery we could represent all these use cases with Events: simple events with small/sparse data, event history and high frequency data. It can be even made backward compatible.
But it would be a simpler solution to separate interactions with bulk delivery vs single delivery, so we may want to stick with the current assumption that Event interactions should be used for state change notifications, eventually carrying small and sparse data (like in the use cases @benfrancis quoted). History of such Events would then be supported by a separate op, with options.
For example, DDS has QoS of History to configure how many (or all) values should be retained. Please see page 105 in DDS specification. Some industrial applications find this useful to make analysis of past values when it started observing a property.
Beside of the technical discussion we should have "historical events" as a use case and shared it with the Arch TF. I will provide an issue for it.
There is a "Event Sourcing" pattern for historical events. (from Microsoft Azure documentation), which might provide an useful input to the use case discussion.
When to use this pattern
This pattern might not be useful in the following situations:
Support for event queues is in the current charter and I'm conscious we haven't come up with a solution for this yet. This is also needed in order to make WebThings W3C compliant (see https://github.com/WebThingsIO/gateway/issues/2806 and https://github.com/WebThingsIO/gateway/issues/2807). This feature would be particularly useful for an HTTP WoT consumer which doesn't support a separate event mechanism like Server Sent Events or WebSockets, but is also useful for all consumers which want to get historical event data (e.g. which occurred before a WebSocket was opened).
Proposed Solution
readpastevents
What do people think about a readpastevents
operation which fetches a log of recent event data for a given event? e.g.
"events": {
"overheated": {
"title": "Overheated",
"type": "number",
"unit": "degree celsius",
"description": "The lamp has exceeded its safe operating temperature",
"forms": [{
"op": "readpastevents",
"href": "https://foo.webthings.io/things/lamp/events/overheated"
}]
}
}
readallpastevents
There could also be a readallpastevents
operation in the top level forms
member of a thing description which gets a log of event data for all a thing's events. e.g.
"forms": [
{
"op": "readallpastevents",
"href": "https://foo.webthings.io/things/lamp/events"
}
[
Challenge 1: Operation naming
I'm open to suggestions on naming since readallpastevents
is a bit ambiguous as to whether it's all event types or all event instances.
Challenge 2: Payload representation
Another challenge is how to describe the payload of such resources, since its desirable to include timestamps in the data.
Currently WebThings returns payloads like this for both event and events resources:
[
{
"overheated": {
"data": 102,
"timestamp": "2017-01-25T15:01:35+00:00"
}
},
{
"overheated": {
"data": 101,
"timestamp": "2017-01-24T13:02:45+00:00"
}
}
]
But one of the things we're trying to do in order to be W3C compliant is to remove these types of object wrappers which are described in the Web Thing API specification rather than declaratively inside the TD.
For the payloads returned for individual event affordances we could remove the event name from the payload as that is implicit, e.g.
[
{
"data": 102,
"timestamp": "2017-01-25T15:01:35+00:00"
},
{
"data": 101,
"timestamp": "2017-01-24T13:02:45+00:00"
}
]
We could then try to describe this payload declaratively in the TD, e.g.
{
"events": {
"overheated": {
"title": "Overheated",
"type": "array",
"items": {
"type": "object",
"properties": {
"data": {
"type": "integer"
},
"timestamp": {
"type": "string",
"format": "date-time"
}
}
},
"unit": "degree celsius",
"description": "The lamp has exceeded its safe operating temperature",
"forms": [{
"op": "readpastevents",
"href": "https://foo.webthings.io/things/lamp/events/overheated"
}]
}
}
}
A downside of describing the payload declaratively in the Thing Description in this way is that it makes the method of providing timestamps implementation specific which will be harder for consumers to make sense of. Additional semantic annotations might be needed to better explain the semantic meaning of the payloads.
For the readallpastevents
operation we would need the event name for each event instance. That could use special text in the specification like readallproperties
which wraps a separate event log for each event type in its own object keyed by object name. For our use case in WebThings of showing the user a log of all events, this would require more work because we'd have to merge and sort the lists, but it could do the job.
{
"overheated": [
{
"data": 102,
"timestamp": "2017-01-25T15:01:35+00:00"
},
{
"data": 101,
"timestamp": "2017-01-24T13:02:45+00:00"
}
],
"alarm": [
{
"timestamp": "2017-01-25T15:01:35+00:00"
},
{
"timestamp": "2017-01-24T13:02:43+00:00"
}
]
}
Challenge 3: Pagination
Another challenge is how to decide how many events to show at a time (WebThings Gateway just picks an arbitrary number), and whether pagination features are needed like are being discussed for the Directory Service API. And if so how those features would be described in the Thing Description.
I acknowledge that the above is all a bit complicated and is different to existing operations in that it describes historical data (there is no way to get past values of properties for example). The working group may therefore decide only to specify the readpastevents
operation, or not to specify any new operations at all.
However, if no solution is found to this problem some consequences will be:
Challenge 1: Operation naming
I think this can be done like readallproperties
and readmultipleproperties
operations we currently have so it would be clear whether we mean all events or a subset.
Challenge 2: Payload representation
I think this is a more difficult thing to solve.
A downside of describing the payload declaratively in the Thing Description in this way is that it makes the method of providing timestamps implementation specific which will be harder for consumers to make sense of. Additional semantic annotations might be needed to better explain the semantic meaning of the payloads.
This would also change the Data Schema for a single and historical data, i.e. the example you have would not describe the Data Schema of a single event (btw we wrap event data in a data
object). However, this has given me the following idea (that I am not super satisfied of :grimacing: ):
{
"events": {
"overheated": {
"data": {
"type": "integer"
},
"historicalData": {
"type": "array",
"items": {
"type": "object",
"properties": {
"data": {
"$ref": "#/events/overheated/data"
},
"timestamp": {
"type": "string",
"format": "date-time"
}
}
}
},
"forms": [{
"op": "subscribeevent",
"href": "https://foo.webthings.io/things/lamp/events/overheated",
"subprotocol": "sse"
}, {
"op": "readpastevents",
"href": "https://foo.webthings.io/things/lamp/events/overheated"
}]
}
}
}
Still the timestamp
key needs to be understood by each consumer but it doesn't have to look for a semantic tag everywhere and this would allow the historical data to be modeled as a custom object as well (not that it makes sense but it would be possible).
Regarding pagination, it might be also described in the historicalData
object somehow?
Regarding your last points: I don't think that they should be removed, historical data is very useful for dashboards showing graph of a value over time. If no solution is found, the worst-case solution would be to define a property and link it to the events
This makes sense, but not every device might [be able to] implement it. What matters is that a TD can describe it when there is support for it.
About pagination, an iterator/cursor approach would work on a JS API level, but on protocol level it needs to be supported. The interaction options could contain the usual parameters in this case,
I think that readallpastevents
is a can of worms, but I see the theoretical gap without it :).
I support the idea of introducing a new operation type for this goal, as long as we don't start to spawn one new operation type every time we found that something is missing. I like the approach because is protocol binding independent so that existing implementations do not need any new update, very nice 👍🏻 . Just to mention the possible impact points of the proposal:
Now the bad news, this feature is not backward compatible. In the sense that a TD that has readpastevents
would not validate the TD 1.0 schema. This would force us to wait until TD 2.0 😢. However, I might have an idea.
What if we define a meta/pseudo resource/propertyaffordance(?) called the EventLog
that can be associated with an EventAffordace or a PropertyAffordance or at root level(optionally as @zolkis suggested). Then the EventLog can be read thanks to the newly introduced op type. Kinda following what @egekorkan is proposing. Here's a concrete example:
{
"events": {
"overheated": {
"data": {
"type": "integer"
},
"eventLog": {
/* Event log implements dataschema
so that we can describe it's content
similarly to Ege's example */
"forms":[
{
"op": "readeventlog",
"href": "https://foo.webthings.io/things/lamp/events/overheated/log"
}
]
},
"forms": [
{
"op": "subscribeevent",
"href": "https://foo.webthings.io/things/lamp/events/overheated",
"subprotocol": "sse"
}
]
}
},
"eventLog":{
"forms": [
{
"op": "readalleventlogs", // or simply reuse "readeventlog"
"href": "https://foo.webthings.io/things/lamp/events/logs",
"subprotocol": "sse"
}
]
}
}
We might even exploit this design pattern to solve #302 and other problems related to the state management of actions. The biggest pro right now is that it's backward compatible (i.e., an old client would just ignore it). I also kinda see it as a possible good pattern for future extestions, but what do people think? is it too verbose/complex?
@relu91 wrote:
I support the idea of introducing a new operation type for this goal, as long as we don't start to spawn one new operation type every time we found that something is missing.
I'm afraid this is an inevitable consequence of the form-based declarative protocol binding approach of the W3C TD spec. This list is only going to grow over time. For 1.1 we are already discussing:
readpastevents
(or readeventlog
)readallpastevents
(or readalleventlogs
)queryaction
updateaction
cancelaction
Now the bad news, this feature is not backward compatible. In the sense that a TD that has readpastevents would not validate the TD 1.0 schema.
Ugh, really? Are you sure this isn't backwards compatible? The normative text in section 5.3.4.2 says:
The list of possible operation types of a form is fixed. As of this version of the specification, it only includes the well-known types necessary to implement the WoT interaction model described in [WOT-ARCHITECTURE]. Future versions of the standard may extend this list but operations types SHOULD NOT be arbitrarily set by servients.
Therefore, whilst the list of operations is fixed in 1.0, consumers should be expecting that future versions of the specification may extend this list. It also doesn't say that operations MUST NOT be arbitrarily set by servients, only that they SHOULD NOT, so consumers would trip up on producers which don't follow this guidance.
The JSON Schema in section B does indeed assert that op
must have oneOf
a fixed enum
of values, but that section is non-normative.
If the list of operations really can't be extended, then that was a significant oversight and I don't see how it's possible to fulfill may of the goals set out in the current charter like extending the Thing Description vocabulary, extending protocol binding vocabulary and complex interactions. If this is the case, and the version number is the problem, then rather than using workarounds like defining new nested forms inside interaction affordances (which is a clever solution, but is going to create a real mess over time) I'd advocate simply skipping to 2.0! The charter doesn't say "Web of Things (WoT) Thing Description 1.1", or anything about backwards compatibility, it just says "Web of Things (WoT) Thing Description (Update)".
I'm afraid this is an inevitable consequence of the form-based declarative protocol binding approach of the W3C TD spec. This list is only going to grow over time.
I am not against it, as I said this approach is scalable and well integrated with the current model. It was just a warning to not be too permissive 😅 . I think it would be weird to discuss an operation that would be easily implemented using the existing mechanisms. We might need to describe a rationale that rules the new operation introduction.
The JSON Schema in section B does indeed assert that op must have oneOf a fixed enum of values, but that section is non-normative.
Yeah, I was referring to the JSON schema. Naively I thought that it would follow the normative sections, but as the text is more permissive I think we have no problem then!
If this is the case, and the version number is the problem, then rather than using workarounds like defining new nested forms inside interaction affordances (which is a clever solution, but is going to create a real mess over time) I'd advocate simply skipping to 2.0! The charter doesn't say "Web of Things (WoT) Thing Description 1.1", or anything about backwards compatibility, it just says "Web of Things (WoT) Thing Description (Update)".
Yep, the nested object was more like a temporary solution to be changed in 2.0, still it might be used as a means to the describe the log dataschema as @egekorkan is pointing out.
About Challenge 2:
I think the only way to assure a little bit of consistency is to define proper semantic tags. However, some implementations could use different structures, making it difficult to really use the event log in a consistent matter. We have similar problems with read/writeall/mutipleproperties
as the TD spec does not really explain how the payload really should look like (please, correct me if am wrong).
About challenge 3:
I would say that without pagination reading the event log would be impractical. Therefore, this is a kinda blocking feature that we need to understand how to properly model. In practice, pagination support should provide a description of how an application would provide a page number and total count to the underlying protocol. The output representation might be less problematic cause usually pagination is used for the list of things and the eventLog
would be already described as an array.
If we want to be declarative as in other places, this would imply to resume the idea of the nested object to describe the inputs. Would the eventlog
support other parameters? Filtering? Should we just surrender to the idea that we cannot describe everything declarativly?
Following the declarative path, I would go with the definition of semantic tags (e.g., page
and count
or others) to indicate that those parameters are used for the pagination control. The advantage is that they could be used also in actions or properties.
Now the bad news, this feature is not backward compatible. In the sense that a TD that has readpastevents would not validate the TD 1.0 schema. This would force us to wait until TD 2.0 cry.
Just on this point, backwards compatibility is very poorly defined, you can even ask the TD Task Force members and you will get different answers. If a TD that used to validate now does not, it is serious compatibility issue. However, a new TD does not have to be consumed by an old consumer without any problems. The @context
value will change anyways and if the consumer was doing basic validation, it will fail. So we can add new op
values :)
Regarding adding more and more operations, I think it is inevitable. Scripting API will have to cover those as well. Most programming APIs keep adding new functions/methods as well so maybe it will be seen badly. However, this should spice up the profile discussions ;)
@relu91 wrote:
I think the only way to assure a little bit of consistency is to define proper semantic tags.
I agree.
However, some implementations could use different structures, making it difficult to really use the event log in a consistent matter. We have similar problems with
read/writeall/mutipleproperties
as the TD spec does not really explain how the payload really should look like (please, correct me if am wrong).
Actually the spec does define this:
"The data schema for each of these 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."
In my example I applied the same rule to readallpastevents
.
I would say that without pagination reading the event log would be impractical. Therefore, this is a kinda blocking feature that we need to understand how to properly model.
I like the approach being proposed for the Directory Service API in https://github.com/w3c/wot-discovery/pull/130 (rendered here) which is based on the Linked Data Platform Paging specification using Link and Content-Range headers in responses. The actual payload just ends up being a JSON array because all the pagination metadata is contained in HTTP headers. It might be possible to describe this in a Thing Description but it's a bit of a push, plus it's HTTP specific and I'm not sure whether other protocols have an equivalent.
"forms": [{
"op": "readpastevents",
"href": "https://foo.webthings.io/things/lamp/events/overheated?offset={offset}&limit={limit}",
"htv:methodName": "GET",
"response": {
"htv:headers": [
{
"htv:fieldName": "Link"
},
{
"htv:fieldName": "Content-Range"
}
],
},
"uriVariables": {
"offset": {
"type": "number",
"default": 0
},
"limit": {
"type": "number",
"default": 10
}
}
}]
The above follows the WIP example from the Directory Service API, but I don't think it's complete because I think the htv:headers also needs to specify the meaning of the content of the Link and Content-Range headers (see https://www.w3.org/TR/wot-binding-templates/#example-21-http-vocabulary-example-for-header-options for how you describe the content of headers).
To follow up on this, for the time being I think we've concluded not to try to describe the historical event log WebThings exposes in the Thing Description and to instead implement new Server-Sent Events endpoints which are described in the Thing Description instead. This will most likely follow the events API proposed for the WoT Core Profile in https://github.com/w3c/wot-profile/pull/92
from today's TD call:
I am looking at implementing Events for a TD-providing project with HTTP bindings. The event affoardance seem to cover subscribing to a stream of events, but is there any mechanism for requesting an array of past events?
For context, I have a project that originally started following the Mozilla WOT spec, and I am now adapting it to cover the W3C TD spec instead. Mozilla specifies the event endpoint as an array of past events with timestamps, and this is something I would like to be able to model with a TD.