blob: 9c8890723832db3c919e2c298caffa341b87ea6e [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2021 the Eclipse BaSyx Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
******************************************************************************/
package org.eclipse.basyx.vab.protocol.opcua.connector;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.basyx.vab.exception.provider.ProviderException;
import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
import org.eclipse.basyx.vab.protocol.opcua.server.BaSyxOpcUaClientRunner;
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.NodeId;
import org.eclipse.milo.opcua.stack.core.types.builtin.QualifiedName;
import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
import org.eclipse.milo.opcua.stack.core.types.structured.BrowsePath;
import org.eclipse.milo.opcua.stack.core.types.structured.CallResponse;
import org.eclipse.milo.opcua.stack.core.types.structured.RelativePath;
import org.eclipse.milo.opcua.stack.core.types.structured.RelativePathElement;
import org.eclipse.milo.opcua.stack.core.types.structured.TranslateBrowsePathsToNodeIdsResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* OPC UA connector class
*
* @author kdorofeev
*
*/
public class OpcUaConnector implements IModelProvider {
private Logger logger = LoggerFactory.getLogger(OpcUaConnector.class);
private String address;
private BaSyxOpcUaClientRunner clientRunner;
/**
* Invoke a BaSys get operation via OPC UA
*
* @param servicePath
* requested node path
* @return the requested value
* @throws Exception
*/
@Override
public String getModelPropertyValue(String servicePath) {
try {
clientRunner = new BaSyxOpcUaClientRunner(address);
clientRunner.run();
} catch (Exception e) {
logger.error("Exception in getModelPropertyValue", e);
}
return opcUaRead(translateBrowsePathToNodeId(servicePath)[1]);
}
@Override
public void setModelPropertyValue(String servicePath, Object newValue) throws ProviderException {
try {
clientRunner = new BaSyxOpcUaClientRunner(address);
clientRunner.run();
} catch (Exception e) {
// TODO Auto-generated catch block
logger.error("Exception in setModelPropertyValue", e);
}
opcUaWrite(translateBrowsePathToNodeId(servicePath)[1], newValue);
}
@Override
public void createValue(String path, Object newEntity) throws ProviderException {
}
@Override
public void deleteValue(String path) throws ProviderException {
}
@Override
public void deleteValue(String path, Object obj) throws ProviderException {
}
@Override
public Object invokeOperation(String servicePath, Object... parameters) throws ProviderException {
try {
clientRunner = new BaSyxOpcUaClientRunner(address);
clientRunner.run();
} catch (Exception e) {
logger.error("Exception in invokeOperation", e);
}
return opcUaMethodCall(translateBrowsePathToNodeId(servicePath), parameters);
}
public OpcUaConnector(String address) {
this.address = address;
}
/**
* Perform a OPC UA read request
*
* @param servicePath
* @return
*/
private String opcUaRead(NodeId servicePath) {
try {
List<NodeId> nodes = new ArrayList<NodeId>();
nodes.add(servicePath);
CompletableFuture<List<DataValue>> result = clientRunner.read(nodes);
return result.get().get(0).getValue().getValue().toString();
} catch (Exception e) {
logger.error("Exception in opcUaRead", e);
}
return null;
}
private String opcUaMethodCall(NodeId[] methodNodes, Object[] inputParameters) {
try {
Variant[] inputs = new Variant[inputParameters.length];
for (int i = 0; i < inputParameters.length; i++) {
inputs[i] = new Variant(inputParameters[i]);
}
CompletableFuture<CallResponse> result = clientRunner.callMethod(methodNodes[0], methodNodes[1], inputs);
Variant[] outputs = result.get().getResults()[0].getOutputArguments();
String ret = "";
for (Variant var : outputs) {
ret += var.getValue() + " ";
}
return ret;
} catch (Exception e) {
logger.error("Exception in opcUaMethodCall", e);
}
return null;
}
private String opcUaWrite(NodeId servicePath, Object parameter) throws ProviderException {
try {
List<NodeId> nodes = new ArrayList<NodeId>();
nodes.add(servicePath);
List<DataValue> parameters = new ArrayList<DataValue>();
parameters.add(new DataValue(new Variant(parameter), null, null, null));
CompletableFuture<List<StatusCode>> result = clientRunner.write(nodes, parameters);
return result.get().get(0).toString();
} catch (Exception e) {
logger.error("Exception in opcUaWrite", e);
}
return null;
}
private NodeId[] translateBrowsePathToNodeId(String path) {
String[] nodes = path.split("/");
List<RelativePathElement> rpe_list = new ArrayList<RelativePathElement>();
for (String node : nodes) {
if (node.split(":").length != 2) {
logger.warn("OpcUaName should be in form namespaceIdx:identifier");
}
int nsIdx = Integer.valueOf(node.split(":")[0]);
String name = node.split(":")[1];
rpe_list.add(new RelativePathElement(Identifiers.HierarchicalReferences, false, true,
new QualifiedName(nsIdx, name)));
}
RelativePathElement[] rpe_node_arr = new RelativePathElement[rpe_list.size()];
RelativePathElement[] rpe_parent_arr = new RelativePathElement[rpe_list.size() - 1];
rpe_node_arr = rpe_list.toArray(rpe_node_arr);
// get list for parent (all but the last one)
rpe_list.remove(rpe_list.size() - 1);
rpe_parent_arr = rpe_list.toArray(rpe_parent_arr);
BrowsePath bp_node = new BrowsePath(Identifiers.RootFolder, new RelativePath(rpe_node_arr));
BrowsePath bp_parent = new BrowsePath(Identifiers.RootFolder, new RelativePath(rpe_parent_arr));
List<BrowsePath> bp_node_list = new ArrayList<BrowsePath>();
List<BrowsePath> bp_parent_list = new ArrayList<BrowsePath>();
bp_node_list.add(bp_node);
bp_parent_list.add(bp_parent);
try {
CompletableFuture<TranslateBrowsePathsToNodeIdsResponse> result_node = clientRunner.translate(bp_node_list);
CompletableFuture<TranslateBrowsePathsToNodeIdsResponse> result_parent = clientRunner
.translate(bp_parent_list);
if (result_node.get().getResults().length == 0) {
logger.warn("TranslateBrowsePathsToNodeIdsResponse result size = 0, checkthe browse path!");
return null;
}
if (result_node.get().getResults().length > 1) {
logger.warn("TranslateBrowsePathsToNodeIdsResponse result size > 1, the method returns only the first one!");
}
if (result_node.get().getResults()[0].getTargets().length > 1) {
logger.warn("TranslateBrowsePathsToNodeIdsResponse targets size > 1, the method returns only the first one!");
}
if (result_node.get().getResults()[0].getTargets().length == 0) {
logger.warn("TranslateBrowsePathsToNodeIdsResponse targets size = 0, check the browse path!");
logger.trace(result_node.get().getResults()[0].getStatusCode().toString());
return null;
}
Object nodeIdentifier = result_node.get().getResults()[0].getTargets()[0].getTargetId().getIdentifier();
Object parentIdentifier = result_parent.get().getResults()[0].getTargets()[0].getTargetId().getIdentifier();
UShort nodeNsIdx = result_node.get().getResults()[0].getTargets()[0].getTargetId().getNamespaceIndex();
UShort parentNsIdx = result_parent.get().getResults()[0].getTargets()[0].getTargetId().getNamespaceIndex();
if (nodeIdentifier instanceof String && parentIdentifier instanceof String) {
return new NodeId[] { new NodeId(parentNsIdx, (String) parentIdentifier),
new NodeId(nodeNsIdx, (String) nodeIdentifier) };
}
if (nodeIdentifier instanceof UInteger && parentIdentifier instanceof UInteger) {
return new NodeId[] { new NodeId(parentNsIdx, (UInteger) parentIdentifier),
new NodeId(nodeNsIdx, (UInteger) nodeIdentifier) };
}
if (nodeIdentifier instanceof UInteger && parentIdentifier instanceof String) {
return new NodeId[] { new NodeId(parentNsIdx, (String) parentIdentifier),
new NodeId(nodeNsIdx, (UInteger) nodeIdentifier) };
}
if (nodeIdentifier instanceof String && parentIdentifier instanceof UInteger) {
return new NodeId[] { new NodeId(parentNsIdx, (UInteger) parentIdentifier),
new NodeId(nodeNsIdx, (String) nodeIdentifier) };
} else {
logger.error("NodeId identifier is not neither String, nor int");
return null;
}
} catch (Exception e) {
logger.error("Exception in translateBrowsePathToNodeId", e);
}
return null;
}
}