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

"System.Device.Model - attributes for device bindings" progression #2074

Open
logicaloud opened this issue May 6, 2023 · 11 comments
Open

"System.Device.Model - attributes for device bindings" progression #2074

logicaloud opened this issue May 6, 2023 · 11 comments
Assignees
Labels
Design Discussion Ongoing discussion about design without consensus Priority:3 Work that is nice to have

Comments

@logicaloud
Copy link

The approach of adding attributes to devices for plug-and-play scenarios sounds like a good idea. It has not progressed much recently and many devices don't seem to use the attributes that are already defined.

Are there any plans for this to be pushed further?

Or has this been moved to a different package elsewhere?

Or are there any insights that indicate that such an approach is not flexible enough?

@logicaloud logicaloud added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label May 6, 2023
@ghost ghost added the untriaged label May 6, 2023
@krwq
Copy link
Member

krwq commented May 19, 2023

Hello @logicaloud - yes but it's unofficial, I'm driving that project on the side but progress is rather slow because I don't actively work on that (I usually spend a bit of time on that once every couple of weeks). Current goals are following:

  • create representation of those devices which can be generated using attributes (model)
    • there is some progress but it's not happening publicly (there is no particular reason for not being public other than the fact that we don't want to commit to specific design just yet and we don't want people to start depending on it yet while we haven't proven design for ourselves - it's in early alpha stage)
    • our first goal was to create end to end prototype which can generate couple of things using these attributes:
      • DTDL2 file (for Azure PnP)
      • entire console app which reads data and sends it somehwere (input is full class name you'd want the code to be generated for and references for finding that class if needed) - app can be used as a template for further development but if device has parameterless ctor it will work out of the box, otherwise you need to modify one file to create your main class instance - currently it automatically collects data periodically and sends it to Azure Iot Hub but code is designed such that it can be changed relatively easily
      • entire library for reading this data using Rx - input is DTDL2 file
    • now we're prototyping library which can read the attributes and give you the model of those devices - we're playing around with various designs to see what makes most sense
  • while designing model we did some clean up of attributes and we plan investigation if it's possible to reuse i.e. ComponentModel.Annotations instead of our own attributes

basically the largest problems we stumbled upon is that device components (i.e. SenseHat contains: thermometers, accelerometer etc.) are independent from their connection and we need a good way to represent that. We're currently trying to create abstraction which will allow us to reuse that in both our project (so we need to change our prototype generator to use that library) and we're working with folks who designed https://github.com/pi-top/pi-top-4-.NET-SDK because they have similar problem - we're trying to create something which will work for both of our projects.

Some other problems we found:

  • how frequently should data be send
  • generating app is not ideal approach - changing model would require regeneration which will be problematic - library would be much better but designing it is much harder than prototype

cc: @colombod

@krwq krwq added the Priority:3 Work that is nice to have label May 19, 2023
@ghost ghost removed the untriaged label May 19, 2023
@krwq krwq self-assigned this May 19, 2023
@krwq krwq added Design Discussion Ongoing discussion about design without consensus and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels May 19, 2023
@krwq
Copy link
Member

krwq commented May 19, 2023

ref: #2074 - note that we've slightly shifted from "this needs to be Azure PnP" toward "we prefer more generic solution" - prorotype is still using Azure PnP though

@logicaloud
Copy link
Author

Hi @krwq, Thank you for the detailed response.

It is still a while before I can do more research into this topic and I would be very much in favor of a device model that is generic and independent from Azure PnP (which does not preclude targeting it separately, I presume).

In my simple mind, I had envisioned that .NET itself becomes the definition language for this project and all devices can be loaded dynamically and explored using .NET reflection (by applications developed outside of this project). Defined attributes would guide an application to the relevant types and methods, which may be device factories, properties, telemetry, commands, and so forth, and the application could then expose a generic user interface for device configuration and operation, transfer specific data to a cloud IoT provider, or convert device information into a different format (like DTDL). Having a .NET assembly for a device module dropped into a folder for a generic application should be enough to make the device usable. More specialized applications could restrict themselves to deal with some specific device category only, of course.

What you are describing seems to already aim a bit higher and includes developing applications consuming the device model attributes, i.e., applications that generate device representations, solve how device connections should be handled, or how often data should be sent, correct?

The scope that I had envisioned for the project would simply specify attributes and their meanings, sufficient to define the behavior for a large number of devices, including the problem cases you have mentions. If the goal is to create applications, then maybe these applications can be separated out? From my point of view, the project can stand on its own by just specifying the "right attributes", because all kinds of applications can be based on that separately. At the same time I see that development of an application is needed to proof that attribute specifications are sufficient (that can serve as an example).

I may be misunderstanding something. Any thoughts?

@krwq
Copy link
Member

krwq commented Jun 2, 2023

Our first goal is to provide versatile library (model). The goal of the model is to:

  • query what specific device (.NET type) is capable of - what telemetries it can produce etc - in a fairly generic way
  • given object instance it should be able to also get that telemetry (this gets us into "how they're connected" part)

How the devices are connected is not our first goal but we should have at least some sort of initial design for that before we can say that our design of model will play nicely with that. I.e. some device may allow connecting other devices to them (i.e. GPIO expander or FT4222; there are also some devices which produce telemetry AND allow connecting other things) - on its own it may not produce telemetry but stuff connected to it can do it. At the query time we're not aware that anything is connected but we'd still like to understand how to represent that tree structure and how we could add such capability in the future.

Attributes goal is to make it easier to create such model (i.e. rather than doing it by hand you simply tell which type you'd like the model generated for). Some applications require model to be created at runtime and that's our design will revolve around model (this is similar approach to what serializers are doing). We added them early so that we could start experimenting with that since their shape is unlikely going to change greatly and we can start using them for docs as well (unfortunately we lacked time to do that).

Prototype/code-gen is only to prove the design is useful and can be used for i.e. code-gen and if something useful is created from that - even better.

I'd be really interested in hearing more how you'd envision working with attributes/model (i.e. write prototype code of how you'd interact with it). I'm curious of how that would be similar/different to our prototype 😄

@logicaloud
Copy link
Author

Thanks again for sharing your thoughts. Happy to share prototypes but it may be a few months before I can have a good look. My envisioned approach would be simple: Pick some devices and see if the defined interfaces and attributes are sufficient (and convenient / efficient) to construct a working device dynamically by using reflection. If not then revise interfaces and attributes. Turning the code that uses reflection into a library would be secondary step.

@krwq
Copy link
Member

krwq commented Jun 12, 2023

Sure, there is no rush, I'm unlikely going to have time to play with this any time soon... Here is some brain dump and notes of various scenarios to think about...

Some interesting devices to look at when prototyping:

  • SenseHAT (combo of multiple devices but all setup is known up front) - this is my favorite and one of the simplest example to show hierarchy
  • any kind of extender or anything allowing you to add sensors
  • anything requiring some sort of setup (i.e. DC motor)

other interesting devices:

  • devices which take some time to produce telemetry and something you would usually manually trigger (i.e. scale) - initial thoughts on that is that device does not automatically collect telemetry but it is complimented by "produce_telemetry_now" command or similar; other example: buttons, joysticks where telemetry shape/time is really different depending on application - actually - part of SenseHat as well
  • popular devices which are not good for non-real time OS (1-wire thermometers)
  • displays - they don't produce telemetry and input is not straightforward

other interesting scenarios:

  • some applications do not need all telemetry and just need a filtered down version of it (i.e. you're interested when temeperature/water level/... is below threshold rather than what's the current level)
  • statistical information - moving averages etc. - data with lots of noise like accelerometer
  • on the usage side - you might need data from both device which is directly connected to your board and remote data from things like temperature in different building

for the last one - we've had fairly good luck with Rx - so far we haven't seen any problems with that approach and code is clean and really easy to add stats/filtering/essentially whatever. Also plays really well with anything telemetry related including combining, transforming etc. The downside of Rx is that writing Rx code requires knowing the library (reading is simple though) - with ChatGPT now on the table perhaps is no longer that big of deal anymore.

@logicaloud
Copy link
Author

Great info, thank you. I'll leave it to you to decide whether this issue should be closed in the meantime.

@logicaloud
Copy link
Author

logicaloud commented Jun 29, 2023

Edit: Having dug a bit deeper, I think some of my previous thoughts are just not feasible (i.e. device probing/discovery) or already realized in some other form. It is probably best to carry on when I have something more tangible.

@logicaloud
Copy link
Author

Finally, I had a chance to have a closer look at the device model. Overall, I think the current attributes and constraints worked out well. There are of course quite a few devices that do not have these attributes.

Initially I had envisioned an approach where all devices are loaded dynamically at runtime, accessing reflected methods and properties, and so forth. While that would be possible, the parsing code would become quite complex, and performance could become a concern. I ended up going down the code generation path. This then resulted in code that could be tightly integrated into the targeted solution. It does mean though that for each new device or device change, another code generation run is required (which could still be automated).

I carried on with the mindset that the library’s device model is not primarily targeted at dynamic device instantiation and operation, but to form a basis for custom solutions. Any solution on top of what is already there is likely to be opinionated. A ‘less opiniated’ generic solution for plug and play would probably benefit from using interfaces as definite contracts rather than attributes; attributes could still be used to generate that solution (which could become part of the library itself).

In my case, the custom solution involved the integration of the IoT library for a browser UI with a containerized backend, designed around the concepts of the loT library, i.e. exposing properties, telemetries, commands, and “events”:

The addition of an “Event” attribute may be useful to indicate that a device generates data on its own. For example, the GPIO controller has callbacks to signal a pin value change, and in my experimentation, I squeezed the GPIO controller into the existing device model with the addition of the “Event” attribute on pin values. If I am not mistaken, then there are other devices that can notify values changed. In general, the Event attribute could either be added to telemetry, or to Subscribe/Unsubscribe methods to indicate to the code generator that event notifications are available.

Components provided through SenseHat did not really pose a challenge. The Component attribute was simply used to browse through the components and then treat them like any other device, limited to those components that expose device model attributes. This does not include the LED matrix, for example – it will be interesting to see how this is going to work. That the SenseHat components allow for default connection settings is already expressed in the optional construction parameters (i.e., i2caddress = null), which can be evaluated in the code generator, although I have not done so yet.

I have kept device discoverability simple: All devices within the IoT library (with attributes) are available for selection in the UI, and it is left to the user to pick one that is actually there, then the user can select any of the device's properties, telemetries, commands or events to operate on,

Some challenges for integrating the ~50 devices that currently use the device model attributes were:

  • There could be multiple device constructors.
  • Device constructors could derive from a base class.
  • Constructor arguments could be optional.
  • Some device constructors take another device as a parameter. I have not explored this yet, but I think the solution will work for this scenario with a small addition, also for a chain of embedded devices.
  • Not sure yet how to best handle shoudDispose in constructors; this parameter is currently not exposed in the user interface and is set to “true”.
  • Methods returning tuples required some special handling.

So far, I have only tested the generated solution with the various SenseHat devices and did some conceptual GPIO testing. There is no way for me to test all devices. But if one “representative” device works, then so should others that fall into the same category.

Is the current objective to continue with the use of the device model attributes?

If yes, then I’d like to contribute by adding attributes to more devices and explore the potential limitations imposed by device categories that I have not explored yet.

@Ellerbach
Copy link
Member

Thanks @logicaloud for this feedback. And explaining your thoughts process and where you ended up. We're very open for improvements and PR on the device model and on the bindings to make them better!
Some thoughts from you challenges:

There could be multiple device constructors.

Maybe this can be addressed with a specific attribute like [DefaultConstructor] or something like this that can be added to the device model?

Device constructors could derive from a base class.

Similar to the previous one, if there is an heritage, it still can be trapped with the same principle and a default one.

Constructor arguments could be optional.

In that case, then, we can assume that the default ones are the correct ones for those scenarios. In most cases, it's indeed what's done and the reason they are optionals.

Some device constructors take another device as a parameter. I have not explored this yet, but I think the solution will work for this scenario with a small addition, also for a chain of embedded devices.

Yes, I guess it's possible to create a "device tree" with the dependencies, so that you can ask to have a class created before. That should also solve the I2C, SPI problem. They can be created the same way as any other object.

Not sure yet how to best handle shoudDispose in constructors; this parameter is currently not exposed in the user interface and is set to “true”.

As any default parameter: they are ment to be for "normal usage"

Methods returning tuples required some special handling.

For that one, no idea :-)

@logicaloud
Copy link
Author

Thank you for your feedback, @Ellerbach.

Maybe this can be addressed with a specific attribute like [DefaultConstructor] or something like this that can be added to the device model?

I solved this by ordering the constructors by the number of parameters (higher number first), and then check whether the user has specified all parameters of the first constructor; if not, then move onto the next constructor, and so forth. A [DefaultConstructor] parameter is not required, I believe. If there are gaps in the specified parameters, then the constructor will complain and the user gets an error message.

Methods returning tuples required some special handling.

It just required some type unpacking to have some presentable type information (that will also be required for other types used in commands, i.e. ReadOnlySpan), but that is unrelated to the device model itself.

I have added a pull request for SenseHat here #2324, if you'd like to check how that fits into the device model.

If that's OK, then the next device I'd like to apply the device model to is the GPIO controller with a concept of an "Event" attribute.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Discussion Ongoing discussion about design without consensus Priority:3 Work that is nice to have
Projects
None yet
Development

No branches or pull requests

3 participants