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 2020/11/18 14:35:46 UTC
[plc4x] branch feature/plc4go updated: - Fixed the reading of
strings and Wstrings (Introduced a similar type of string fields,
which have a length attribute just like in S7)
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch feature/plc4go
in repository https://gitbox.apache.org/repos/asf/plc4x.git
The following commit(s) were added to refs/heads/feature/plc4go by this push:
new 62706e0 - Fixed the reading of strings and Wstrings (Introduced a similar type of string fields, which have a length attribute just like in S7)
62706e0 is described below
commit 62706e0abaf4455ffe0da2f1a64eed7c24bd6c53
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Wed Nov 18 15:35:38 2020 +0100
- Fixed the reading of strings and Wstrings (Introduced a similar type of string fields, which have a length attribute just like in S7)
---
.../plc4x/java/ads/field/AdsFieldHandler.java | 8 +-
.../plc4x/java/ads/field/AdsStringField.java | 25 ++++
.../plc4x/java/ads/field/DirectAdsStringField.java | 126 +++++++++++++++++++++
.../plc4x/java/ads/field/SymbolicAdsField.java | 7 +-
...icAdsField.java => SymbolicAdsStringField.java} | 75 ++++--------
.../plc4x/java/ads/protocol/AdsProtocolLogic.java | 107 ++++++++++++-----
.../apache/plc4x/java/ads/utils/StaticHelper.java | 32 +++---
.../plc4x/protocol/ads/ManualAdsDriverTest.java | 8 +-
.../plc4x/java/examples/helloplc4x/HelloPlc4x.java | 10 +-
.../org/apache/plc4x/test/manual/ManualTest.java | 1 -
.../ads/src/main/resources/protocols/ads/ads.mspec | 16 ++-
11 files changed, 290 insertions(+), 125 deletions(-)
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 2b85eea..fdeaebd 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
@@ -26,8 +26,12 @@ public class AdsFieldHandler implements PlcFieldHandler {
@Override
public PlcField createField(String fieldQuery) throws PlcInvalidFieldException {
- if (DirectAdsField.matches(fieldQuery)) {
- return DirectAdsField.of(fieldQuery);
+ if (DirectAdsStringField.matches(fieldQuery)) {
+ return DirectAdsStringField.of(fieldQuery);
+ } else if (DirectAdsField.matches(fieldQuery)) {
+ return SymbolicAdsField.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/AdsStringField.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsStringField.java
new file mode 100644
index 0000000..92ab3db
--- /dev/null
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/AdsStringField.java
@@ -0,0 +1,25 @@
+/*
+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
+
+ http://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;
+
+public interface AdsStringField extends AdsField {
+
+ int getStringLength();
+
+}
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
new file mode 100644
index 0000000..89d00bc
--- /dev/null
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/DirectAdsStringField.java
@@ -0,0 +1,126 @@
+/*
+ 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
+
+ http://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.types.AdsDataType;
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * ADS address witch is defined by {@code indexGroup/indexOffset}. These values can be either supplied as int or hex
+ * representation.
+ */
+public class DirectAdsStringField extends DirectAdsField implements AdsStringField {
+
+ private static final Pattern RESOURCE_STRING_ADDRESS_PATTERN = Pattern.compile("^((0[xX](?<indexGroupHex>[0-9a-fA-F]+))|(?<indexGroup>\\d+))/((0[xX](?<indexOffsetHex>[0-9a-fA-F]+))|(?<indexOffset>\\d+)):(?<adsDataType>STRING|WSTRING)\\((?<stringLength>\\d{1,3})\\)(\\[(?<numberOfElements>\\d)])?");
+
+ private final int stringLength;
+
+ public DirectAdsStringField(long indexGroup, long indexOffset, AdsDataType adsDataType, int stringLength, Integer numberOfElements) {
+ super(indexGroup, indexOffset, adsDataType, 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(String address) {
+ Matcher matcher = RESOURCE_STRING_ADDRESS_PATTERN.matcher(address);
+ if (!matcher.matches()) {
+ throw new PlcInvalidFieldException(address, RESOURCE_STRING_ADDRESS_PATTERN, "{indexGroup}/{indexOffset}:{adsDataType}([numberOfElements])?");
+ }
+
+ String indexGroupStringHex = matcher.group("indexGroupHex");
+ String indexGroupString = matcher.group("indexGroup");
+
+ String indexOffsetStringHex = matcher.group("indexOffsetHex");
+ String indexOffsetString = matcher.group("indexOffset");
+
+ long indexGroup;
+ if (indexGroupStringHex != null) {
+ indexGroup = Long.parseLong(indexGroupStringHex, 16);
+ } else {
+ indexGroup = Long.parseLong(indexGroupString);
+ }
+
+ long indexOffset;
+ if (indexOffsetStringHex != null) {
+ indexOffset = Long.parseLong(indexOffsetStringHex, 16);
+ } else {
+ indexOffset = Long.parseLong(indexOffsetString);
+ }
+
+ 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 DirectAdsStringField(indexGroup, indexOffset, adsDataType, stringLength, numberOfElements);
+ }
+
+ public static boolean matches(String address) {
+ return RESOURCE_STRING_ADDRESS_PATTERN.matcher(address).matches();
+ }
+
+ @Override
+ public int getStringLength() {
+ return stringLength;
+ }
+
+ @Override
+ public String toString() {
+ return "DirectAdsStringField{" +
+ "indexGroup=" + getIndexGroup() +
+ ", indexOffset=" + getIndexOffset() +
+ ", stringLength=" + stringLength +
+ '}';
+ }
+
+ @Override
+ public void xmlSerialize(Element parent) {
+ Document doc = parent.getOwnerDocument();
+ Element messageElement = doc.createElement(getClass().getSimpleName());
+ parent.appendChild(messageElement);
+ Element indexGroupElement = doc.createElement("indexGroup");
+ indexGroupElement.appendChild(doc.createTextNode(Long.toString(getIndexGroup())));
+ messageElement.appendChild(indexGroupElement);
+
+ Element indexOffsetElement = doc.createElement("indexOffset");
+ indexOffsetElement.appendChild(doc.createTextNode(Long.toString(getIndexOffset())));
+ messageElement.appendChild(indexOffsetElement);
+
+ Element numberOfElementsElement = doc.createElement("numberOfElements");
+ numberOfElementsElement.appendChild(doc.createTextNode(Integer.toString(getNumberOfElements())));
+ messageElement.appendChild(numberOfElementsElement);
+
+ Element datatypeElement = doc.createElement("dataType");
+ datatypeElement.appendChild(doc.createTextNode(getPlcDataType()));
+ messageElement.appendChild(datatypeElement);
+ }
+
+}
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 d18305b..4455c59 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
@@ -40,13 +40,14 @@ public class SymbolicAdsField implements AdsField {
private final int numberOfElements;
- private SymbolicAdsField(String symbolicAddress, AdsDataType adsDataType, Integer numberOfElements) {
+ public SymbolicAdsField(String symbolicAddress, AdsDataType adsDataType, Integer numberOfElements) {
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) {
@@ -69,7 +70,7 @@ public class SymbolicAdsField implements AdsField {
return SYMBOLIC_ADDRESS_PATTERN.matcher(address).matches();
}
- public String getSymbolicField() {
+ public String getSymbolicAddress() {
return symbolicAddress;
}
@@ -119,7 +120,7 @@ public class SymbolicAdsField implements AdsField {
parent.appendChild(messageElement);
Element symbolicAddressElement = doc.createElement("symbolicAddress");
- symbolicAddressElement.appendChild(doc.createTextNode(getSymbolicField()));
+ symbolicAddressElement.appendChild(doc.createTextNode(getSymbolicAddress()));
messageElement.appendChild(symbolicAddressElement);
Element numberOfElementsElement = doc.createElement("numberOfElements");
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/SymbolicAdsStringField.java
similarity index 57%
copy from plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsField.java
copy to plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/field/SymbolicAdsStringField.java
index d18305b..554cf06 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/SymbolicAdsStringField.java
@@ -30,85 +30,50 @@ import java.util.regex.Pattern;
/**
* ADS address witch is defined by symbolic name (e.g. {@code Main.items[0]}).
*/
-public class SymbolicAdsField implements AdsField {
+public class SymbolicAdsStringField extends SymbolicAdsField implements AdsStringField {
- private static final Pattern SYMBOLIC_ADDRESS_PATTERN = Pattern.compile("^(?<symbolicAddress>.+):(?<adsDataType>\\w+)(\\[(?<numberOfElements>\\d)])?");
+ private static final Pattern SYMBOLIC_ADDRESS_STRING_PATTERN = Pattern.compile("^(?<symbolicAddress>.+):(?<adsDataType>'STRING'|'WSTRING')\\((?<stringLength>\\d{1,3})\\)(\\[(?<numberOfElements>\\d)])?");
- private final String symbolicAddress;
+ private final int stringLength;
- private final AdsDataType adsDataType;
-
- private final int numberOfElements;
-
- private SymbolicAdsField(String symbolicAddress, AdsDataType adsDataType, Integer numberOfElements) {
- 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);
- }
+ private SymbolicAdsStringField(String symbolicAddress, AdsDataType adsDataType, int stringLength, Integer numberOfElements) {
+ super(symbolicAddress, adsDataType, numberOfElements);
+ this.stringLength = stringLength;
}
- public static SymbolicAdsField of(String address) {
- Matcher matcher = SYMBOLIC_ADDRESS_PATTERN.matcher(address);
+ public static SymbolicAdsStringField of(String address) {
+ Matcher matcher = SYMBOLIC_ADDRESS_STRING_PATTERN.matcher(address);
if (!matcher.matches()) {
- throw new PlcInvalidFieldException(address, SYMBOLIC_ADDRESS_PATTERN, "{address}");
+ 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 SymbolicAdsField(symbolicAddress, adsDataType, numberOfElements);
+ return new SymbolicAdsStringField(symbolicAddress, adsDataType, stringLength, numberOfElements);
}
public static boolean matches(String address) {
- return SYMBOLIC_ADDRESS_PATTERN.matcher(address).matches();
- }
-
- public String getSymbolicField() {
- 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) {
- return true;
- }
- if (!(o instanceof SymbolicAdsField)) {
- return false;
- }
- SymbolicAdsField that = (SymbolicAdsField) o;
- return Objects.equals(symbolicAddress, that.symbolicAddress);
+ return SYMBOLIC_ADDRESS_STRING_PATTERN.matcher(address).matches();
}
@Override
- public int hashCode() {
- return Objects.hash(symbolicAddress);
+ public int getStringLength() {
+ return stringLength;
}
@Override
public String toString() {
- return "SymbolicAdsField{" +
- "symbolicAddress='" + symbolicAddress + '\'' +
+ return "SymbolicAdsStringField{" +
+ "symbolicAddress='" + getSymbolicAddress() + '\'' +
+ ", stringLength=" + stringLength +
'}';
}
@@ -119,7 +84,7 @@ public class SymbolicAdsField implements AdsField {
parent.appendChild(messageElement);
Element symbolicAddressElement = doc.createElement("symbolicAddress");
- symbolicAddressElement.appendChild(doc.createTextNode(getSymbolicField()));
+ symbolicAddressElement.appendChild(doc.createTextNode(getSymbolicAddress()));
messageElement.appendChild(symbolicAddressElement);
Element numberOfElementsElement = doc.createElement("numberOfElements");
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 817215f..93990bf 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
@@ -18,15 +18,17 @@ under the License.
*/
package org.apache.plc4x.java.ads.protocol;
-import org.apache.plc4x.java.ads.readwrite.types.ReturnCode;
import org.apache.plc4x.java.ads.configuration.AdsConfiguration;
import org.apache.plc4x.java.ads.field.AdsField;
+import org.apache.plc4x.java.ads.field.AdsStringField;
import org.apache.plc4x.java.ads.field.DirectAdsField;
import org.apache.plc4x.java.ads.field.SymbolicAdsField;
import org.apache.plc4x.java.ads.readwrite.*;
import org.apache.plc4x.java.ads.readwrite.io.DataItemIO;
+import org.apache.plc4x.java.ads.readwrite.types.AdsDataType;
import org.apache.plc4x.java.ads.readwrite.types.CommandId;
import org.apache.plc4x.java.ads.readwrite.types.ReservedIndexGroups;
+import org.apache.plc4x.java.ads.readwrite.types.ReturnCode;
import org.apache.plc4x.java.api.exceptions.PlcException;
import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
@@ -35,7 +37,7 @@ import org.apache.plc4x.java.api.messages.PlcWriteRequest;
import org.apache.plc4x.java.api.messages.PlcWriteResponse;
import org.apache.plc4x.java.api.model.PlcField;
import org.apache.plc4x.java.api.types.PlcResponseCode;
-import org.apache.plc4x.java.api.value.*;
+import org.apache.plc4x.java.api.value.PlcValue;
import org.apache.plc4x.java.spi.ConversationContext;
import org.apache.plc4x.java.spi.Plc4xProtocolBase;
import org.apache.plc4x.java.spi.configuration.HasConfiguration;
@@ -51,8 +53,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.Duration;
-import java.util.*;
-import java.util.concurrent.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -119,9 +125,9 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
getDirectAddresses(readRequest.getFields());
// If all addresses were already resolved we can send the request immediately.
- if(directAdsFieldsFuture.isDone()) {
+ if (directAdsFieldsFuture.isDone()) {
final List<DirectAdsField> fields = directAdsFieldsFuture.getNow(null);
- if(fields != null) {
+ if (fields != null) {
return executeRead(readRequest, fields);
} else {
final CompletableFuture<PlcReadResponse> errorFuture = new CompletableFuture<>();
@@ -139,7 +145,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
else {
CompletableFuture<PlcReadResponse> delayedRead = new CompletableFuture<>();
directAdsFieldsFuture.handle((directAdsFields, throwable) -> {
- if(directAdsFields != null) {
+ if (directAdsFields != null) {
final CompletableFuture<PlcReadResponse> delayedResponse =
executeRead(readRequest, directAdsFields);
delayedResponse.handle((plcReadResponse, throwable1) -> {
@@ -175,8 +181,20 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
protected CompletableFuture<PlcReadResponse> singleRead(PlcReadRequest readRequest, DirectAdsField directAdsField) {
CompletableFuture<PlcReadResponse> future = new CompletableFuture<>();
- int size = directAdsField.getAdsDataType().getNumBytes() * directAdsField.getNumberOfElements();
- AdsData adsData = new AdsReadRequest(directAdsField.getIndexGroup(), directAdsField.getIndexOffset(), size);
+ 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();
+ }
+ AdsData adsData = new AdsReadRequest(directAdsField.getIndexGroup(), directAdsField.getIndexOffset(),
+ size * directAdsField.getNumberOfElements());
AmsPacket amsPacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
CommandId.ADS_READ, DEFAULT_COMMAND_STATE, 0, getInvokeId(), adsData);
@@ -191,7 +209,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
.check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == amsPacket.getInvokeId())
.unwrap(response -> (AdsReadResponse) response.getUserdata().getData())
.handle(responseAdsData -> {
- if(responseAdsData.getResult() == ReturnCode.OK) {
+ if (responseAdsData.getResult() == ReturnCode.OK) {
final PlcReadResponse plcReadResponse = convertToPlc4xReadResponse(readRequest, responseAdsData);
// Convert the response from the PLC into a PLC4X Response ...
future.complete(plcReadResponse);
@@ -211,7 +229,21 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
// Calculate the size of all fields together.
// Calculate the expected size of the response data.
long expectedResponseDataSize = directAdsFields.stream().mapToLong(
- field -> (field.getAdsDataType().getNumBytes() * field.getNumberOfElements()) + 4).sum();
+ 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 : 81;
+ } 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: 162;
+ } else {
+ size = field.getAdsDataType().getNumBytes();
+ }
+ return (size * field.getNumberOfElements()) + 4;
+ }).sum();
// With multi-requests, the index-group is fixed and the index offset indicates the number of elements.
AdsData adsData = new AdsReadWriteRequest(
@@ -235,7 +267,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
.check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == amsPacket.getInvokeId())
.unwrap(response -> (AdsReadWriteResponse) response.getUserdata().getData())
.handle(responseAdsData -> {
- if(responseAdsData.getResult() == ReturnCode.OK) {
+ if (responseAdsData.getResult() == ReturnCode.OK) {
final PlcReadResponse plcReadResponse = convertToPlc4xReadResponse(readRequest, responseAdsData);
// Convert the response from the PLC into a PLC4X Response ...
future.complete(plcReadResponse);
@@ -271,12 +303,12 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
}
}
}
- if(readBuffer != null) {
+ if (readBuffer != null) {
Map<String, ResponseItem<PlcValue>> values = new HashMap<>();
for (String fieldName : readRequest.getFieldNames()) {
AdsField field = (AdsField) readRequest.getField(fieldName);
// If the response-code was anything but OK, we don't need to parse the payload.
- if(responseCodes.get(fieldName) != PlcResponseCode.OK) {
+ 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.
@@ -300,14 +332,21 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
private ResponseItem<PlcValue> parsePlcValue(AdsField field, ReadBuffer readBuffer) {
try {
+ int strLen = 0;
+ if (field.getAdsDataType() == AdsDataType.STRING) {
+ strLen = (field instanceof AdsStringField) ? ((AdsStringField) field).getStringLength() : 256;
+ } else if (field.getAdsDataType() == AdsDataType.WSTRING) {
+ strLen = (field instanceof AdsStringField) ? ((AdsStringField) field).getStringLength() : 512;
+ }
+ final int stringLength = strLen;
if (field.getNumberOfElements() == 1) {
return new ResponseItem<>(PlcResponseCode.OK,
- DataItemIO.staticParse(readBuffer, field.getAdsDataType().getDataFormatName()));
+ DataItemIO.staticParse(readBuffer, field.getAdsDataType().getDataFormatName(), stringLength));
} else {
// Fetch all
final PlcValue[] resultItems = IntStream.range(0, field.getNumberOfElements()).mapToObj(i -> {
try {
- return DataItemIO.staticParse(readBuffer, field.getAdsDataType().getDataFormatName());
+ return DataItemIO.staticParse(readBuffer, field.getAdsDataType().getDataFormatName(), stringLength);
} catch (ParseException e) {
LOGGER.warn("Error parsing field item of type: '{}' (at position {}})", field.getAdsDataType(), i, e);
}
@@ -328,9 +367,9 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
getDirectAddresses(writeRequest.getFields());
// If all addresses were already resolved we can send the request immediately.
- if(directAdsFieldsFuture.isDone()) {
+ if (directAdsFieldsFuture.isDone()) {
final List<DirectAdsField> fields = directAdsFieldsFuture.getNow(null);
- if(fields != null) {
+ if (fields != null) {
return executeWrite(writeRequest, fields);
} else {
final CompletableFuture<PlcWriteResponse> errorFuture = new CompletableFuture<>();
@@ -348,7 +387,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
else {
CompletableFuture<PlcWriteResponse> delayedWrite = new CompletableFuture<>();
directAdsFieldsFuture.handle((directAdsFields, throwable) -> {
- if(directAdsFields != null) {
+ if (directAdsFields != null) {
final CompletableFuture<PlcWriteResponse> delayedResponse =
executeWrite(writeRequest, directAdsFields);
delayedResponse.handle((plcReadResponse, throwable1) -> {
@@ -369,7 +408,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
}
protected CompletableFuture<PlcWriteResponse> executeWrite(PlcWriteRequest writeRequest,
- List<DirectAdsField> directAdsFields) {
+ List<DirectAdsField> directAdsFields) {
// Depending on the number of fields, use a single item request or a sum-request
if (directAdsFields.size() == 1) {
// Do a normal (single item) ADS Write Request
@@ -387,8 +426,12 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
final String fieldName = writeRequest.getFieldNames().iterator().next();
final AdsField plcField = (AdsField) writeRequest.getField(fieldName);
final PlcValue plcValue = writeRequest.getPlcValue(fieldName);
+ final int stringLength = (directAdsField.getAdsDataType() == AdsDataType.STRING) ?
+ plcValue.getString().length() + 1 : (directAdsField.getAdsDataType() == AdsDataType.WSTRING) ?
+ (plcValue.getString().length() + 1) * 2 : 0;
try {
- WriteBuffer writeBuffer = DataItemIO.staticSerialize(plcValue, plcField.getAdsDataType().getDataFormatName(), true);
+ WriteBuffer writeBuffer = DataItemIO.staticSerialize(plcValue,
+ plcField.getAdsDataType().getDataFormatName(), stringLength, true);
AdsData adsData = new AdsWriteRequest(
directAdsField.getIndexGroup(), directAdsField.getIndexOffset(), writeBuffer.getData());
AmsPacket amsPacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
@@ -434,8 +477,12 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
for (String fieldName : writeRequest.getFieldNames()) {
final AdsField field = (AdsField) writeRequest.getField(fieldName);
final PlcValue plcValue = writeRequest.getPlcValue(fieldName);
+ final int stringLength = (field.getAdsDataType() == AdsDataType.STRING) ?
+ plcValue.getString().length() + 1 : (field.getAdsDataType() == AdsDataType.WSTRING) ?
+ (plcValue.getString().length() + 1) * 2 : 0;
try {
- final WriteBuffer itemWriteBuffer = DataItemIO.staticSerialize(plcValue, field.getAdsDataType().getDataFormatName(), true);
+ final WriteBuffer itemWriteBuffer = DataItemIO.staticSerialize(plcValue,
+ field.getAdsDataType().getDataFormatName(), stringLength, true);
int numBytes = itemWriteBuffer.getPos();
System.arraycopy(itemWriteBuffer.getData(), 0, writeBuffer, pos, numBytes);
pos += numBytes;
@@ -466,7 +513,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
.check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == amsPacket.getInvokeId())
.unwrap(response -> (AdsReadWriteResponse) response.getUserdata().getData())
.handle(responseAdsData -> {
- if(responseAdsData.getResult() == ReturnCode.OK) {
+ if (responseAdsData.getResult() == ReturnCode.OK) {
final PlcWriteResponse plcWriteResponse = convertToPlc4xWriteResponse(writeRequest, responseAdsData);
// Convert the response from the PLC into a PLC4X Response ...
future.complete(plcWriteResponse);
@@ -557,7 +604,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
// Complete the future asynchronously as soon as all fields are resolved.
resolutionComplete.handleAsync((unused, throwable) -> {
- if(throwable != null) {
+ if (throwable != null) {
return future.completeExceptionally(throwable.getCause());
} else {
List<DirectAdsField> directAdsFields = new ArrayList<>(fields.size());
@@ -589,7 +636,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
AdsData adsData = new AdsReadWriteRequest(ReservedIndexGroups.ADSIGRP_SYM_HNDBYNAME.getValue(), 0,
4, null,
- getNullByteTerminatedArray(symbolicAdsField.getSymbolicField()));
+ getNullByteTerminatedArray(symbolicAdsField.getSymbolicAddress()));
AmsPacket amsPacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
CommandId.ADS_READ_WRITE, DEFAULT_COMMAND_STATE, 0, getInvokeId(), adsData);
@@ -606,9 +653,9 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
.check(adsDataResponse -> adsDataResponse instanceof AdsReadWriteResponse)
.unwrap(adsDataResponse -> (AdsReadWriteResponse) adsDataResponse)
.handle(responseAdsData -> {
- if(responseAdsData.getResult() != ReturnCode.OK) {
+ if (responseAdsData.getResult() != ReturnCode.OK) {
future.completeExceptionally(new PlcException("Couldn't retrieve handle for symbolic field " +
- symbolicAdsField.getSymbolicField() + " got return code " + responseAdsData.getResult().name()));
+ symbolicAdsField.getSymbolicAddress() + " got return code " + responseAdsData.getResult().name()));
} else {
ReadBuffer readBuffer = new ReadBuffer(responseAdsData.getData(), true);
try {
@@ -636,11 +683,11 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
long expectedResponseDataSize = symbolicAdsFields.size() * 12;
// Concatenate the string part of each symbolic address into one concattenated string and get the bytes.
byte[] addressData = symbolicAdsFields.stream().map(
- SymbolicAdsField::getSymbolicField).collect(Collectors.joining("")).getBytes();
+ SymbolicAdsField::getSymbolicAddress).collect(Collectors.joining("")).getBytes();
AdsData adsData = new AdsReadWriteRequest(ReservedIndexGroups.ADSIGRP_MULTIPLE_READ_WRITE.getValue(),
symbolicAdsFields.size(), expectedResponseDataSize, symbolicAdsFields.stream().map(symbolicAdsField ->
new AdsMultiRequestItemReadWrite(ReservedIndexGroups.ADSIGRP_SYM_HNDBYNAME.getValue(), 0,
- 4, symbolicAdsField.getSymbolicField().length())
+ 4, symbolicAdsField.getSymbolicAddress().length())
).toArray(AdsMultiRequestItem[]::new), addressData);
AmsPacket amsPacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
@@ -700,7 +747,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
protected long getInvokeId() {
long invokeId = invokeIdGenerator.getAndIncrement();
// If we've reached the max value for a 16 bit transaction identifier, reset back to 1
- if(invokeIdGenerator.get() == 0xFFFFFFFF) {
+ if (invokeIdGenerator.get() == 0xFFFFFFFF) {
invokeIdGenerator.set(1);
}
return invokeId;
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/utils/StaticHelper.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/utils/StaticHelper.java
index 1ae2ca0..5622bac 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/utils/StaticHelper.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/utils/StaticHelper.java
@@ -30,39 +30,39 @@ import java.util.List;
public class StaticHelper {
- public static String parseAmsString(ReadBuffer io, String encoding) {
+ public static String parseAmsString(ReadBuffer io, int stringLength, String encoding) {
try {
if ("UTF-8".equalsIgnoreCase(encoding)) {
List<Byte> bytes = new ArrayList<>();
- while (io.hasMore(8)) {
+ for(int i = 0; (i < stringLength) && io.hasMore(8); i++) {
final byte curByte = io.readByte(8);
if (curByte != 0) {
bytes.add(curByte);
} else {
- final byte[] byteArray = new byte[bytes.size()];
- for (int i = 0; i < bytes.size(); i++) {
- byteArray[i] = bytes.get(i);
- }
- return new String(byteArray, StandardCharsets.UTF_8);
+ break;
}
}
- throw new PlcRuntimeException("Reached the end of the input without finishing the string");
+ final byte[] byteArray = new byte[bytes.size()];
+ for (int i = 0; i < bytes.size(); i++) {
+ byteArray[i] = bytes.get(i);
+ }
+ return new String(byteArray, StandardCharsets.UTF_8);
} else if ("UTF-16".equalsIgnoreCase(encoding)) {
List<Byte> bytes = new ArrayList<>();
- while (io.hasMore(16)) {
+ for(int i = 0; (i < stringLength) && io.hasMore(16); i++) {
final short curShort = io.readShort(16);
if (curShort != 0) {
bytes.add((byte) (curShort >>> 8));
bytes.add((byte) (curShort & 0xFF));
} else {
- final byte[] byteArray = new byte[bytes.size()];
- for (int i = 0; i < bytes.size(); i++) {
- byteArray[i] = bytes.get(i);
- }
- return new String(byteArray, StandardCharsets.UTF_16);
+ break;
}
}
- throw new PlcRuntimeException("Reached the end of the input without finishing the string");
+ final byte[] byteArray = new byte[bytes.size()];
+ for (int i = 0; i < bytes.size(); i++) {
+ byteArray[i] = bytes.get(i);
+ }
+ return new String(byteArray, StandardCharsets.UTF_16);
} else {
throw new PlcRuntimeException("Unsupported string encoding " + encoding);
}
@@ -71,7 +71,7 @@ public class StaticHelper {
}
}
- public static void serializeAmsString(WriteBuffer io, PlcValue value, Object encoding) {
+ public static void serializeAmsString(WriteBuffer io, PlcValue value, int stringLength, Object encoding) {
// TODO: Need to implement the serialization or we can't write strings
throw new PlcRuntimeException("Not implemented yet");
}
diff --git a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java
index 2df45f2..50cb24b 100644
--- a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java
+++ b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ManualAdsDriverTest.java
@@ -69,7 +69,7 @@ public class ManualAdsDriverTest extends ManualTest {
public static void main(String[] args) throws Exception {
ManualAdsDriverTest test = new ManualAdsDriverTest("ads:tcp://192.168.23.20?sourceAmsNetId=192.168.23.200.1.1&sourceAmsPort=65534&targetAmsNetId=192.168.23.20.1.1&targetAmsPort=851");
- test.addTestCase("main.hurz_BOOL:BOOL", true);
+ /*test.addTestCase("main.hurz_BOOL:BOOL", true);
test.addTestCase("main.hurz_BYTE:BYTE", Arrays.asList(false, false, true, false, true, false, true, false));
test.addTestCase("main.hurz_WORD:WORD", Arrays.asList(true, false, true, false, false, true, false, true, true, false, true, true, true, false, false, false));
test.addTestCase("main.hurz_DWORD:DWORD", Arrays.asList(true, true, true, true, true, true, false, false, true, true, false, true, true, true, true, false, true, false, false, false, true, false, false, false, true, false, true, true, true, false, false, false));
@@ -83,15 +83,15 @@ public class ManualAdsDriverTest extends ManualTest {
test.addTestCase("main.hurz_ULINT:ULINT", 4242442424242424242L);
test.addTestCase("main.hurz_REAL:REAL", 3.14159265359F);
test.addTestCase("main.hurz_LREAL:LREAL", 2.71828182846D);
- test.addTestCase("main.hurz_STRING:STRING", "hurz");
+ test.addTestCase("main.hurz_STRING:STRING", "hurz");*/
test.addTestCase("main.hurz_WSTRING:WSTRING", "wolf");
- test.addTestCase("main.hurz_TIME:TIME", "PT1.234S");
+ /*test.addTestCase("main.hurz_TIME:TIME", "PT1.234S");
test.addTestCase("main.hurz_LTIME:LTIME", "PT24015H23M12.034002044S");
test.addTestCase("main.hurz_DATE:DATE", "1978-03-28");
test.addTestCase("main.hurz_TIME_OF_DAY:TIME_OF_DAY", "15:36:30.123");
test.addTestCase("main.hurz_TOD:TOD", "16:17:18.123");
test.addTestCase("main.hurz_DATE_AND_TIME:DATE_AND_TIME", "1996-05-06T15:36:30");
- test.addTestCase("main.hurz_DT:DT", "1972-03-29T00:00");
+ test.addTestCase("main.hurz_DT:DT", "1972-03-29T00:00");*/
test.run();
}
diff --git a/plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/HelloPlc4x.java b/plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/HelloPlc4x.java
index f5e40e6..f4db3e4 100644
--- a/plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/HelloPlc4x.java
+++ b/plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/HelloPlc4x.java
@@ -60,7 +60,7 @@ public class HelloPlc4x {
// - Give the single item requested the alias name "value"
PlcReadRequest.Builder builder = plcConnection.readRequestBuilder();
for (int i = 0; i < options.getFieldAddress().length; i++) {
- builder.addItem("value-" + i, options.getFieldAddress()[i]);
+ builder.addItem("value-" + options.getFieldAddress()[i], options.getFieldAddress()[i]);
}
PlcReadRequest readRequest = builder.build();
@@ -73,13 +73,13 @@ public class HelloPlc4x {
// Simply iterating over the field names returned in the response.
printResponse(syncResponse);
- PlcValue asPlcValue = syncResponse.getAsPlcValue();
- System.out.println(asPlcValue.toString());
+ /*PlcValue asPlcValue = syncResponse.getAsPlcValue();
+ System.out.println(asPlcValue.toString());*/
//////////////////////////////////////////////////////////
// Read asynchronously ...
// Register a callback executed as soon as a response arrives.
- logger.info("Asynchronous request ...");
+ /*logger.info("Asynchronous request ...");
CompletionStage<? extends PlcReadResponse> asyncResponse = readRequest.execute();
asyncResponse.whenComplete((readResponse, throwable) -> {
if (readResponse != null) {
@@ -87,7 +87,7 @@ public class HelloPlc4x {
} else {
logger.error("An error occurred: " + throwable.getMessage(), throwable);
}
- });
+ });*/
// Give the async request a little time...
TimeUnit.MILLISECONDS.sleep(1000);
diff --git a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/manual/ManualTest.java b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/manual/ManualTest.java
index c9110ec..0c549d4 100644
--- a/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/manual/ManualTest.java
+++ b/plc4j/utils/test-utils/src/main/java/org/apache/plc4x/test/manual/ManualTest.java
@@ -28,7 +28,6 @@ import org.junit.jupiter.api.Assertions;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
public abstract class ManualTest {
diff --git a/protocols/ads/src/main/resources/protocols/ads/ads.mspec b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
index 508dcd0..2e49132 100644
--- a/protocols/ads/src/main/resources/protocols/ads/ads.mspec
+++ b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
@@ -386,7 +386,7 @@
[array int 8 'data' count 'sampleSize']
]
-[dataIo 'DataItem' [string 'dataFormatName']
+[dataIo 'DataItem' [string 'dataFormatName', int 32 'stringLength']
[typeSwitch 'dataFormatName'
// -----------------------------------------
// Bit
@@ -464,12 +464,10 @@
// [simple string 16 'UTF-16' 'value']
]
['IEC61131_STRING' STRING
- [manual string 'UTF-8' 'value' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.parseAmsString", io, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.serializeAmsString", io, _value, _type.encoding)' '_value.length + 2']
- [reserved uint 32 '0x00000000']
+ [manual string 'UTF-8' 'value' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.parseAmsString", io, stringLength, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.serializeAmsString", io, _value, stringLength, _type.encoding)' 'stringLength + 1']
]
['IEC61131_WSTRING' STRING
- [manual string 'UTF-16' 'value' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.parseAmsString", io, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.serializeAmsString", io, _value, _type.encoding)' '_value.length + 2']
- [reserved uint 64 '0x00000000']
+ [manual string 'UTF-16' 'value' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.parseAmsString", io, stringLength, _type.encoding)' 'STATIC_CALL("org.apache.plc4x.java.ads.utils.StaticHelper.serializeAmsString", io, _value, stringLength, _type.encoding)' '(stringLength * 2) + 2']
]
// -----------------------------------------
@@ -547,10 +545,10 @@
// -----------------------------------------
// Characters & Strings
// -----------------------------------------
- [CHAR ['1', 'IEC61131_CHAR']]
- [WCHAR ['2', 'IEC61131_WCHAR']]
- [STRING ['9', 'IEC61131_STRING']]
- [WSTRING ['18', 'IEC61131_WSTRING']]
+ [CHAR ['1', 'IEC61131_CHAR']]
+ [WCHAR ['2', 'IEC61131_WCHAR']]
+ [STRING ['256', 'IEC61131_STRING']]
+ [WSTRING ['512', 'IEC61131_WSTRING']]
// -----------------------------------------
// Dates & Times
// -----------------------------------------