You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2022/08/31 14:40:47 UTC
[plc4x] 03/03: fix(plc4j/ads): Refactored the ADS driver to support reading of complex types.
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit 9f3cc6d0d4f5686e51db1780bf2df12b0ce351c5
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Wed Aug 31 16:40:22 2022 +0200
fix(plc4j/ads): Refactored the ADS driver to support reading of complex types.
---
.../org/apache/plc4x/java/ads/field/AdsField.java | 3 -
.../plc4x/java/ads/field/AdsFieldHandler.java | 2 -
.../plc4x/java/ads/field/DirectAdsField.java | 25 +-
.../plc4x/java/ads/field/DirectAdsStringField.java | 16 +-
.../plc4x/java/ads/field/SymbolicAdsField.java | 38 +--
.../java/ads/field/SymbolicAdsStringField.java | 95 --------
.../java/ads/model/AdsSubscriptionHandle.java | 8 +-
.../plc4x/java/ads/protocol/AdsProtocolLogic.java | 261 ++++++++++++++++-----
.../java/ads/readwrite/utils/StaticHelper.java | 4 +
9 files changed, 223 insertions(+), 229 deletions(-)
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsField.java
index 8d5b1b274..f403d3983 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsField.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsField.java
@@ -18,12 +18,9 @@
*/
package org.apache.plc4x.java.ads.field;
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
import org.apache.plc4x.java.api.model.PlcField;
import org.apache.plc4x.java.spi.utils.Serializable;
public interface AdsField extends PlcField, Serializable {
- AdsDataType getAdsDataType();
-
}
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsFieldHandler.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsFieldHandler.java
index c94c1c3e8..447755620 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsFieldHandler.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsFieldHandler.java
@@ -30,8 +30,6 @@ public class AdsFieldHandler implements PlcFieldHandler {
return DirectAdsStringField.of(fieldQuery);
} else if (DirectAdsField.matches(fieldQuery)) {
return DirectAdsField.of(fieldQuery);
- } else if (SymbolicAdsStringField.matches(fieldQuery)) {
- return SymbolicAdsStringField.of(fieldQuery);
} else if (SymbolicAdsField.matches(fieldQuery)) {
return SymbolicAdsField.of(fieldQuery);
}
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsField.java
index 7abef7f87..cb6f79793 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsField.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsField.java
@@ -18,9 +18,7 @@
*/
package org.apache.plc4x.java.ads.field;
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
-import org.apache.plc4x.java.spi.generation.ParseException;
import org.apache.plc4x.java.spi.generation.SerializationException;
import org.apache.plc4x.java.spi.generation.WriteBuffer;
@@ -41,24 +39,24 @@ public class DirectAdsField implements AdsField {
private final long indexOffset;
- private final AdsDataType adsDataType;
+ private final String adsDataTypeName;
private final int numberOfElements;
- public DirectAdsField(long indexGroup, long indexOffset, AdsDataType adsDataType, Integer numberOfElements) {
+ public DirectAdsField(long indexGroup, long indexOffset, String adsDataTypeName, Integer numberOfElements) {
//ByteValue.checkUnsignedBounds(indexGroup, 4);
this.indexGroup = indexGroup;
//ByteValue.checkUnsignedBounds(indexOffset, 4);
this.indexOffset = indexOffset;
- this.adsDataType = Objects.requireNonNull(adsDataType);
+ this.adsDataTypeName = Objects.requireNonNull(adsDataTypeName);
this.numberOfElements = numberOfElements != null ? numberOfElements : 1;
if (this.numberOfElements <= 0) {
throw new IllegalArgumentException("numberOfElements must be greater then zero. Was " + this.numberOfElements);
}
}
- public static DirectAdsField of(long indexGroup, long indexOffset, AdsDataType adsDataType, Integer numberOfElements) {
- return new DirectAdsField(indexGroup, indexOffset, adsDataType, numberOfElements);
+ public static DirectAdsField of(long indexGroup, long indexOffset, String adsDataTypeName, Integer numberOfElements) {
+ return new DirectAdsField(indexGroup, indexOffset, adsDataTypeName, numberOfElements);
}
public static DirectAdsField of(String address) {
@@ -88,12 +86,11 @@ public class DirectAdsField implements AdsField {
}
String adsDataTypeString = matcher.group("adsDataType");
- AdsDataType adsDataType = AdsDataType.valueOf(adsDataTypeString);
String numberOfElementsString = matcher.group("numberOfElements");
Integer numberOfElements = numberOfElementsString != null ? Integer.valueOf(numberOfElementsString) : null;
- return new DirectAdsField(indexGroup, indexOffset, adsDataType, numberOfElements);
+ return new DirectAdsField(indexGroup, indexOffset, adsDataTypeString, numberOfElements);
}
public static boolean matches(String address) {
@@ -108,14 +105,8 @@ public class DirectAdsField implements AdsField {
return indexOffset;
}
- @Override
- public AdsDataType getAdsDataType() {
- return adsDataType;
- }
-
- @Override
- public String getPlcDataType() {
- return adsDataType.toString();
+ public String getAdsDataTypeName() {
+ return adsDataTypeName;
}
@Override
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsStringField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsStringField.java
index d003c4612..04d4109cc 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsStringField.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsStringField.java
@@ -18,7 +18,6 @@
*/
package org.apache.plc4x.java.ads.field;
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
import org.apache.plc4x.java.spi.generation.SerializationException;
import org.apache.plc4x.java.spi.generation.WriteBuffer;
@@ -37,13 +36,13 @@ public class DirectAdsStringField extends DirectAdsField implements AdsStringFie
private final int stringLength;
- public DirectAdsStringField(long indexGroup, long indexOffset, AdsDataType adsDataType, int stringLength, Integer numberOfElements) {
- super(indexGroup, indexOffset, adsDataType, numberOfElements);
+ public DirectAdsStringField(long indexGroup, long indexOffset, String adsDataTypeName, int stringLength, Integer numberOfElements) {
+ super(indexGroup, indexOffset, adsDataTypeName, numberOfElements);
this.stringLength = stringLength;
}
- public static DirectAdsStringField of(long indexGroup, long indexOffset, AdsDataType adsDataType, int stringLength, Integer numberOfElements) {
- return new DirectAdsStringField(indexGroup, indexOffset, adsDataType, stringLength, numberOfElements);
+ public static DirectAdsStringField of(long indexGroup, long indexOffset, String adsDataTypeName, int stringLength, Integer numberOfElements) {
+ return new DirectAdsStringField(indexGroup, indexOffset, adsDataTypeName, stringLength, numberOfElements);
}
public static DirectAdsStringField of(String address) {
@@ -72,16 +71,15 @@ public class DirectAdsStringField extends DirectAdsField implements AdsStringFie
indexOffset = Long.parseLong(indexOffsetString);
}
- String adsDataTypeString = matcher.group("adsDataType");
- AdsDataType adsDataType = AdsDataType.valueOf(adsDataTypeString);
+ String adsDataTypeName = matcher.group("adsDataType");
String stringLengthString = matcher.group("stringLength");
- Integer stringLength = stringLengthString != null ? Integer.valueOf(stringLengthString) : null;
+ int stringLength = stringLengthString != null ? Integer.parseInt(stringLengthString) : 0;
String numberOfElementsString = matcher.group("numberOfElements");
Integer numberOfElements = numberOfElementsString != null ? Integer.valueOf(numberOfElementsString) : null;
- return new DirectAdsStringField(indexGroup, indexOffset, adsDataType, stringLength, numberOfElements);
+ return new DirectAdsStringField(indexGroup, indexOffset, adsDataTypeName, stringLength, numberOfElements);
}
public static boolean matches(String address) {
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsField.java
index 4d13dd92f..140b1fb60 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsField.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsField.java
@@ -18,7 +18,6 @@
*/
package org.apache.plc4x.java.ads.field;
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
import org.apache.plc4x.java.spi.generation.SerializationException;
import org.apache.plc4x.java.spi.generation.WriteBuffer;
@@ -33,22 +32,12 @@ import java.util.regex.Pattern;
*/
public class SymbolicAdsField implements AdsField {
- private static final Pattern SYMBOLIC_ADDRESS_PATTERN = Pattern.compile("^(?<symbolicAddress>.+):(?<adsDataType>\\w+)(\\[(?<numberOfElements>\\d+)])?");
+ private static final Pattern SYMBOLIC_ADDRESS_PATTERN = Pattern.compile("^(?<symbolicAddress>.+)");
private final String symbolicAddress;
- private final AdsDataType adsDataType;
-
- private final int numberOfElements;
-
- public SymbolicAdsField(String symbolicAddress, AdsDataType adsDataType, Integer numberOfElements) {
+ public SymbolicAdsField(String symbolicAddress) {
this.symbolicAddress = Objects.requireNonNull(symbolicAddress);
- this.adsDataType = Objects.requireNonNull(adsDataType);
- this.numberOfElements = numberOfElements != null ? numberOfElements : 1;
- if (this.numberOfElements <= 0) {
- throw new IllegalArgumentException("numberOfElements must be greater then zero. Was " + this.numberOfElements);
- }
-
}
public static SymbolicAdsField of(String address) {
@@ -58,13 +47,7 @@ public class SymbolicAdsField implements AdsField {
}
String symbolicAddress = matcher.group("symbolicAddress");
- String adsDataTypeString = matcher.group("adsDataType");
- AdsDataType adsDataType = AdsDataType.valueOf(adsDataTypeString);
-
- String numberOfElementsString = matcher.group("numberOfElements");
- Integer numberOfElements = numberOfElementsString != null ? Integer.valueOf(numberOfElementsString) : null;
-
- return new SymbolicAdsField(symbolicAddress, adsDataType, numberOfElements);
+ return new SymbolicAdsField(symbolicAddress);
}
public static boolean matches(String address) {
@@ -75,21 +58,6 @@ public class SymbolicAdsField implements AdsField {
return symbolicAddress;
}
- @Override
- public AdsDataType getAdsDataType() {
- return adsDataType;
- }
-
- @Override
- public String getPlcDataType() {
- return adsDataType.toString();
- }
-
- @Override
- public int getNumberOfElements() {
- return numberOfElements;
- }
-
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsStringField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsStringField.java
deleted file mode 100644
index ef064afb5..000000000
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsStringField.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.plc4x.java.ads.field;
-
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
-import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
-import org.apache.plc4x.java.spi.generation.SerializationException;
-import org.apache.plc4x.java.spi.generation.WriteBuffer;
-
-import java.nio.charset.StandardCharsets;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * ADS address witch is defined by symbolic name (e.g. {@code Main.items[0]}).
- */
-public class SymbolicAdsStringField extends SymbolicAdsField implements AdsStringField {
-
- private static final Pattern SYMBOLIC_ADDRESS_STRING_PATTERN = Pattern.compile("^(?<symbolicAddress>.+):(?<adsDataType>'STRING'|'WSTRING')\\((?<stringLength>\\d{1,3})\\)(\\[(?<numberOfElements>\\d+)])?");
-
- private final int stringLength;
-
- private SymbolicAdsStringField(String symbolicAddress, AdsDataType adsDataType, int stringLength, Integer numberOfElements) {
- super(symbolicAddress, adsDataType, numberOfElements);
- this.stringLength = stringLength;
- }
-
- public static SymbolicAdsStringField of(String address) {
- Matcher matcher = SYMBOLIC_ADDRESS_STRING_PATTERN.matcher(address);
- if (!matcher.matches()) {
- throw new PlcInvalidFieldException(address, SYMBOLIC_ADDRESS_STRING_PATTERN, "{address}");
- }
- String symbolicAddress = matcher.group("symbolicAddress");
-
- String adsDataTypeString = matcher.group("adsDataType");
- AdsDataType adsDataType = AdsDataType.valueOf(adsDataTypeString);
-
- String stringLengthString = matcher.group("stringLength");
- Integer stringLength = stringLengthString != null ? Integer.valueOf(stringLengthString) : null;
-
- String numberOfElementsString = matcher.group("numberOfElements");
- Integer numberOfElements = numberOfElementsString != null ? Integer.valueOf(numberOfElementsString) : null;
-
- return new SymbolicAdsStringField(symbolicAddress, adsDataType, stringLength, numberOfElements);
- }
-
- public static boolean matches(String address) {
- return SYMBOLIC_ADDRESS_STRING_PATTERN.matcher(address).matches();
- }
-
- @Override
- public int getStringLength() {
- return stringLength;
- }
-
- @Override
- public String toString() {
- return "SymbolicAdsStringField{" +
- "symbolicAddress='" + getSymbolicAddress() + '\'' +
- ", stringLength=" + stringLength +
- '}';
- }
-
- @Override
- public void serialize(WriteBuffer writeBuffer) throws SerializationException {
- writeBuffer.pushContext(getClass().getSimpleName());
-
- String symbolicAddress = getSymbolicAddress();
- writeBuffer.writeString("symbolicAddress", symbolicAddress.getBytes(StandardCharsets.UTF_8).length * 8, StandardCharsets.UTF_8.name(), symbolicAddress);
-
- writeBuffer.writeUnsignedLong("numberOfElements", 32, getNumberOfElements());
-
- String dataType = getPlcDataType();
- writeBuffer.writeString("dataType", dataType.getBytes(StandardCharsets.UTF_8).length * 8, StandardCharsets.UTF_8.name(), dataType);
-
- writeBuffer.writeInt("stringLength", 32, getStringLength());
- writeBuffer.popContext(getClass().getSimpleName());
- }
-}
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsSubscriptionHandle.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsSubscriptionHandle.java
index a1f89670d..b1dc8ff72 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsSubscriptionHandle.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/model/AdsSubscriptionHandle.java
@@ -18,7 +18,7 @@
*/
package org.apache.plc4x.java.ads.model;
-import org.apache.plc4x.java.ads.readwrite.AdsDataType;
+import org.apache.plc4x.java.ads.readwrite.AdsDataTypeTableEntry;
import org.apache.plc4x.java.spi.messages.PlcSubscriber;
import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle;
@@ -28,11 +28,11 @@ public class AdsSubscriptionHandle extends DefaultPlcSubscriptionHandle {
private final String plcFieldName;
- private final AdsDataType adsDataType;
+ private final AdsDataTypeTableEntry adsDataType;
private final Long notificationHandle;
- public AdsSubscriptionHandle(PlcSubscriber plcSubscriber, String plcFieldName, AdsDataType adsDataType, Long notificationHandle) {
+ public AdsSubscriptionHandle(PlcSubscriber plcSubscriber, String plcFieldName, AdsDataTypeTableEntry adsDataType, Long notificationHandle) {
super(plcSubscriber);
this.plcFieldName = plcFieldName;
this.adsDataType = adsDataType;
@@ -43,7 +43,7 @@ public class AdsSubscriptionHandle extends DefaultPlcSubscriptionHandle {
return plcFieldName;
}
- public AdsDataType getAdsDataType() {
+ public AdsDataTypeTableEntry getAdsDataType() {
return adsDataType;
}
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
index 3f2c09a9c..abe27b7f0 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
@@ -46,6 +46,8 @@ import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration;
import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionField;
import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;
import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
+import org.apache.plc4x.java.spi.values.PlcList;
+import org.apache.plc4x.java.spi.values.PlcStruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -72,14 +74,14 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
private final Map<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> consumers = new ConcurrentHashMap<>();
- private final ConcurrentHashMap<SymbolicAdsField, DirectAdsField> symbolicFieldMapping;
+// private final ConcurrentHashMap<SymbolicAdsField, DirectAdsField> symbolicFieldMapping;
private final ConcurrentHashMap<SymbolicAdsField, CompletableFuture<Void>> pendingResolutionRequests;
private final Map<String, AdsSymbolTableEntry> symbolTable;
private final Map<String, AdsDataTypeTableEntry> dataTypeTable;
public AdsProtocolLogic() {
- symbolicFieldMapping = new ConcurrentHashMap<>();
+// symbolicFieldMapping = new ConcurrentHashMap<>();
pendingResolutionRequests = new ConcurrentHashMap<>();
symbolTable = new HashMap<>();
dataTypeTable = new HashMap<>();
@@ -410,18 +412,10 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
protected CompletableFuture<PlcReadResponse> singleRead(PlcReadRequest readRequest, DirectAdsField directAdsField) {
CompletableFuture<PlcReadResponse> future = new CompletableFuture<>();
- long size;
- if (directAdsField.getAdsDataType() == AdsDataType.STRING) {
- // If an explicit size is given with the string, use this, if not use 256
- size = (directAdsField instanceof AdsStringField) ?
- ((AdsStringField) directAdsField).getStringLength() + 1 : 81;
- } else if (directAdsField.getAdsDataType() == AdsDataType.WSTRING) {
- // If an explicit size is given with the string, use this, if not use 512
- size = (directAdsField instanceof AdsStringField) ?
- ((long) ((AdsStringField) directAdsField).getStringLength() + 1) * 2 : 162;
- } else {
- size = directAdsField.getAdsDataType().getNumBytes();
- }
+ String dataTypeName = directAdsField.getAdsDataTypeName();
+ AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+ long size = adsDataTypeTableEntry.getSize();
+
AmsPacket amsPacket = new AdsReadRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(), 0, getInvokeId(),
directAdsField.getIndexGroup(), directAdsField.getIndexOffset(),size * directAdsField.getNumberOfElements());
@@ -457,18 +451,9 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
// Calculate the expected size of the response data.
long expectedResponseDataSize = directAdsFields.stream().mapToLong(
field -> {
- long size;
- if (field.getAdsDataType() == AdsDataType.STRING) {
- // If an explicit size is given with the string, use this, if not use 256
- size = (field instanceof AdsStringField) ?
- ((AdsStringField) field).getStringLength() + 1 : 256;
- } else if (field.getAdsDataType() == AdsDataType.WSTRING) {
- // If an explicit size is given with the string, use this, if not use 512
- size = (field instanceof AdsStringField) ?
- ((long) ((AdsStringField) field).getStringLength() + 1) * 2 : 512;
- } else {
- size = field.getAdsDataType().getNumBytes();
- }
+ String dataTypeName = field.getAdsDataTypeName();
+ AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+ long size = adsDataTypeTableEntry.getSize();
// Status code + payload size
return 4 + (size * field.getNumberOfElements());
}).sum();
@@ -476,11 +461,15 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
// With multi-requests, the index-group is fixed and the index offset indicates the number of elements.
AmsPacket amsPacket = new AdsReadWriteRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
- 0, getInvokeId(), ReservedIndexGroups.ADSIGRP_MULTIPLE_READ.getValue(), directAdsFields.size(), expectedResponseDataSize,
- directAdsFields.stream().map(directAdsField -> new AdsMultiRequestItemRead(
- directAdsField.getIndexGroup(), directAdsField.getIndexOffset(),
- ((long) directAdsField.getAdsDataType().getNumBytes() * directAdsField.getNumberOfElements())))
- .collect(Collectors.toList()), null);
+ 0, getInvokeId(), ReservedIndexGroups.ADSIGRP_MULTIPLE_READ.getValue(), directAdsFields.size(),
+ expectedResponseDataSize, directAdsFields.stream().map(directAdsField -> {
+ String dataTypeName = directAdsField.getAdsDataTypeName();
+ AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+ long size = adsDataTypeTableEntry.getSize();
+ return new AdsMultiRequestItemRead(
+ directAdsField.getIndexGroup(), directAdsField.getIndexOffset(),
+ (size * directAdsField.getNumberOfElements()));
+ }).collect(Collectors.toList()), null);
AmsTCPPacket amsTCPPacket = new AmsTCPPacket(amsPacket);
// Start a new request-transaction (Is ended in the response-handler)
@@ -535,14 +524,20 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
if (readBuffer != null) {
Map<String, ResponseItem<PlcValue>> values = new HashMap<>();
for (String fieldName : readRequest.getFieldNames()) {
- AdsField field = (AdsField) readRequest.getField(fieldName);
+ DirectAdsField field;
+ if(readRequest.getField(fieldName) instanceof DirectAdsField) {
+ field = (DirectAdsField) readRequest.getField(fieldName);
+ } else {
+ SymbolicAdsField symbolicAdsField = (SymbolicAdsField) readRequest.getField(fieldName);
+ field = getDirectAdsFieldForSymbolicName(symbolicAdsField);
+ }
// If the response-code was anything but OK, we don't need to parse the payload.
if (responseCodes.get(fieldName) != PlcResponseCode.OK) {
values.put(fieldName, new ResponseItem<>(responseCodes.get(fieldName), null));
}
// If the response-code was ok, parse the data returned.
else {
- values.put(fieldName, parsePlcValue(field, readBuffer));
+ values.put(fieldName, parseResponseItem(field, readBuffer));
}
}
return new DefaultPlcReadResponse(readRequest, values);
@@ -559,34 +554,91 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
}
}
- private ResponseItem<PlcValue> parsePlcValue(AdsField field, ReadBuffer readBuffer) {
+ private ResponseItem<PlcValue> parseResponseItem(DirectAdsField field, ReadBuffer readBuffer) {
try {
+ String dataTypeName = field.getAdsDataTypeName();
+ AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+ PlcValueType plcValueType = getPlcValueTypeForAdsDataType(adsDataTypeTableEntry);
+
int strLen = 0;
- if ((field.getAdsDataType() == AdsDataType.STRING) || (field.getAdsDataType() == AdsDataType.WSTRING)) {
+ if ((plcValueType == PlcValueType.STRING) || (plcValueType == PlcValueType.WSTRING)) {
strLen = (field instanceof AdsStringField) ? ((AdsStringField) field).getStringLength() : 256;
}
final int stringLength = strLen;
if (field.getNumberOfElements() == 1) {
- return new ResponseItem<>(PlcResponseCode.OK,
- DataItem.staticParse(readBuffer, field.getAdsDataType().getPlcValueType(), stringLength));
+ return new ResponseItem<>(PlcResponseCode.OK, parsePlcValue(plcValueType, adsDataTypeTableEntry, stringLength, readBuffer));
} else {
// Fetch all
final PlcValue[] resultItems = IntStream.range(0, field.getNumberOfElements()).mapToObj(i -> {
try {
- return DataItem.staticParse(readBuffer, field.getAdsDataType().getPlcValueType(), stringLength);
+ return parsePlcValue(plcValueType, adsDataTypeTableEntry, stringLength, readBuffer);
} catch (ParseException e) {
- LOGGER.warn("Error parsing field item of type: '{}' (at position {}})", field.getAdsDataType(), i, e);
+ LOGGER.warn("Error parsing field item of type: '{}' (at position {}})", field.getAdsDataTypeName(), i, e);
}
return null;
}).toArray(PlcValue[]::new);
return new ResponseItem<>(PlcResponseCode.OK, IEC61131ValueHandler.of(resultItems));
}
} catch (Exception e) {
- LOGGER.warn(String.format("Error parsing field item of type: '%s'", field.getAdsDataType()), e);
+ LOGGER.warn(String.format("Error parsing field item of type: '%s'", field.getAdsDataTypeName()), e);
return new ResponseItem<>(PlcResponseCode.INTERNAL_ERROR, null);
}
}
+ private PlcValue parsePlcValue(PlcValueType plcValueType, AdsDataTypeTableEntry adsDataTypeTableEntry, int stringLength, ReadBuffer readBuffer) throws ParseException {
+ switch (plcValueType) {
+ case Struct:
+ Map<String, PlcValue> properties = new HashMap<>();
+ int startPos = readBuffer.getPos();
+ int curPos = 0;
+ for (AdsDataTypeTableChildEntry child : adsDataTypeTableEntry.getChildren()) {
+ if(child.getOffset() > curPos) {
+ long skipBytes = child.getOffset() - curPos;
+ for(long i = 0; i < skipBytes; i++) {
+ readBuffer.readByte();
+ }
+ }
+ String propertyName = child.getPropertyName();
+ AdsDataTypeTableEntry propertyDataTypeTableEntry = dataTypeTable.get(child.getDataTypeName());
+ PlcValueType propertyPlcValueType = getPlcValueTypeForAdsDataType(propertyDataTypeTableEntry);
+ PlcValue propertyValue = parsePlcValue(propertyPlcValueType, propertyDataTypeTableEntry, stringLength, readBuffer);
+ properties.put(propertyName, propertyValue);
+ curPos = readBuffer.getPos() - startPos;
+ }
+ return new PlcStruct(properties);
+ case List:
+ return parseArrayLevel(adsDataTypeTableEntry, adsDataTypeTableEntry.getArrayInfo(), readBuffer);
+ default:
+ return DataItem.staticParse(readBuffer, plcValueType, stringLength);
+ }
+ }
+
+ private PlcValue parseArrayLevel(AdsDataTypeTableEntry adsDataTypeTableEntry, List<AdsDataTypeArrayInfo> arrayLayers, ReadBuffer readBuffer) throws ParseException {
+ // If this is the last layer of the Array, parse the values themselves.
+ if(arrayLayers.isEmpty()) {
+ String dataTypeName = adsDataTypeTableEntry.getDataTypeName();
+ dataTypeName = dataTypeName.substring(dataTypeName.lastIndexOf(" OF ") + 4);
+ int stringLength = 0;
+ if(dataTypeName.startsWith("STRING(")) {
+ stringLength = Integer.parseInt(dataTypeName.substring(7, dataTypeName.length() - 1));
+ } else if(dataTypeName.startsWith("WSTRING(")) {
+ stringLength = Integer.parseInt(dataTypeName.substring(8, dataTypeName.length() - 1));
+ }
+ AdsDataTypeTableEntry elementDataTypeTableEntry = dataTypeTable.get(dataTypeName);
+ PlcValueType plcValueType = getPlcValueTypeForAdsDataType(elementDataTypeTableEntry);
+ return parsePlcValue(plcValueType, elementDataTypeTableEntry, stringLength, readBuffer);
+ }
+
+ List<PlcValue> elements = new ArrayList<>();
+ List<AdsDataTypeArrayInfo> arrayInfo = adsDataTypeTableEntry.getArrayInfo();
+ AdsDataTypeArrayInfo firstLayer = arrayInfo.get(0);
+ for(int i = 0; i < firstLayer.getNumElements(); i++) {
+ List<AdsDataTypeArrayInfo> remainingLayers = arrayInfo.subList(1, arrayInfo.size());
+ elements.add(parseArrayLevel(adsDataTypeTableEntry, remainingLayers, readBuffer));
+ }
+ return new PlcList(elements);
+ }
+
@Override
public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
// Get all ADS addresses in their resolved state.
@@ -650,7 +702,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
protected CompletableFuture<PlcWriteResponse> singleWrite(PlcWriteRequest writeRequest, DirectAdsField directAdsField) {
CompletableFuture<PlcWriteResponse> future = new CompletableFuture<>();
- final String fieldName = writeRequest.getFieldNames().iterator().next();
+/* final String fieldName = writeRequest.getFieldNames().iterator().next();
final AdsField plcField = (AdsField) writeRequest.getField(fieldName);
final PlcValue plcValue = writeRequest.getPlcValue(fieldName);
final int stringLength;
@@ -694,7 +746,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
}));
} catch (Exception e) {
future.completeExceptionally(new PlcException("Error"));
- }
+ }*/
return future;
}
@@ -703,7 +755,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
// Calculate the size of all fields together.
// Calculate the expected size of the response data.
- int expectedRequestDataSize = directAdsFields.stream().mapToInt(
+/* int expectedRequestDataSize = directAdsFields.stream().mapToInt(
field -> field.getAdsDataType().getNumBytes() * field.getNumberOfElements()).sum();
byte[] writeBuffer = new byte[expectedRequestDataSize];
int pos = 0;
@@ -762,7 +814,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
}
// Finish the request-transaction.
transaction.endRequest();
- }));
+ }));*/
return future;
}
@@ -845,15 +897,19 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
List<AmsTCPPacket> amsTCPPackets = subscribeRequest.getFields().stream()
.map(field -> (DefaultPlcSubscriptionField) field)
- .map(field -> new AmsTCPPacket(new AdsAddDeviceNotificationRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
- configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
- 0, getInvokeId(),
- symbolicFieldMapping.get((SymbolicAdsField) field.getPlcField()).getIndexGroup(),
- symbolicFieldMapping.get((SymbolicAdsField) field.getPlcField()).getIndexOffset(),
- (long) ((AdsField) field.getPlcField()).getAdsDataType().getNumBytes() * field.getNumberOfElements(),
- field.getPlcSubscriptionType() == PlcSubscriptionType.CYCLIC ? 3 : 4, // if it's not cyclic, it's on change or event
- 0, // there is no api for that yet
- field.getDuration().orElse(Duration.ZERO).toMillis())))
+ .map(field -> {
+ AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(((DirectAdsField) field.getPlcField()).getAdsDataTypeName());
+ DirectAdsField directAdsField = getDirectAdsFieldForSymbolicName(field);
+ return new AmsTCPPacket(new AdsAddDeviceNotificationRequest(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+ configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
+ 0, getInvokeId(),
+ directAdsField.getIndexGroup(),
+ directAdsField.getIndexOffset(),
+ adsDataTypeTableEntry.getSize() * field.getNumberOfElements(),
+ field.getPlcSubscriptionType() == PlcSubscriptionType.CYCLIC ? 3 : 4, // if it's not cyclic, it's on change or event
+ 0, // there is no api for that yet
+ field.getDuration().orElse(Duration.ZERO).toMillis()));
+ })
.collect(Collectors.toList());
Map<String, ResponseItem<PlcSubscriptionHandle>> responses = new HashMap<>();
@@ -887,12 +943,15 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
.unwrap(responseAmsPacket -> (AdsAddDeviceNotificationResponse) responseAmsPacket.getUserdata())
.handle(response -> {
if (response.getResult() == ReturnCode.OK) {
+ DefaultPlcSubscriptionField subscriptionField = (DefaultPlcSubscriptionField) subscriptionRequest.getField(fieldName);
+ AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(((DirectAdsField) subscriptionField.getPlcField()).getAdsDataTypeName());
+
// Collect notification handle from individual response.
responses.put(fieldName, new ResponseItem<>(
parsePlcResponseCode(response.getResult()),
new AdsSubscriptionHandle(this,
fieldName,
- ((AdsField) ((DefaultPlcSubscriptionField) subscriptionRequest.getField(fieldName)).getPlcField()).getAdsDataType(),
+ adsDataTypeTableEntry,
response.getNotificationHandle())));
// After receiving the last ADD_DEVICE_NOTIFICATION response, complete the PLC4X response.
@@ -1022,7 +1081,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
Map<String, ResponseItem<PlcValue>> values = new HashMap<>();
ReadBufferByteBased readBuffer = new ReadBufferByteBased(data, ByteOrder.LITTLE_ENDIAN);
values.put(subscriptionHandle.getPlcFieldName(), new ResponseItem<>(PlcResponseCode.OK,
- DataItem.staticParse(readBuffer, subscriptionHandle.getAdsDataType().getPlcValueType(), data.length)));
+ DataItem.staticParse(readBuffer, getPlcValueTypeForAdsDataType(subscriptionHandle.getAdsDataType()), data.length)));
return values;
}
@@ -1052,7 +1111,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
// Find out for which of these symbolic addresses no resolution has been initiated.
final List<SymbolicAdsField> symbolicFieldsNeedingResolution = referencedSymbolicFields.stream()
- .filter(symbolicAdsField -> !symbolicFieldMapping.containsKey(symbolicAdsField))
+ .filter(symbolicAdsField -> getDirectAdsFieldForSymbolicName(symbolicAdsField) == null)
.collect(Collectors.toList());
// If there are unresolved symbolic addresses, initiate the resolution
@@ -1095,7 +1154,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
List<DirectAdsField> directAdsFields = new ArrayList<>(fields.size());
for (PlcField field : fields) {
if (field instanceof SymbolicAdsField) {
- directAdsFields.add(symbolicFieldMapping.get(field));
+ directAdsFields.add(getDirectAdsFieldForSymbolicName(field));
} else {
directAdsFields.add((DirectAdsField) field);
}
@@ -1107,7 +1166,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
// If all fields were resolved, we can continue instantly.
future.complete(fields.stream().map(plcField -> {
if (plcField instanceof SymbolicAdsField) {
- return symbolicFieldMapping.get(plcField);
+ return getDirectAdsFieldForSymbolicName(plcField);
} else {
return (DirectAdsField) plcField;
}
@@ -1146,10 +1205,12 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
// Read the handle.
long handle = readBuffer.readUnsignedLong(32);
- DirectAdsField directAdsField = new DirectAdsField(
+/* DirectAdsField directAdsField = new DirectAdsField(
ReservedIndexGroups.ADSIGRP_SYM_VALBYHND.getValue(), handle,
- symbolicAdsField.getAdsDataType(), symbolicAdsField.getNumberOfElements());
- symbolicFieldMapping.put(symbolicAdsField, directAdsField);
+ symbolicAdsField.getAdsDataTypeName(), symbolicAdsField.getNumberOfElements());*/
+
+ // TODO: Find out how to read the datatype for the given symbolic field
+ //symbolicFieldMapping.put(symbolicAdsField, directAdsField);
future.complete(null);
} catch (ParseException e) {
future.completeExceptionally(e);
@@ -1210,10 +1271,11 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
// Read the handle.
long handle = readBuffer.readUnsignedLong(32);
- DirectAdsField directAdsField = new DirectAdsField(
+ /*DirectAdsField directAdsField = new DirectAdsField(
ReservedIndexGroups.ADSIGRP_SYM_VALBYHND.getValue(), handle,
- symbolicAdsField.getAdsDataType(), symbolicAdsField.getNumberOfElements());
- symbolicFieldMapping.put(symbolicAdsField, directAdsField);
+ symbolicAdsField.getAdsDataTypeName(), symbolicAdsField.getNumberOfElements());*/
+ // TODO: Find out how to read the datatype for the given symbolic field
+ //symbolicFieldMapping.put(symbolicAdsField, directAdsField);
} else {
// TODO: Handle the case of unsuccessful resolution ..
}
@@ -1236,6 +1298,77 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
return invokeId;
}
+ protected DirectAdsField getDirectAdsFieldForSymbolicName(PlcField field) {
+ if(field instanceof DirectAdsField) {
+ return (DirectAdsField) field;
+ }
+
+ SymbolicAdsField symbolicAdsField = (SymbolicAdsField) field;
+ String symbolicAddress = symbolicAdsField.getSymbolicAddress();
+ String[] addressParts = symbolicAddress.split("\\.");
+
+ // If the number of parts are less than 2, we can find the entry in the symbol table directly.
+ if(addressParts.length < 2) {
+ // We can't find it, so we need to resolve it.
+ if(!symbolTable.containsKey(symbolicAddress)) {
+ return null;
+ }
+
+ AdsSymbolTableEntry adsSymbolTableEntry = symbolTable.get(symbolicAddress);
+ AdsDataTypeTableEntry dataTypeTableEntry = dataTypeTable.get(adsSymbolTableEntry.getDataTypeName());
+ return new DirectAdsField(adsSymbolTableEntry.getGroup(), adsSymbolTableEntry.getOffset(),
+ dataTypeTableEntry.getDataTypeName(), dataTypeTableEntry.getArrayDimensions());
+ }
+ // Otherwise we'll have to crawl through the dataType definitions.
+ else {
+ String symbolName = addressParts[0] + "." + addressParts[1];
+ AdsSymbolTableEntry adsSymbolTableEntry = symbolTable.get(symbolName);
+ AdsDataTypeTableEntry adsDataTypeTableEntry = dataTypeTable.get(adsSymbolTableEntry.getDataTypeName());
+ return resolveDirectAdsFieldForSymbolicNameFromDataType(
+ Arrays.asList(addressParts).subList(2, addressParts.length),
+ adsSymbolTableEntry.getGroup(), adsSymbolTableEntry.getOffset(), adsDataTypeTableEntry);
+ }
+ }
+
+ protected DirectAdsField resolveDirectAdsFieldForSymbolicNameFromDataType(List<String> remainingAddressParts, long currentGroup, long currentOffset, AdsDataTypeTableEntry adsDataTypeTableEntry) {
+ if(remainingAddressParts.isEmpty()) {
+ // TODO: Implement the Array support
+ return new DirectAdsField(currentGroup, currentOffset, adsDataTypeTableEntry.getDataTypeName(), null);
+ }
+
+ // Go through all children looking for a matching one.
+ for (AdsDataTypeTableChildEntry child : adsDataTypeTableEntry.getChildren()) {
+ if(child.getPropertyName().equals(remainingAddressParts.get(0))) {
+ AdsDataTypeTableEntry childAdsDataTypeTableEntry = dataTypeTable.get(child.getDataTypeName());
+ return resolveDirectAdsFieldForSymbolicNameFromDataType(
+ remainingAddressParts.subList(1, remainingAddressParts.size()),
+ currentGroup, currentOffset + child.getOffset(), childAdsDataTypeTableEntry);
+ }
+ }
+
+ throw new PlcRuntimeException(String.format("Couldn't find child with name '%s' for type '%s'",
+ remainingAddressParts.get(0), adsDataTypeTableEntry.getDataTypeName()));
+ }
+
+ protected PlcValueType getPlcValueTypeForAdsDataType(AdsDataTypeTableEntry dataTypeTableEntry) {
+ String dataTypeName = dataTypeTableEntry.getDataTypeName();
+ if(dataTypeName.startsWith("STRING(")) {
+ dataTypeName = "STRING";
+ } else if(dataTypeName.startsWith("WSTRING(")) {
+ dataTypeName = "WSTRING";
+ }
+ // First check, if this is a primitive type.
+ try {
+ return PlcValueType.valueOf(dataTypeName);
+ } catch (IllegalArgumentException e) {
+ // Then check if this is an array.
+ if(dataTypeTableEntry.getArrayDimensions() > 0) {
+ return PlcValueType.List;
+ }
+ return PlcValueType.Struct;
+ }
+ }
+
protected byte[] getNullByteTerminatedArray(String value) {
byte[] valueBytes = value.getBytes();
byte[] nullTerminatedBytes = new byte[valueBytes.length + 1];
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java
index 8e0b02735..f0b874735 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/readwrite/utils/StaticHelper.java
@@ -47,6 +47,8 @@ public class StaticHelper {
break;
}
}
+ // Read the terminating byte.
+ readBuffer.readByte();
final byte[] byteArray = new byte[bytes.size()];
for (int i = 0; i < bytes.size(); i++) {
byteArray[i] = bytes.get(i);
@@ -68,6 +70,8 @@ public class StaticHelper {
break;
}
}
+ // Read the terminating byte.
+ readBuffer.readByte();
final byte[] byteArray = new byte[bytes.size()];
for (int i = 0; i < bytes.size(); i++) {
byteArray[i] = bytes.get(i);