I am trying to create an OPC-UA device driver in 8.1 and am using the OPC-UA example from GitHub as the starting point.
I understand the general gist of the example, but am trying to figure out how to implement a device that subscribes to data changes in a controller. I can't figure out the correct way to push these changes back to ignition, or how to handle polls from ignition where there has been no data change. My device also doesn't support browsing, and I can't figure out from the example how to implement tags without populating the tag browser.
I've tried looking for the javadoc for the managed device. It appears that all of the opcua packages are missing from the ignition javadoc web pages. com.inductiveautomation.ignition.gateway.opcua.server.api.ManagedDevice
I'm also not sure the function of the following methods:
public void onDataItemsCreated(List<DataItem> dataItems) {
subscriptionModel.onDataItemsCreated(dataItems);
}
public void onDataItemsModified(List<DataItem> dataItems) {
subscriptionModel.onDataItemsModified(dataItems);
}
public void onDataItemsDeleted(List<DataItem> dataItems) {
subscriptionModel.onDataItemsDeleted(dataItems);
}
public void onMonitoringModeChanged(List<MonitoredItem> monitoredItems) {
subscriptionModel.onMonitoringModeChanged(monitoredItems);
}
Thanks for the info regarding milo.
I've looked through the milo examples and I'm still a bit lost.
I don't understand how the ManagedDevice lines up with milo or how I am supposed to receive notification of new tags with a new 'OPC Item Path'.
I'll look into this on Monday. I know there are some docs written on the basic interfaces, but those interfaces are written in Kotlin, and so producing Javadocs for those interfaces/classes presented some issue for our build system that I don't think was ever resolved. Might not be able to produce any until I rewrite the interfaces back to Java in 8.3
If you can't browse your device, and you don't know ahead of time or at configuration time what Nodes will exist, then the ManagedDevice abstraction won't be of any use to you and you'll just have to implement Device directly.
Your implementation doesn't have to implement browsing. By default, any NodeId with a String identifier starting with [YourDeviceName] will be passed to your Device implementation's Read, Write, Browse, or Monitored Item related calls.
The example is implementing Subscription / Monitored Item behavior by simply calling the read method over and over. It's up to your implementation to do something smarter if it's available. You don't ever just arbitrarily send information to the client, though. The client creates Monitored Items, and you are responsible for updating that item at the requested Sampling Interval, based on your best efforts.
The honest reality is that between the current examples (Ignition SDK, Milo example server and demo server) and the available code (all Milo stuff is open source), it's sink or swim and you need to figure it out.
Most of the Device API is just a thin extension on top of Milo OPC UA stuff, except the basic things on Device that aren't inherited, or the registration of a DeviceType, etc...
Nothing, but for some reason our build system, or the KDoc plugin, or some combination, won't produce/combine Javadocs for the stuff written in Kotlin. I don't remember the details any more because I don't work on the build.
I have a driver where nodes are not known at startup, so I need to implement Device interface instead of extend a ManagedDevice.
Is it possible to create the UaNode from the onDataItemsCreated ?
Is onDataItemsCreated called when Ignition tag opc item has not been created in the device ?
public class TestDevice implements Device {
@NotNull
@Override
public String getName() {
return null;
}
@NotNull
@Override
public String getStatus() {
return "Running";
}
@NotNull
@Override
public String getTypeId() {
return null;
}
@Override
public void startup() {
}
@Override
public void shutdown() {
}
@Override
public void read(ReadContext readContext, Double aDouble, TimestampsToReturn timestampsToReturn, List<ReadValueId> list) {
}
@Override
public void write(WriteContext writeContext, List<WriteValue> list) {
}
@Override
public void onDataItemsCreated(List<DataItem> list) {
}
@Override
public void onDataItemsModified(List<DataItem> list) {
}
@Override
public void onDataItemsDeleted(List<DataItem> list) {
}
@Override
public void onMonitoringModeChanged(List<MonitoredItem> list) {
}
@Override
public void browse(BrowseContext browseContext, ViewDescription viewDescription, NodeId nodeId) {
}
@Override
public void getReferences(BrowseContext browseContext, ViewDescription viewDescription, NodeId nodeId) {
}
}
It won't be called unless your Device indicates that Node exists and is managed by your AddressSpace. It doesn't necessarily need a UaNode instance to exist, but when a client calls CreateMonitoredItems, you'll end up getting a call to read for some of the attributes needed when creating a MonitoredItem, and provided this call is successful and a MonitoredItem can be created, you'll get a subsequent call to onDataItemsCreated.
If you're building a driver where you don't know what exists ahead of time, then maybe consider just not creating any UaNode instances at all. It's possible to do everything dynamically. My Modbus Server Driver does this.
For Modbus and Modbus-like drivers where you just address memory ranges it makes a lot of sense. For others where there's a well-defined model (Logix, 61850), or other reasons (DNP3, BACnet), we use UaNode instances.
That said - a future rewrite of the Logix driver would go fully dynamic.
Part of the reason the AddressSpace API looks like it does (instead of being perhaps easier to use) is to maintain the ability for an unlimited number of Nodes to exist without actually needing instances of them to exist in memory.
My drivers are good candidates, then, since they all have a "model" of the target's configured or browsed address space. In other words, its all in memory that way now. Just need attribute handling for those.
After the v83 conversion hysteria settles, I'll revisit this.