This is an automated email from the ASF dual-hosted git repository.
hutcheb pushed a commit to branch plc4j/opcua-browse
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/plc4j/opcua-browse by this
push:
new 018c27ed8 chore(plc4x): Browse now browses each node. Still need to
sort out how to create the BrowseItem Tree.
018c27ed8 is described below
commit 018c27ed8bef9971cf774c5e6b380e4ca0d2f912
Author: Ben Hutcheson <[email protected]>
AuthorDate: Thu Sep 15 14:19:17 2022 -0600
chore(plc4x): Browse now browses each node. Still need to sort out how to
create the BrowseItem Tree.
---
.../java/opcua/protocol/OpcuaProtocolLogic.java | 142 +++++++++++++--------
protocols/opcua/src/main/xslt/opc-common.xsl | 19 +--
2 files changed, 96 insertions(+), 65 deletions(-)
diff --git
a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
index 5b3c75358..62924d546 100644
---
a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
+++
b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/protocol/OpcuaProtocolLogic.java
@@ -39,6 +39,7 @@ import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration;
import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionField;
import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
+import org.apache.plc4x.java.spi.values.PlcINT;
import org.apache.plc4x.java.spi.values.PlcList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -83,6 +84,9 @@ public class OpcuaProtocolLogic extends
Plc4xProtocolBase<OpcuaAPU> implements H
private Map<Long, OpcuaSubscriptionHandle> subscriptions = new HashMap<>();
private SecureChannel channel;
private AtomicBoolean securedConnection = new AtomicBoolean(false);
+ private LinkedHashMap<String, ReferenceDescription> discoveredNodes= new
LinkedHashMap<>();
+ private LinkedHashMap<String, ReferenceDescription> browsedNodes= new
LinkedHashMap<>();
+ private LinkedHashMap<String, String> nodeChildren= new LinkedHashMap<>();
@Override
public void setConfiguration(OpcuaConfiguration configuration) {
@@ -128,8 +132,8 @@ public class OpcuaProtocolLogic extends
Plc4xProtocolBase<OpcuaAPU> implements H
channel.onDiscover(context);
}
- private CompletableFuture<List<PlcBrowseItem>> browseNode(NodeId nodeId) {
- CompletableFuture<List<PlcBrowseItem>> future = new
CompletableFuture<>();
+ private CompletableFuture<PlcBrowseItem> browseNode(ExpandedNodeId nodeId)
{
+ CompletableFuture<PlcBrowseItem> future = new CompletableFuture<>();
RequestHeader requestHeader = new
RequestHeader(channel.getAuthenticationToken(),
SecureChannel.getCurrentDateTime(),
channel.getRequestHandle(),
@@ -140,7 +144,7 @@ public class OpcuaProtocolLogic extends
Plc4xProtocolBase<OpcuaAPU> implements H
List<ExtensionObjectDefinition> requestedValues = new ArrayList<>(1);
requestedValues.add(new BrowseDescription(
- nodeId,
+ new NodeId(nodeId.getNodeId()),
BrowseDirection.browseDirectionForward,
new NodeId(new NodeIdTwoByte((short) 33)),
true,
@@ -176,13 +180,15 @@ public class OpcuaProtocolLogic extends
Plc4xProtocolBase<OpcuaAPU> implements H
Consumer<byte[]> consumer = opcuaResponse -> {
try {
ExtensionObjectDefinition reply =
ExtensionObject.staticParse(new ReadBufferByteBased(opcuaResponse,
ByteOrder.LITTLE_ENDIAN), false).getBody();
+ PlcBrowseItem value = null;
if (reply instanceof BrowseResponse) {
BrowseResponse response = (BrowseResponse) reply;
BrowseResult castResult = (BrowseResult)
response.getResults().get(0);
- List<PlcBrowseItem> values = new
ArrayList<>(response.getResults().size());
+ ArrayList<PlcBrowseItem> children = new ArrayList<>(0);
for (ExtensionObjectDefinition result :
castResult.getReferences()) {
ReferenceDescription referenceResult =
(ReferenceDescription) result;
+ referenceResult.getNodeId().getIdentifier();
String typeDefinition =
referenceResult.getTypeDefinition().getIdentifier();
PlcValueType plcValue = null;
if (OpcuaDataType.isDefined(typeDefinition)) {
@@ -191,33 +197,36 @@ public class OpcuaProtocolLogic extends
Plc4xProtocolBase<OpcuaAPU> implements H
} else {
plcValue = PlcValueType.Struct;
}
- ExpandedNodeId tempNodeId =
((ReferenceDescription) result).getNodeId();
- NodeId tempNode = new NodeId(
- new NodeIdString(tempNodeId
tempNodeId.getIdentifier());
- )
- CompletableFuture<List<PlcBrowseItem>> childFuture
= browseNode();
- List<PlcBrowseItem> list = childFuture.get(5000L,
TimeUnit.SECONDS);
- if (list != null) {
- values.add(new DefaultPlcBrowseItem(
-
referenceResult.getBrowseName().getName().getStringValue(),
-
referenceResult.getDisplayName().getText().getStringValue(),
- plcValue,
- true,
- true,
- true, list, null));
- } else {
- values.add(new DefaultPlcBrowseItem(
-
referenceResult.getBrowseName().getName().getStringValue(),
-
referenceResult.getDisplayName().getText().getStringValue(),
- plcValue,
- true,
- true,
- true, new ArrayList<PlcBrowseItem>(0),
null));
+ String test =
referenceResult.getNodeId().getIdentifier();
+ if (referenceResult.getNodeClass() ==
NodeClass.nodeClassObject || referenceResult.getNodeClass() ==
NodeClass.nodeClassVariable) {
+ nodeChildren.put(nodeId.getIdentifier(),
referenceResult.getNodeId().getIdentifier());
+
discoveredNodes.put(referenceResult.getNodeId().getIdentifier(),
referenceResult);
}
}
- future.complete(values);
+ value = new DefaultPlcBrowseItem(
+ nodeId.getIdentifier(),
+ nodeId.getIdentifier(),
+ PlcValueType.INT,
+ true,
+ true,
+ true,
+ new ArrayList<>(0),
+ new HashMap<>()
+ );
+
+ future.complete(value);
} else {
- List<PlcBrowseItem> values = new ArrayList<>(0);
+ value = new DefaultPlcBrowseItem(
+ nodeId.getIdentifier(),
+ nodeId.getNamespaceURI().getStringValue(),
+ PlcValueType.INT,
+ false,
+ false,
+ false,
+ new ArrayList<>(0),
+ new HashMap<>()
+ );
+
if (reply instanceof ServiceFault) {
ExtensionObjectDefinition header = ((ServiceFault)
reply).getResponseHeader();
LOGGER.error("Browse request ended up with
ServiceFault: {}", header);
@@ -225,17 +234,10 @@ public class OpcuaProtocolLogic extends
Plc4xProtocolBase<OpcuaAPU> implements H
LOGGER.error("Remote party returned an error
'{}'", reply);
}
- future.complete(values);
- return;
+ future.complete(value);
}
} catch (ParseException e) {
future.completeExceptionally(new PlcRuntimeException(e));
- } catch (ExecutionException e) {
- throw new RuntimeException(e);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } catch (TimeoutException e) {
- throw new RuntimeException(e);
}
};
@@ -262,25 +264,61 @@ public class OpcuaProtocolLogic extends
Plc4xProtocolBase<OpcuaAPU> implements H
@Override
public CompletableFuture<PlcBrowseResponse> browse(PlcBrowseRequest
browseRequest) {
CompletableFuture<PlcBrowseResponse> future = new
CompletableFuture<>();
+ boolean browsable = true;
+ PlcBrowseResponse response = null;
- CompletableFuture<List<PlcBrowseItem>> childFuture = browseNode(new
ExpandedNodeId(
- false,
- false,
- new NodeIdTwoByte((short) 85),
- null,
- 0L));
+ ReferenceDescription referenceDescription = new ReferenceDescription(
+ new NodeId(
+ new NodeIdTwoByte((short) 47)
+ ),
+ true,
+ new ExpandedNodeId(
+ false,
+ false,
+ new NodeIdTwoByte((short) 85),
+ null,
+ 0L),
+ new QualifiedName(
+ 0,
+ new PascalString("root")
+ ),
+ new LocalizedText(
+ true,
+ true,
+ new PascalString("en"),
+ new PascalString("root")),
+ NodeClass.nodeClassObject,
+ new ExpandedNodeId(
+ false,
+ false,
+ new NodeIdTwoByte((short) 2034),
+ null,
+ 0L)
+ );
- PlcBrowseResponse response = null;
- try {
- response = new DefaultPlcBrowseResponse(browseRequest,
PlcResponseCode.OK, childFuture.get(1000L, TimeUnit.SECONDS));
- future.complete(response);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- } catch (ExecutionException e) {
- throw new RuntimeException(e);
- } catch (TimeoutException e) {
- throw new RuntimeException(e);
+ discoveredNodes.put(referenceDescription.getNodeId().getIdentifier(),
referenceDescription);
+
+ while (browsable) {
+ CompletableFuture<PlcBrowseItem> childFuture =
browseNode(referenceDescription.getNodeId());
+
+ try {
+ PlcBrowseItem responseItem = childFuture.get(10L,
TimeUnit.SECONDS);
+
browsedNodes.put(referenceDescription.getNodeId().getIdentifier(),
referenceDescription);
+
discoveredNodes.remove(referenceDescription.getNodeId().getIdentifier());
+ Optional<String> first =
discoveredNodes.keySet().stream().findFirst();
+ if (first.isPresent()) {
+ referenceDescription = discoveredNodes.get(first.get());
+ }
+ } catch (TimeoutException | InterruptedException |
ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ if (discoveredNodes.isEmpty()) {
+ browsable = false;
+ }
}
+
+ response = new DefaultPlcBrowseResponse(browseRequest,
PlcResponseCode.OK, new ArrayList<PlcBrowseItem>());
+ future.complete(response);
//new DefaultPlcBrowseResponse(browseRequest,
PlcResponseCode.INTERNAL_ERROR, values)
return future;
}
diff --git a/protocols/opcua/src/main/xslt/opc-common.xsl
b/protocols/opcua/src/main/xslt/opc-common.xsl
index 630feca92..73ca19b34 100644
--- a/protocols/opcua/src/main/xslt/opc-common.xsl
+++ b/protocols/opcua/src/main/xslt/opc-common.xsl
@@ -354,16 +354,15 @@
<xsl:function name="plc4x:getDataTypeLength" as="xs:integer">
<xsl:param name="lengthMap" as="map(xs:string, xs:int)"/>
<xsl:param name="datatype"/>
- <xsl:message>[DEBUG] Getting length of <xsl:value-of
select="xs:string($datatype[@TypeName])"/></xsl:message>
<xsl:choose>
- <xsl:when test="map:contains($lengthMap,
xs:string($datatype[@TypeName]))">
- <xsl:message>[DEBUG] Bit Length <xsl:value-of
select="$lengthMap(xs:string($datatype[@TypeName]))"/></xsl:message>
- <xsl:value-of select="map:get($lengthMap,
xs:string($datatype[@TypeName]))"/>
+ <xsl:when test="map:contains($lengthMap,
xs:string($datatype/[@TypeName]))">
+ <xsl:message>[DEBUG] Bit Length <xsl:value-of
select="$lengthMap(xs:string($datatype/[@TypeName]))"/></xsl:message>
+ <xsl:value-of select="map:get($lengthMap,
xs:string($datatype/[@TypeName]))"/>
</xsl:when>
- <xsl:when test="($datatype[@TypeName] = 'opc:Bit') or
($datatype[@TypeName] = 'opc:Boolean')">
+ <xsl:when test="($datatype/[@TypeName] = 'opc:Bit') or
($datatype/[@TypeName] = 'opc:Boolean')">
<xsl:choose>
- <xsl:when test="$datatype[@Length] != ''">
- <xsl:value-of select="xs:int($datatype[@Length])"/>
+ <xsl:when test="$datatype/[@Length] != ''">
+ <xsl:value-of select="xs:int($datatype/[@Length])"/>
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
@@ -379,7 +378,6 @@
<xsl:param name="currentNodePosition" as="xs:int"/>
<xsl:param name="currentBitPosition" as="xs:int"/>
<xsl:param name="currentBytePosition" as="xs:int"/>
- <xsl:message>[DEBUG] Recursively rearranging bit order in nodes,
Position - <xsl:value-of select="$currentNodePosition"/>, Bit Position -
<xsl:value-of select="$currentBitPosition"/>, Byte Position - <xsl:value-of
select="$currentBytePosition"/></xsl:message>
<xsl:for-each select="$baseNode/opc:Field">
<xsl:message>[DEBUG] <xsl:value-of select="position()"/> -
<xsl:value-of select="@TypeName"/></xsl:message>
</xsl:for-each>
@@ -390,7 +388,6 @@
<xsl:choose>
<xsl:when test="$currentBitPosition != 0">
<!-- Add a reserved field if we are halfway through a
Byte. -->
- <xsl:message>[DEBUG] Adding a reserved
field</xsl:message>
<xsl:call-template name="plc4x:parseFields">
<xsl:with-param name="baseNode">
<xsl:copy-of
select="$baseNode/opc:Field[position() lt ($currentNodePosition -
$currentBytePosition)]"/>
@@ -427,7 +424,6 @@
<xsl:choose>
<xsl:when test="$currentBitPosition=0">
<!-- Put node into current position -->
- <xsl:message>[DEBUG] First Bit in
Byte</xsl:message>
<xsl:call-template name="plc4x:parseFields">
<xsl:with-param name="baseNode">
<xsl:copy-of
select="$baseNode/opc:Field"/>
@@ -446,7 +442,6 @@
</xsl:when>
<xsl:otherwise>
<!-- Put node into correct position based on
bit and byte position -->
- <xsl:message>[DEBUG] Additional Bit in
Byte</xsl:message>
<xsl:call-template name="plc4x:parseFields">
<xsl:with-param name="baseNode">
<xsl:copy-of
select="$baseNode/opc:Field[position() lt ($currentNodePosition -
$currentBytePosition)]"/>
@@ -472,7 +467,6 @@
<xsl:choose>
<xsl:when test="$currentBitPosition != 0 and
$currentBitPosition lt 8">
<!-- Add a reserved field if we are halfway
through a Byte. -->
- <xsl:message>[DEBUG] Adding a reserved
field</xsl:message>
<xsl:call-template name="plc4x:parseFields">
<xsl:with-param name="baseNode">
<xsl:copy-of
select="$baseNode/opc:Field[position() lt ($currentNodePosition -
$currentBytePosition)]"/>
@@ -497,7 +491,6 @@
</xsl:when>
<xsl:otherwise>
<!-- Put node into current position -->
- <xsl:message>[DEBUG] not a bit data type, just
leave it in it's place</xsl:message>
<xsl:call-template name="plc4x:parseFields">
<xsl:with-param name="baseNode">
<xsl:copy-of
select="$baseNode/opc:Field"/>