You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@plc4x.apache.org by Łukasz Dywicki <lu...@code-house.org> on 2022/05/16 20:50:41 UTC
Higher level device API for drivers - stepping above a plc field
Hello,
I been thinking of issues I so far found within plc4x api and one of
them is field-centric approach which bounds client to think in the same
way. Yet, some protocols think in different categories and there
field-centric work turns out into a nightmare.
For example within CANopen it is possible to read an variable length
array of values through SDO transfer (ie. object 0x5000). Yet, in order
to do that using plc4x apis it is necessary to construct a first call
(0x5000/0x0) which reads array length and then goes over sub-indexes
(0x5000,0x1..N). This is not a big burden, but as soon as same structure
is used across multiple places amount of code to orchestrate array
retrieval grows. More over, I think that everyone who will get into
CANopen and SDO read outs will sooner or later copy this logic to his code.
I thought if driver could get it better, but I don't think it would
without .. object dictionary which is specific to CANopen. Not sure of
other protocols, as they seem to be rather fine with field level access,
but I don't think that this measure fits all is right. Looking at more
complex protocols such as BACnet it is indeed possible to make a client
which rely on fields, but it will turn caller code into a mess forcing
further structuring of layers above plc4x just to reflect protocol
philosophy. On other hand we have a M-Bus which in most of the cases
works by returning all device measurements at one shot breaking the 1:1
mapping between request and reply item. We haven't made a consensus how
it could be addressed, but to me this protocol (as well as WM-Bus) is
not well fit to keep above contract.
Because of rather bad code I made in 2020 while working on first CANopen
binding for openHAB I started to re-structure it and improve its
internal design in 2021. A bit of time have passed and I am again in a
place where I don't think my work back then was going into right
direction. First of all, I extracted CoNode, CoConnection and several
more interfaces, but still at interaction level it does not involve
object dictionary describing device abilities which is crucial in quick
bootstrapping of integration. As I am preparing myself to another
attempt to untie connectorio canopen binding code from specific vendor
flavor I had to implement, I am back to the problem of 1) where to put
higher level API 2) how to structure it.
While I currently do not running bacnet not profinet driver from plc4x I
do smell very similar troubles there, especially with bacnet objects
(ie. schedules) and profinet configs which reflect GSDML and other
descriptors out there. Obviously we can always make everything a field,
but will it make a sense?
_just an idea (tm)_
My brief thinking is about possibility to define an
"ExtendedDriver<Rq,Rp>" interface (rq - request container, rp - response
container), which would provide us a generic purpose request/response
logic as well as subscription stuff. I am sure it will be a bunch of
work, however in some ways this might feel easier to implement than
mapping everything to fields and back. This could be compared to a
stored procedure call within JDBC api which is usually vendor specific.
Caller of such procedure usually has to handle result according to
database provider.
In our case through definition of request/response container we could at
least enforce some basic contract between various equipment.
It would let us also to define a higher level types (subset of PlcObject
convertible to a PlcStruc's?) to interact:
ExtendedDriver<CANopenReqest, CANopenResponse> extendedDriver;
// assuming CompletableFuture as outcome
// maybe request could optionally have a SDO entry mapper?
extendedDriver.read(new CANopenSDORequest(0x5000, CANopenType.ARRAY))
.whenComplete((CANopenResponse r, Throwablee ) -> {
// CANopen specific logic, but well, it is interacting with CANopen
// and this code knows what to do with it
MappedData[] data = r.getArray(/*maybe SDO mapper here?*/);
// CANopenObject o = r.getPlcObject() ??
})
I don't really have any time frame nor pursue to work on that topic,
however I wanted to bring it up as I feel we could improve (simplify
till some degree) device interaction in some cases. Maybe not all, but
these which are more "object oriented" for sure. I want also to give
myself enough of time to think of how to make a first attempt without
making it too dumb nor too complicated.
Above, I think, could also be used for discovery of symbols or objects
through connections which rely on the same transport as driver itself.
For example Beckhoff symbols are just read from index within TCP
connection we make to PLC. Also, if we look at udp discovery in BACnet,
its just one of operations defined in protocol. Both give us different
outcome, yet they do work over connection we already made. Obviously it
can be mapped through connection metadata. While it would fit into this
API (which is not used anywhere I guess), this approach also gives us a
trouble of mapping all into plc fields. With an explicit contract of
field retrieval we are forced to serialize everything to a field, even
if it does not reflect how given automation system work. Also our field
definitions will get more and more complicated and harder to keep in
sync between languages.
Looking forward to your comments on above or counter ideas. Till summer
season I suppose. :-)
Best,
Łukasz
Re: Higher level device API for drivers - stepping above a plc field
Posted by Sebastian Rühl <sr...@apache.org>.
Hi Łukasz,
at the moment I use the model classes directly but regarding BACNet I also expect the troubles you are describing. So I like the idea to have a way to work with protocol native features directly like you described with the ExtendedDriver.
- Sebastian
On 2022/05/16 20:50:41 Łukasz Dywicki wrote:
> Hello,
> I been thinking of issues I so far found within plc4x api and one of
> them is field-centric approach which bounds client to think in the same
> way. Yet, some protocols think in different categories and there
> field-centric work turns out into a nightmare.
>
> For example within CANopen it is possible to read an variable length
> array of values through SDO transfer (ie. object 0x5000). Yet, in order
> to do that using plc4x apis it is necessary to construct a first call
> (0x5000/0x0) which reads array length and then goes over sub-indexes
> (0x5000,0x1..N). This is not a big burden, but as soon as same structure
> is used across multiple places amount of code to orchestrate array
> retrieval grows. More over, I think that everyone who will get into
> CANopen and SDO read outs will sooner or later copy this logic to his code.
> I thought if driver could get it better, but I don't think it would
> without .. object dictionary which is specific to CANopen. Not sure of
> other protocols, as they seem to be rather fine with field level access,
> but I don't think that this measure fits all is right. Looking at more
> complex protocols such as BACnet it is indeed possible to make a client
> which rely on fields, but it will turn caller code into a mess forcing
> further structuring of layers above plc4x just to reflect protocol
> philosophy. On other hand we have a M-Bus which in most of the cases
> works by returning all device measurements at one shot breaking the 1:1
> mapping between request and reply item. We haven't made a consensus how
> it could be addressed, but to me this protocol (as well as WM-Bus) is
> not well fit to keep above contract.
>
> Because of rather bad code I made in 2020 while working on first CANopen
> binding for openHAB I started to re-structure it and improve its
> internal design in 2021. A bit of time have passed and I am again in a
> place where I don't think my work back then was going into right
> direction. First of all, I extracted CoNode, CoConnection and several
> more interfaces, but still at interaction level it does not involve
> object dictionary describing device abilities which is crucial in quick
> bootstrapping of integration. As I am preparing myself to another
> attempt to untie connectorio canopen binding code from specific vendor
> flavor I had to implement, I am back to the problem of 1) where to put
> higher level API 2) how to structure it.
> While I currently do not running bacnet not profinet driver from plc4x I
> do smell very similar troubles there, especially with bacnet objects
> (ie. schedules) and profinet configs which reflect GSDML and other
> descriptors out there. Obviously we can always make everything a field,
> but will it make a sense?
>
> _just an idea (tm)_
> My brief thinking is about possibility to define an
> "ExtendedDriver<Rq,Rp>" interface (rq - request container, rp - response
> container), which would provide us a generic purpose request/response
> logic as well as subscription stuff. I am sure it will be a bunch of
> work, however in some ways this might feel easier to implement than
> mapping everything to fields and back. This could be compared to a
> stored procedure call within JDBC api which is usually vendor specific.
> Caller of such procedure usually has to handle result according to
> database provider.
> In our case through definition of request/response container we could at
> least enforce some basic contract between various equipment.
> It would let us also to define a higher level types (subset of PlcObject
> convertible to a PlcStruc's?) to interact:
>
> ExtendedDriver<CANopenReqest, CANopenResponse> extendedDriver;
> // assuming CompletableFuture as outcome
> // maybe request could optionally have a SDO entry mapper?
> extendedDriver.read(new CANopenSDORequest(0x5000, CANopenType.ARRAY))
> .whenComplete((CANopenResponse r, Throwablee ) -> {
> // CANopen specific logic, but well, it is interacting with CANopen
> // and this code knows what to do with it
> MappedData[] data = r.getArray(/*maybe SDO mapper here?*/);
> // CANopenObject o = r.getPlcObject() ??
> })
>
> I don't really have any time frame nor pursue to work on that topic,
> however I wanted to bring it up as I feel we could improve (simplify
> till some degree) device interaction in some cases. Maybe not all, but
> these which are more "object oriented" for sure. I want also to give
> myself enough of time to think of how to make a first attempt without
> making it too dumb nor too complicated.
> Above, I think, could also be used for discovery of symbols or objects
> through connections which rely on the same transport as driver itself.
> For example Beckhoff symbols are just read from index within TCP
> connection we make to PLC. Also, if we look at udp discovery in BACnet,
> its just one of operations defined in protocol. Both give us different
> outcome, yet they do work over connection we already made. Obviously it
> can be mapped through connection metadata. While it would fit into this
> API (which is not used anywhere I guess), this approach also gives us a
> trouble of mapping all into plc fields. With an explicit contract of
> field retrieval we are forced to serialize everything to a field, even
> if it does not reflect how given automation system work. Also our field
> definitions will get more and more complicated and harder to keep in
> sync between languages.
>
> Looking forward to your comments on above or counter ideas. Till summer
> season I suppose. :-)
>
> Best,
> Łukasz
>