Hi all, I’ve started playing with the Igntion 8 SDK, with no experience of the previous SDKs, and I’ve managed to open the SDK examples modify the sources and create modules with some little customization.
In particular I’m actually looking the OPC UA Device example: using the example code provided, I could extend it creating a device taking custom settings, and create a list of folder/tags in the device with some custom logic. It’s pretty straitforward given the example code.
What I’d like to do now, and I’m quite stuck in it, is the “opposite” thing: I wish to create an OPC tag on the gateway, pointing to my custom device, and get the related value based on the OPC item path. I guess that the point of the matter lies behind the classes SubscriptionModel and DeviceContext, but I really don’t get a clue of how to use them.
Can someone give me some tips to address me in the right way?
Many thanks in advance,
Francesco
I’m not sure I understand what you’re asking.
Are you trying to build a Device implementation that can respond dynamically to the NodeIds (OPC Item Path up in Ignition) it receives in read/write/subscribe calls? Instead of defining them ahead of time like the example?
Yes exactly
Okay, in that case you would either:
- implement the
Device
interface directly, or - override the
browse
,read
,write
methods fromManagedAddressSpace
and determine if the NodeId represents a dynamically addressable Node, a pre-defined “managed” Node, or isn’t a Node at all, before taking any action.
There’s no examples for this, you’re on your own a bit. There’s javadoc on these methods and they’re actually directly inherited from the AddressSpaceServices
composite interface (and in turn from its sub-interfaces), which is part of the Milo OPC UA SDK.
Thanks for the tips! With your help I made a little step further: I took the ExampleDevice class from the sdk example, that extends ManagedAddressSpaceServices, and overridden the three methods you told me, anyway it seems to me that the only method I need to override is “read”, is it correct?
Anyway, the “read” method implemented in the base class has this code:
public void read(ReadContext context, Double maxAge, TimestampsToReturn timestamps, List<ReadValueId> readValueIds) {
List<DataValue> results = Lists.newArrayListWithCapacity(readValueIds.size());
Iterator var6 = readValueIds.iterator();
while(var6.hasNext()) {
ReadValueId readValueId = (ReadValueId)var6.next();
UaServerNode node = (UaServerNode)this.nodeManager.get(readValueId.getNodeId());
if (node != null) {
DataValue value = node.readAttribute(new AttributeContext(context), readValueId.getAttributeId(), timestamps, readValueId.getIndexRange(), readValueId.getDataEncoding());
this.logger.debug("Read value {} from attribute {} of {}", new Object[]{value.getValue().getValue(), AttributeId.from(readValueId.getAttributeId()).map(Object::toString).orElse("unknown"), node.getNodeId()});
results.add(value);
} else {
/* UNMANAGED NODE */
results.add(new DataValue(2150891520L));
}
}
context.success(results);
}
Using some logging, I’ve found that, creating a new OPC tag pointing to that device and with a custom opc path, I fall in the UNMANAGED NODE case: but at this point how to set the value? I thought that I could simply do something like results.add(new DataValue(“CUSTOM VALUE”)) but it’s not working.
Thanks again for the help!
Well, what’s probably going wrong is that Ignition is trying to subscribe to this tag, not just read it, and that’s triggering other code paths that your Device hasn’t implemented yet. You also need to pay attention to the Attribute being read, because it won’t always be the Value attribute.
Try issuing a system.opc.readValue
for your test NodeId rather than creating a tag in Ignition. This should directly trigger your overridden read
method.
And if it helps, the code from the parent class is open source, you don’t need to decompile: https://github.com/eclipse/milo/blob/9d92144355ca74ad999ca9082aaad7cdab023adf/opc-ua-sdk/sdk-server/src/main/java/org/eclipse/milo/opcua/sdk/server/api/ManagedAddressSpaceServices.java#L159-L195