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
The following commit(s) were added to refs/heads/develop by this push:
new 0379b241c9 fix: [Bug]: The block optimizer used in s7-light causes
errors, if a tag references the same byte multiple times
0379b241c9 is described below
commit 0379b241c92879a763caf53345866736173856be
Author: Christofer Dutz <[email protected]>
AuthorDate: Sun Aug 10 11:26:43 2025 +0200
fix: [Bug]: The block optimizer used in s7-light causes errors, if a tag
references the same byte multiple times
Closes: #2213
---
.../readwrite/optimizer/S7BlockReadOptimizer.java | 46 ++++++++++++-----
.../java/s7/readwrite/ManualS71200DriverTest2.java | 58 ++++++++++++++++++++++
2 files changed, 91 insertions(+), 13 deletions(-)
diff --git
a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7light/readwrite/optimizer/S7BlockReadOptimizer.java
b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7light/readwrite/optimizer/S7BlockReadOptimizer.java
index e6d0119b76..a17f8639a9 100644
---
a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7light/readwrite/optimizer/S7BlockReadOptimizer.java
+++
b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7light/readwrite/optimizer/S7BlockReadOptimizer.java
@@ -37,6 +37,7 @@ import
org.apache.plc4x.java.spi.messages.utils.DefaultPlcTagItem;
import org.apache.plc4x.java.spi.messages.utils.PlcResponseItem;
import org.apache.plc4x.java.spi.messages.utils.PlcTagItem;
import org.apache.plc4x.java.spi.values.DefaultPlcValueHandler;
+import org.apache.plc4x.java.spi.values.PlcBOOL;
import org.apache.plc4x.java.spi.values.PlcNull;
import org.apache.plc4x.java.spi.values.PlcRawByteArray;
import org.slf4j.Logger;
@@ -391,25 +392,44 @@ public class S7BlockReadOptimizer extends S7Optimizer {
try {
int stringLength = (tag instanceof S7StringFixedLengthTag) ?
((S7StringFixedLengthTag) tag).getStringLength() : 254;
if (tag.getNumberOfElements() == 1) {
- return DataItem.staticParse(readBuffer,
tag.getDataType().getDataProtocolId(),
- s7DriverContext.getControllerType(), stringLength);
+ // If a boolean is being read, we need to manually parse it as
we are reading bytes and not single bits.
+ if(tag.getDataType() == TransportSize.BOOL) {
+ boolean bitValue = ((data[0] >> tag.getBitOffset()) &
0x01) != 0;
+ return PlcBOOL.of(bitValue);
+ } else {
+ return DataItem.staticParse(readBuffer,
tag.getDataType().getDataProtocolId(),
+ s7DriverContext.getControllerType(), stringLength);
+ }
} else {
// In case of reading an array of bytes, make use of our
simpler PlcRawByteArray as the user is
// probably expecting to process the read raw data.
if(tag.getDataType() == TransportSize.BYTE) {
return new PlcRawByteArray(data);
} else {
- // Fetch all
- final PlcValue[] resultItems = IntStream.range(0,
tag.getNumberOfElements()).mapToObj(i -> {
- try {
- return DataItem.staticParse(readBuffer,
tag.getDataType().getDataProtocolId(),
- s7DriverContext.getControllerType(),
stringLength);
- } catch (ParseException e) {
- logger.warn("Error parsing tag item of type: '{}'
(at position {}})", tag.getDataType().name(), i, e);
- }
- return null;
- }).toArray(PlcValue[]::new);
- return DefaultPlcValueHandler.of(tag, resultItems);
+ // If a boolean is being read, we need to manually parse
it as we are reading bytes and not single bits.
+ if(tag.getDataType() == TransportSize.BOOL) {
+ int rootBitOffset = tag.getBitOffset();
+ final PlcValue[] resultItems = IntStream.range(0,
tag.getNumberOfElements()).mapToObj(i -> {
+ int bitOffset = rootBitOffset + i;
+ int byteOffset = bitOffset / 8;
+ bitOffset = bitOffset % 8;
+ boolean bitValue = ((data[byteOffset] >>
bitOffset) & 0x01) != 0;
+ return PlcBOOL.of(bitValue);
+ }).toArray(PlcValue[]::new);
+ return DefaultPlcValueHandler.of(tag, resultItems);
+ } else {
+ // Fetch all
+ final PlcValue[] resultItems = IntStream.range(0,
tag.getNumberOfElements()).mapToObj(i -> {
+ try {
+ return DataItem.staticParse(readBuffer,
tag.getDataType().getDataProtocolId(),
+ s7DriverContext.getControllerType(),
stringLength);
+ } catch (ParseException e) {
+ logger.warn("Error parsing tag item of type:
'{}' (at position {}})", tag.getDataType().name(), i, e);
+ }
+ return null;
+ }).toArray(PlcValue[]::new);
+ return DefaultPlcValueHandler.of(tag, resultItems);
+ }
}
}
} catch (ParseException e) {
diff --git
a/plc4j/drivers/s7/src/test/java/org/apache/plc4x/java/s7/readwrite/ManualS71200DriverTest2.java
b/plc4j/drivers/s7/src/test/java/org/apache/plc4x/java/s7/readwrite/ManualS71200DriverTest2.java
new file mode 100644
index 0000000000..acd2347bbe
--- /dev/null
+++
b/plc4j/drivers/s7/src/test/java/org/apache/plc4x/java/s7/readwrite/ManualS71200DriverTest2.java
@@ -0,0 +1,58 @@
+/*
+ * 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.s7.readwrite;
+
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.spi.values.*;
+import org.apache.plc4x.test.manual.ManualTest;
+
+import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class ManualS71200DriverTest2 extends ManualTest {
+
+ /*
+ * Test program code on the PLC with the test-data.
+ * Reading the states of the 5 input booleans. Used to test how multiple
bits located in one byte are handled.
+ */
+
+ public ManualS71200DriverTest2(String connectionString) {
+ super(connectionString, true, true, true, true, 100);
+ }
+
+ public static void main(String[] args) throws Exception {
+ ManualS71200DriverTest2 test = new
ManualS71200DriverTest2("s7://192.168.23.30");
+ test.addTestCase("%I0.0:BOOL", new PlcBOOL(false));
+ test.addTestCase("%I0.1:BOOL", new PlcBOOL(true));
+ test.addTestCase("%I0.2:BOOL", new PlcBOOL(true));
+ test.addTestCase("%I0.3:BOOL", new PlcBOOL(false));
+ test.addTestCase("%I0.4:BOOL", new PlcBOOL(false));
+ //test.addTestCase("%I0.0:BOOL[5]", new PlcList(new
ArrayList<>(List.of(new PlcBOOL(false), new PlcBOOL(true), new PlcBOOL(true),
new PlcBOOL(false), new PlcBOOL(false)))));
+
+ long start = System.currentTimeMillis();
+ test.run();
+ long end = System.currentTimeMillis();
+ System.out.printf("Finished in %d ms", end - start);
+ }
+
+}