I try a minimal implementation, but when I Browse the devices, I have a read time out:
com.inductiveautomation.ignition.client.gateway_interface.GatewayException: Read timed out
I probably need to implement getReference but I not sure how to do that ?
package com.inductiveautomation.ignition.examples.tagdriver;
import com.inductiveautomation.ignition.gateway.opcua.server.api.Device;
import com.inductiveautomation.ignition.gateway.opcua.server.api.DeviceContext;
import com.inductiveautomation.ignition.gateway.opcua.server.api.DeviceSettingsRecord;
import org.eclipse.milo.opcua.sdk.core.AccessLevel;
import org.eclipse.milo.opcua.sdk.core.Reference;
import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
import org.eclipse.milo.opcua.sdk.server.UaNodeManager;
import org.eclipse.milo.opcua.sdk.server.api.DataItem;
import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem;
import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaNodeContext;
import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode;
import org.eclipse.milo.opcua.sdk.server.nodes.filters.AttributeFilters;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.BuiltinDataType;
import org.eclipse.milo.opcua.stack.core.Identifiers;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;
import org.eclipse.milo.opcua.stack.core.types.structured.ViewDescription;
import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Set;
/*
https://forum.inductiveautomation.com/t/opc-ua-javadoc-and-general-functionality/81913/13
*/
public class ExampleDevice implements Device {
private final Logger logger = LoggerFactory.getLogger(getClass());
// private final SubscriptionModel subscriptionModel;
private final DeviceContext deviceContext;
private final DeviceSettingsRecord deviceSettings;
private static GenericDeviceNodeContext nodeContext;
private String status = "Running";
public ExampleDevice(
DeviceContext deviceContext,
DeviceSettingsRecord deviceSettings
) {
this.deviceContext = deviceContext;
this.deviceSettings = deviceSettings;
}
@Override
public @NotNull
String getName() {
return deviceSettings.getName();
}
@Override
public @NotNull
String getStatus() {
//return "Running";
return status;
}
@Override
public @NotNull
String getTypeId() {
return deviceSettings.getType();
}
@Override
public void onDataItemsCreated(List<DataItem> dataItems) {
for (DataItem dataItem : dataItems) {
logger.info("onDataItemsCreated : {}", dataItem.getReadValueId().getNodeId().getIdentifier().toString());
logger.info("onDataItemsCreated dataItem.getId() : {}", dataItem.getId());
logger.info("onDataItemsCreated dataItem.getSubscriptionId() : {}", dataItem.getSubscriptionId());
}
}
@Override
public void onDataItemsModified(List<DataItem> dataItems) {
//subscriptionModel.onDataItemsModified(dataItems);
for (DataItem dataItem : dataItems) {
logger.info("onDataItemsModified : {}", dataItem.getReadValueId().getNodeId().getIdentifier().toString());
logger.info("onDataItemsModified dataItem.getId() : {}", dataItem.getId());
}
}
@Override
public void onDataItemsDeleted(List<DataItem> dataItems) {
//subscriptionModel.onDataItemsDeleted(dataItems);
for (DataItem dataItem : dataItems) {
logger.info("onDataItemsDeleted : {}", dataItem.getReadValueId().getNodeId().getIdentifier().toString());
logger.info("onDataItemsDeleted dataItem.getId() : {}", dataItem.getId());
}
}
@Override
public void onMonitoringModeChanged(List<MonitoredItem> monitoredItems) {
//subscriptionModel.onMonitoringModeChanged(monitoredItems);
for (MonitoredItem monitoredItem : monitoredItems) {
logger.info("onMonitoringModeChanged : {}", monitoredItem.getReadValueId().getNodeId().getIdentifier().toString());
logger.info("onMonitoringModeChanged dataItem.getId() : {}", monitoredItem.getId());
}
}
@Override
public void startup() {
logger.info("startup()");
this.nodeContext = new GenericDeviceNodeContext(deviceContext);
UaFolderNode rootNode = new UaFolderNode(
nodeContext,
deviceContext.nodeId(getName()),
deviceContext.qualifiedName(String.format("[%s]", getName())),
new LocalizedText(String.format("[%s]", getName()))
);
logger.info("create node : {}", String.format("[%s]", getName()));
// add the folder node to the server
nodeContext.getNodeManager().addNode(rootNode);
// add a reference to the root "Devices" folder node
rootNode.addReference(new org.eclipse.milo.opcua.sdk.core.Reference(
rootNode.getNodeId(),
Identifiers.Organizes,
deviceContext.getRootNodeId().expanded(),
Reference.Direction.INVERSE
));
addStaticNodes(rootNode, "readOnly", 10, AccessLevel.READ_ONLY);
}
private void addStaticNodes(UaFolderNode rootNode, String name, int count, Set<AccessLevel> accessLevel) {
UaFolderNode folder = new UaFolderNode(
this.nodeContext,
deviceContext.nodeId(name),
deviceContext.qualifiedName(name),
new LocalizedText(name)
);
this.nodeContext.getNodeManager().addNode(folder);
rootNode.addOrganizes(folder);
String name1 = folder.getDisplayName().getText();
for (int i = 0; i < count; i++) {
String formattedName = String.format("%s%d", name1, i);
UaVariableNode node = UaVariableNode.builder(this.nodeContext)
.setNodeId(deviceContext.nodeId(String.format("%s/node%d", formattedName, i)))
.setBrowseName(deviceContext.qualifiedName(formattedName))
.setDisplayName(new LocalizedText(formattedName))
.setDataType(BuiltinDataType.UInt16.getNodeId())
.setTypeDefinition(Identifiers.BaseDataVariableType)
.setAccessLevel(accessLevel)
.setUserAccessLevel(accessLevel)
.build();
node.setValue(new DataValue(new Variant(i)));
if (accessLevel.contains(AccessLevel.CurrentWrite)) {
// This filter just intercepts the write to log it before
// passing it to the next filter in the chain. The default
// filter instance at the end will write the attribute to
// the UaNode instance.
node.getFilterChain().addLast(AttributeFilters.setValue((ctx, value) -> {
logger.info("setValue: {}", value.getValue().getValue());
ctx.setAttribute(AttributeId.Value, value);
}));
}
nodeContext.getNodeManager().addNode(node);
folder.addOrganizes(node);
}
}
@Override
public void shutdown() {
logger.info("shutdown()");
}
@Override
public void read(ReadContext readContext, Double aDouble, TimestampsToReturn timestampsToReturn, List<ReadValueId> list) {
logger.info("read()");
}
@Override
public void write(WriteContext writeContext, List<WriteValue> list) {
logger.info("write()");
}
@Override
public void browse(BrowseContext browseContext, ViewDescription viewDescription, NodeId nodeId) {
logger.info("browse()");
}
@Override
public void getReferences(BrowseContext browseContext, ViewDescription viewDescription, NodeId nodeId) {
logger.info("getReferences() - viewDescription = {} - nodeId = {}",viewDescription,nodeId);
}
public static class GenericDeviceNodeContext implements UaNodeContext {
public final DeviceContext deviceContext;
public final UaNodeManager nodeManager;
public GenericDeviceNodeContext(DeviceContext deviceContext) {
this.deviceContext = deviceContext;
this.nodeManager = new UaNodeManager();
}
@Override
public OpcUaServer getServer() {
return deviceContext.getServer();
}
@Override
public UaNodeManager getNodeManager() {
return nodeManager;
}
}
}