RSISourceNode.java

package de.dlr.bt.stc.source.rsi.uas;

import java.util.concurrent.CompletableFuture;

import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectNode;
import org.eclipse.milo.opcua.sdk.server.nodes.UaObjectTypeNode;
import org.eclipse.milo.opcua.stack.core.Identifiers;
import org.eclipse.milo.opcua.stack.core.UaException;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;

import de.dlr.bt.stc.init.Register;
import de.dlr.bt.stc.opcuaserver.ANodeCreator;
import de.dlr.bt.stc.opcuaserver.NodeFactory;
import de.dlr.bt.stc.opcuaserver.STCNamespace;
import de.dlr.bt.stc.opcuaserver.STCNamespace.Folders;
import de.dlr.bt.stc.source.rsi.RSISource;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RSISourceNode extends ANodeCreator {
	@Register
	public static void register() {
		NodeFactory.getInstance().registerCreator(RSISource.class, RSISourceNode::new);
	}

	private static final String RSISOURCE_TYPE = "RSISourceType";
	private static final String PORT = "Port";
	private static final String DATATYPE = "DataType";
	private static final String PATH = "Path";

	private static final String RSIVALUE_TYPE = "RSIValueType";
	private static final String VALUE = "Value";
	private static final String LAST_UPDATE = "LastUpdate";

	private UaObjectTypeNode sourceTypeNode;
	private UaObjectTypeNode valueTypeNode;

	private RSISourceNode(STCNamespace namespace) {
		super(namespace, "stc", "sources", "rsisource");
	}

	@Override
	public void createObjectType() {
		var port = namespace.createObjectTypeComponent(PORT, namespace.newNodeId(nodePathType(PORT)),
				Identifiers.Integer);
		var datatype = namespace.createObjectTypeComponent(DATATYPE, namespace.newNodeId(nodePathType(DATATYPE)),
				Identifiers.String);
		var path = namespace.createObjectTypeComponent(PATH, namespace.newNodeId(nodePathType(PATH)),
				Identifiers.String);

		var value = namespace.createObjectTypeComponent(VALUE, namespace.newNodeId(nodePathType(VALUE)), NULLNODEID);
		var lastupdate = namespace.createObjectTypeComponent(LAST_UPDATE,
				namespace.newNodeId(nodePathType(LAST_UPDATE)), Identifiers.DateTime);

		valueTypeNode = namespace.createObjectTypeNode(RSIVALUE_TYPE, namespace.newNodeId(nodePathType(RSIVALUE_TYPE)),
				value, lastupdate);

		sourceTypeNode = namespace.createObjectTypeNode("RSISource", namespace.newNodeId(nodePathType(RSISOURCE_TYPE)),
				port, datatype, path);
	}

	@Override
	public void createInstance(Object forNode, Folders folders) {
		if (!(forNode instanceof RSISource rsiSource))
			return;

		try {
			String id = rsiSource.getConfig().getId();
			var uon = namespace.createObjectNode(sourceTypeNode, id, namespace.newNodeId(nodePathInst(id)),
					folders.getSourceFolder());

			rsiSource.addNewVariableListener(
					variable -> CompletableFuture.runAsync(() -> addNewVariable(variable, rsiSource, uon)));

			namespace.setObjectNodeComponent(uon, PORT, new Variant(rsiSource.getConfig().getPort()));
			namespace.setObjectNodeComponent(uon, DATATYPE,
					new Variant(rsiSource.getConfig().getDatatype().toString()));
			namespace.setObjectNodeComponent(uon, PATH, new Variant(rsiSource.getConfig().getPath()));

			addRootNode(forNode, uon);
		} catch (UaException e) {
			log.info("Exception during creation of NodeInstance {}: {}", rsiSource.getConfig().getId(), e);
		}
	}

	private void addNewVariable(String variableName, RSISource source, UaObjectNode objectNode) {
		try {
			var vn = namespace.createObjectNode(valueTypeNode, variableName,
					namespace.newNodeId(nodePathInst(source.getConfig().getId(), variableName)), objectNode);

			var valuenode = namespace.getObjectNodeComponent(vn, VALUE);

			NodeId datatype = Identifiers.String;
			var currentValue = source.getRsiValues().get(variableName).getValue();
			if (currentValue instanceof Long)
				datatype = Identifiers.Integer;
			else if (currentValue instanceof Double)
				datatype = Identifiers.Double;
			else if (currentValue instanceof Boolean)
				datatype = Identifiers.Boolean;

			valuenode.setDataType(datatype);

			namespace.setObjectNodeComponent(vn, VALUE,
					context -> new DataValue(new Variant(source.getRsiValues().get(variableName).getValue())));
			namespace.setObjectNodeComponent(vn, LAST_UPDATE, context -> new DataValue(
					new Variant(new DateTime(source.getRsiValues().get(variableName).getTimestamp()))));
		} catch (UaException e) {
			log.info("Exception during creation of NodeInstance {}: {}", source.getConfig().getId(), e);
		}

	}
}