General API vs Device driver question

Hello Folks,

So I am busy setting up a custom device that has the StartStop nature and uses a custom api that is used to define the most critical channels used inside the device type. Then I have a controller that pulls in the device ID from the config and uses the api above to access the required signals to implement the controller.

My question is the following:

  1. In the controller, if I import the device as is (without making use of the api), I get the warning message:
    Add @SuppressWarnings("restriction")
    The question is, when making use of custom devices with custom functionalities/channels and you want to pull in the device channels into a controller, do you always have to set up an api to define these signals/channels? Or should you rather define the channels individually and specify that inside the controller config.

  2. Also, how do I now go about implementing something like the StartStop functionality, of the device, into the controller? (So that the controller can disable/enable the device.) Or should I just directly access the channel that is defined inside the api to get access to this feature. If yes, I am worried about possible conflict between the controller setpoints sent and the StartStop being in conflict?

I know these are very general, but I would love to hear your thoughts on how to optimally tackle these types of issues. Thanks in advance!

Kind regards,

Bart

Hi Bart,

could you please share the specific code in a Github branch? It’s difficult to explain generally, and I believe I could guide you to some interesting OSGi features for your purpose.

Thanks & Regards,
Stefan

Hi Gents,

This is really something that I have found very difficult to figure out with the OSGI bundle implementation and I would love to hear a way around it, since I have not been able to figure it out.

Typically, from what I have seen, you need an API to define channels that is required by a standard device. The API could also included functions to access these defined channels.

My question is the following: when you have, for example, a modbus device that makes use of this “standard” API’s channels and some other custom channels that is defined inside the specific device. What is the best way of accessing the custom channels from other bundles, like controllers.

Here is an example:
I have an inverter, making use of of the ManagedSymmetricPvInverter which is included in the package io.openems.edge.pvinverter.api;

But now instead of needing to send a constant power setpoint like ACTIVE_POWER_LIMIT, I need to set a custom implementation of droop control channels that you do not typically see. Do I now need to create a custom.inverter.api that details these custom channels? Then include the api inside the device implementation, so that the channels can be accessed from any controller accessing the device.

Adding an extra api feels like so much extra work, since it is only required for this single implementation or am I missing something? I feel like it would be very handy just to directly access the custom channels inside of controllers, without having to add an extra device specific api that is added for an individual device.

Hope to hear some feedback soon.

Regards,

Bartho

Hi Bartho,

sorry, I was very busy recently and was not able to accept your repository invitation in time.

This is the preferred way for everything that is standardized, like the state-of-charge of battery, etc. Using standard “Natures” here, makes it possible to standardize reusable Controllers, OpenEMS UI, etc.

There are two ways:

  1. You can always access a channel by its name. So you could just get a @Reference to your Component, call component.channel("ChannelID") and cast the result to e.g. a IntegerWriteChannel. Downside of this approach is, that all validation (e.g. if the ChannelId does actually exist) is happening only at runtime and might lead to Exceptions.
  2. Using OSGi you can also reference specifically your custom device. I’ll explain this with the example of ESS Time-of-Use Tariff Controller. The Controller needs to know the actual usable capacity of a storage system, so it needs to know if there are any reserved SoC capacities, e.g. by the ESS Emergency Capacity Reserve Controller.
    2.1 According to best practice, the ESS Emergency Capacity Reserve-Controller has separate Interface (ControllerEssEmergencyCapacityReserve.java) and “Impl” classes.
    2.2 The ESS Time-of-Use Tariff-Controller can explicitely demand a Reference for this Controller, by:
    2.2.1 Adding an entry to bnd.bnd [-buildpath](https://github.com/OpenEMS/openems/blob/develop/io.openems.edge.controller.ess.timeofusetariff/bnd.bnd#L11C2-L11C59):
io.openems.edge.controller.ess.emergencycapacityreserve,\

2.2.2 Adding an explicit (in this case optional) Reference to the Controller:

@Reference(policyOption = ReferencePolicyOption.GREEDY, //
		cardinality = ReferenceCardinality.MULTIPLE, //
		target = "(&(enabled=true)(isReserveSocEnabled=true))")
private volatile List<ControllerEssEmergencyCapacityReserve> ctrlEmergencyCapacityReserves = new CopyOnWriteArrayList<>();

2.3 Afterwards you can directly access every Channel by its Channel-ID or any method, e.g.

this.ctrlEmergencyCapacityReserves.stream().mapToInt(ctrl -> ctrl.getActualReserveSoc().orElse(0)

Regards,
Stefan

Hi Stefan,

Thanks for the detailed response. I got it working using 1, but it is good to know that there are some other options as well.

Kind regards,

Bartho