This is an automated email from the ASF dual-hosted git repository. cdutz pushed a commit to branch feat/improved-s7-opcua-support in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit 3dc0976cf61f8ef1ba3a8ceb95611ecaf24ace84 Author: Christofer Dutz <[email protected]> AuthorDate: Sun Feb 22 19:07:41 2026 +0100 chore: Added a test for my S7-1500 opcua setup and did some minor tweaks to support reading and writing most types on this PLC. --- .../apache/plc4x/java/opcua/OpcuaPlcDriver.java | 7 - .../java/opcua/protocol/OpcuaProtocolLogic.java | 51 ++-- .../opcua/ManualOpcUaS71500NewFWDriverTest.java | 149 ++++++++++++ .../java/spi/values/DefaultPlcValueHandler.java | 18 +- .../java/spi/values/LegacyPlcValueHandler.java | 264 --------------------- 5 files changed, 187 insertions(+), 302 deletions(-) diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java index a1200481b7..6043a3ba0f 100644 --- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java +++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java @@ -29,8 +29,6 @@ import org.apache.plc4x.java.opcua.tag.OpcuaTag; import org.apache.plc4x.java.spi.connection.GeneratedDriverBase; import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer; import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer; -import org.apache.plc4x.java.spi.values.LegacyPlcValueHandler; -import org.apache.plc4x.java.spi.values.PlcValueHandler; import java.util.Collections; import java.util.List; @@ -64,11 +62,6 @@ public class OpcuaPlcDriver extends GeneratedDriverBase<OpcuaAPU> { return Collections.singletonList("tcp"); } - @Override - protected PlcValueHandler getValueHandler() { - return new LegacyPlcValueHandler(); - } - @Override protected boolean canRead() { return true; 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 41d8c94cd7..6b804959db 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 @@ -22,6 +22,7 @@ import static org.apache.plc4x.java.opcua.context.SecureChannel.getX509Certifica import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.time.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.apache.plc4x.java.api.authentication.PlcAuthentication; @@ -50,7 +51,6 @@ import org.apache.plc4x.java.spi.Plc4xProtocolBase; import org.apache.plc4x.java.spi.configuration.HasConfiguration; import org.apache.plc4x.java.spi.connection.PlcTagHandler; import org.apache.plc4x.java.spi.context.DriverContext; -import org.apache.plc4x.java.spi.generation.Message; import org.apache.plc4x.java.spi.messages.*; import org.apache.plc4x.java.spi.messages.utils.DefaultPlcResponseItem; import org.apache.plc4x.java.spi.messages.utils.PlcResponseItem; @@ -59,17 +59,12 @@ import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration; import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionTag; import org.apache.plc4x.java.spi.transaction.RequestTransactionManager; import org.apache.plc4x.java.spi.transaction.RequestTransactionManager.RequestTransaction; -import org.apache.plc4x.java.spi.values.LegacyPlcValueHandler; +import org.apache.plc4x.java.spi.values.DefaultPlcValueHandler; import org.apache.plc4x.java.spi.values.PlcList; -import org.apache.plc4x.java.spi.values.PlcSTRING; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.math.BigInteger; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -319,44 +314,44 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H for (int i = 0; i < length; i++) { tmpValue[i] = array[i] != 0; } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantSByte) { byte[] array = ((VariantSByte) variant).getValue(); - value = LegacyPlcValueHandler.of(tag, array); + value = DefaultPlcValueHandler.of(tag, array); } else if (variant instanceof VariantByte) { List<Short> array = ((VariantByte) variant).getValue(); Short[] tmpValue = array.toArray(new Short[0]); - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantInt16) { List<Short> array = ((VariantInt16) variant).getValue(); Short[] tmpValue = array.toArray(new Short[0]); - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantUInt16) { List<Integer> array = ((VariantUInt16) variant).getValue(); Integer[] tmpValue = array.toArray(new Integer[0]); - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantInt32) { List<Integer> array = ((VariantInt32) variant).getValue(); Integer[] tmpValue = array.toArray(new Integer[0]); - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantUInt32) { List<Long> array = ((VariantUInt32) variant).getValue(); Long[] tmpValue = array.toArray(new Long[0]); - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantInt64) { List<Long> array = ((VariantInt64) variant).getValue(); Long[] tmpValue = array.toArray(new Long[0]); - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantUInt64) { - value = LegacyPlcValueHandler.of(((VariantUInt64) variant).getValue()); + value = DefaultPlcValueHandler.of(tag, ((VariantUInt64) variant).getValue()); } else if (variant instanceof VariantFloat) { List<Float> array = ((VariantFloat) variant).getValue(); Float[] tmpValue = array.toArray(new Float[0]); - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantDouble) { List<Double> array = ((VariantDouble) variant).getValue(); Double[] tmpValue = array.toArray(new Double[0]); - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantString) { int length = ((VariantString) variant).getValue().size(); List<PascalString> stringArray = ((VariantString) variant).getValue(); @@ -364,7 +359,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H for (int i = 0; i < length; i++) { tmpValue[i] = stringArray.get(i).getStringValue(); } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantDateTime) { List<Long> array = ((VariantDateTime) variant).getValue(); int length = array.size(); @@ -372,7 +367,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H for (int i = 0; i < length; i++) { tmpValue[i] = LocalDateTime.ofInstant(Instant.ofEpochMilli(getDateTime(array.get(i))), ZoneOffset.UTC); } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantGuid) { List<GuidValue> array = ((VariantGuid) variant).getValue(); int length = array.size(); @@ -391,7 +386,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H } tmpValue[i] = Long.toHexString(array.get(i).getData1()) + "-" + Integer.toHexString(array.get(i).getData2()) + "-" + Integer.toHexString(array.get(i).getData3()) + "-" + Integer.toHexString(data4) + "-" + Long.toHexString(data5); } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantXmlElement) { int length = ((VariantXmlElement) variant).getValue().size(); List<PascalString> strings = ((VariantXmlElement) variant).getValue(); @@ -399,7 +394,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H for (int i = 0; i < length; i++) { tmpValue[i] = strings.get(i).getStringValue(); } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantLocalizedText) { int length = ((VariantLocalizedText) variant).getValue().size(); List<LocalizedText> strings = ((VariantLocalizedText) variant).getValue(); @@ -409,7 +404,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H tmpValue[i] += strings.get(i).getLocaleSpecified() ? strings.get(i).getLocale().getStringValue() + "|" : ""; tmpValue[i] += strings.get(i).getTextSpecified() ? strings.get(i).getText().getStringValue() : ""; } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantQualifiedName) { int length = ((VariantQualifiedName) variant).getValue().size(); List<QualifiedName> strings = ((VariantQualifiedName) variant).getValue(); @@ -417,7 +412,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H for (int i = 0; i < length; i++) { tmpValue[i] = "ns=" + strings.get(i).getNamespaceIndex() + ";s=" + strings.get(i).getName().getStringValue(); } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantExtensionObject) { int length = ((VariantExtensionObject) variant).getValue().size(); List<ExtensionObject> strings = ((VariantExtensionObject) variant).getValue(); @@ -425,7 +420,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H for (int i = 0; i < length; i++) { tmpValue[i] = strings.get(i).toString(); } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantNodeId) { int length = ((VariantNodeId) variant).getValue().size(); List<NodeId> strings = ((VariantNodeId) variant).getValue(); @@ -433,7 +428,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H for (int i = 0; i < length; i++) { tmpValue[i] = strings.get(i).toString(); } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantStatusCode) { int length = ((VariantStatusCode) variant).getValue().size(); List<StatusCode> strings = ((VariantStatusCode) variant).getValue(); @@ -441,7 +436,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H for (int i = 0; i < length; i++) { tmpValue[i] = strings.get(i).toString(); } - value = LegacyPlcValueHandler.of(tmpValue); + value = DefaultPlcValueHandler.of(tag, tmpValue); } else if (variant instanceof VariantByteString) { PlcList plcList = new PlcList(); List<ByteStringArray> array = ((VariantByteString) variant).getValue(); @@ -451,7 +446,7 @@ public class OpcuaProtocolLogic extends Plc4xProtocolBase<OpcuaAPU> implements H for (int i = 0; i < length; i++) { tmpValue[i] = byteStringArray.getValue().get(i); } - plcList.add(LegacyPlcValueHandler.of(tmpValue)); + plcList.add(DefaultPlcValueHandler.of(tag, tmpValue)); } value = plcList; } diff --git a/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualOpcUaS71500NewFWDriverTest.java b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualOpcUaS71500NewFWDriverTest.java new file mode 100644 index 0000000000..9b7050b038 --- /dev/null +++ b/plc4j/drivers/opcua/src/test/java/org/apache/plc4x/java/opcua/ManualOpcUaS71500NewFWDriverTest.java @@ -0,0 +1,149 @@ +/* + * 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.opcua; + +import org.apache.plc4x.java.spi.values.*; +import org.apache.plc4x.test.manual.ManualTest; + +import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; + +public class ManualOpcUaS71500NewFWDriverTest extends ManualTest { + + public ManualOpcUaS71500NewFWDriverTest(String connectionString) { + super(connectionString, true, true, true, true, 100); + } + + public static void main(String[] args) throws Exception { + boolean testArrays = false; + ManualOpcUaS71500NewFWDriverTest test = new ManualOpcUaS71500NewFWDriverTest("opcua://192.168.24.66:4840"); + test.addTestCase(/*"g_b1",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_b1\";BOOL", new PlcBOOL(true)); + test.addTestCase(/*"g_b8",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_b8\";BYTE", new PlcBYTE(0xAB)); + test.addTestCase(/*"g_s8",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_s8\";SINT", new PlcSINT(-12)); + test.addTestCase(/*"g_u8",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_u8\";USINT", new PlcUSINT(250)); + // TODO: Getting "INVALID_ADDRESS", as WORD is not part of OpcuaDataType +// test.addTestCase(/*"g_b16",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_b16\";WORD", new PlcWORD(0xBEEF)); + test.addTestCase(/*"g_s16",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_s16\";INT", new PlcINT(-1234)); + test.addTestCase(/*"g_u16",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_u16\";UINT", new PlcUINT(54321)); + // TODO: Getting "INVALID_ADDRESS", as DWORD is not part of OpcuaDataType +// test.addTestCase(/*"g_b32",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_b32\";DWORD", new PlcDWORD(0xDEADBEEFL)); + test.addTestCase(/*"g_s32",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_s32\";DINT", new PlcDINT(-12345678)); + test.addTestCase(/*"g_u32",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_u32\";UDINT", new PlcUDINT(305419896)); + // TODO: Getting "INVALID_ADDRESS", as LWORD is not part of OpcuaDataType +// test.addTestCase(/*"g_b64",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_b64\";LWORD", new PlcLWORD(0x0123_4567_89AB_CDEFL)); + test.addTestCase(/*"g_s64",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_s64\";LINT", new PlcLINT(-9223372036854770000L)); + // TODO: This seems to write different values to the plc. +// test.addTestCase(/*"g_u64",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_u64\";ULINT", new PlcULINT(new BigDecimal("18446744073709551000"))); + test.addTestCase(/*"g_r32",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_r32\";REAL", new PlcREAL(3.14159)); + test.addTestCase(/*"g_r64",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_r64\";LREAL", new PlcLREAL(2.71828182845905)); + // TODO: Writing of TIME doesn't seem to be supported. +// test.addTestCase(/*"g_tim",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_tim\";TIME", new PlcTIME(2500)); // Is returned as Int32 + // TODO: IEC 61131-3 date types use 1990-01-01 as epoch, PlcDATE etc. use Unix epoch (1970-01-01). +// test.addTestCase(/*"g_dat",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_dat\";DATE", new PlcDATE(LocalDate.of(2025, 11, 12))); // Is returned as UInt16 + // TODO: S7/IEC value is milliseconds since midnight +// test.addTestCase(/*"g_timoday",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_timoday\";TIME_OF_DAY", new PlcTIME_OF_DAY(LocalTime.of(14, 33, 21, 250000000))); // Is returned as UInt32 + // TODO: Getting a class cast exception here +// test.addTestCase(/*"g_dattim",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_dattim\";DATE_AND_TIME", new PlcDATE_AND_LTIME(LocalDateTime.of(2025, 11, 12, 14, 33, 21, 500_000_000))); + + test.addTestCase(/*"g_str",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_str\";STRING", new PlcSTRING("Hello PLC4X")); + // TODO: Getting "INVALID_ADDRESS", as WSTRING is not part of OpcuaDataType +// test.addTestCase(/*"g_wstr",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_wstr\";WSTRING", new PlcWSTRING("Grüße von PLC4X")); + if(testArrays) { + test.addTestCase(/*"g_arrBool",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrBool\"", new PlcList(List.of( + new PlcBOOL(true), new PlcBOOL(false), new PlcBOOL(true), new PlcBOOL(true), + new PlcBOOL(false), new PlcBOOL(false), new PlcBOOL(true), new PlcBOOL(false)) + )); + test.addTestCase(/*"g_arrByte",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrByte\"", new PlcList(List.of( + new PlcBYTE(0xDE), new PlcBYTE(0xAD), new PlcBYTE(0xBE), new PlcBYTE(0xEF), + new PlcBYTE(0x12), new PlcBYTE(0x34), new PlcBYTE(0x56), new PlcBYTE(0x78)) + )); + test.addTestCase(/*"g_arrInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrInt\"", new PlcList(List.of( + new PlcINT(-3), new PlcINT(-1), new PlcINT(0), new PlcINT(1), new PlcINT(3)) + )); + test.addTestCase(/*"g_arrUInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrUInt\"", new PlcList(List.of( + new PlcUINT(1), new PlcUINT(10), new PlcUINT(100), new PlcUINT(1000), new PlcUINT(10000)) + )); + test.addTestCase(/*"g_arrDInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrDInt\"", new PlcList(List.of( + new PlcDINT(-1000), new PlcDINT(0), new PlcDINT(1000), new PlcDINT(2000000)) + )); + test.addTestCase(/*"g_arrUDInt",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrUDInt\"", new PlcList(List.of( + new PlcUDINT(0), new PlcUDINT(1), new PlcUDINT(0xFFFF), new PlcUDINT(0x12345678)) + )); + test.addTestCase(/*"g_arrLReal",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrLReal\"", new PlcList(List.of( + new PlcLREAL(1.5), new PlcLREAL(-2.0), new PlcLREAL(0.125)) + )); + test.addTestCase(/*"g_arrTime",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrTime\";TIME", new PlcList(List.of( + new PlcTIME(Duration.ofMillis(10)), new PlcTIME(Duration.ofSeconds(1)), new PlcTIME(Duration.ofSeconds(10))) + )); + test.addTestCase(/*"g_arrString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrString\"", new PlcList(List.of( + new PlcSTRING("alpha"), new PlcSTRING("beta"), new PlcSTRING("gamma")) + )); + test.addTestCase(/*"g_arrWString",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_arrWString\"", new PlcList(List.of( + new PlcWSTRING("Äpfel"), new PlcWSTRING("Öl")) + )); + test.addTestCase(/*"g_matI16_2x3",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_matI16_2x3\"", new PlcList(List.of( + new PlcList(List.of( + new PlcINT(10), new PlcINT(11), new PlcINT(12) + )), + new PlcList(List.of( + new PlcINT(-10), new PlcINT(-11), new PlcINT(-12) + ))) + )); + test.addTestCase(/*"g_matR32_3x2",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_matR32_3x2\"", new PlcList(List.of( + new PlcList(List.of( + new PlcREAL(1.0), new PlcREAL(1.5) + )), + new PlcList(List.of( + new PlcREAL(2.0), new PlcREAL(2.5) + )), + new PlcList(List.of( + new PlcREAL(3.0), new PlcREAL(3.5) + ))) + )); + test.addTestCase(/*"g_cubeU16_2x2x2",*/ "ns=3;s=\"OPC_UA_DB\".\"OPC Data\".\"g_cubeU16_2x2x2\"", new PlcList(List.of( + new PlcList(List.of( + new PlcList(List.of( + new PlcUINT(1), new PlcUINT(2) + )), + new PlcList(List.of( + new PlcUINT(3), new PlcUINT(4) + )) + )), + new PlcList(List.of( + new PlcList(List.of( + new PlcUINT(5), new PlcUINT(6) + )), + new PlcList(List.of( + new PlcUINT(7), new PlcUINT(8) + )) + ))) + )); + } + + long start = System.currentTimeMillis(); + test.run(); + long end = System.currentTimeMillis(); + System.out.printf("Finished in %d ms", end - start); + } + +} diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/DefaultPlcValueHandler.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/DefaultPlcValueHandler.java index 1cd73fdbc6..125cb18c9d 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/DefaultPlcValueHandler.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/DefaultPlcValueHandler.java @@ -26,6 +26,7 @@ import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.types.PlcValueType; import org.apache.plc4x.java.api.value.PlcValue; +import java.lang.reflect.Array; import java.math.BigInteger; import java.time.Duration; import java.time.LocalDate; @@ -82,7 +83,7 @@ public class DefaultPlcValueHandler implements PlcValueHandler { } private static PlcValue ofElements(PlcValueType type, List<ArrayInfo> arrayInfos, Object[] values) { - ArrayInfo arrayInfo = arrayInfos.get(0); + ArrayInfo arrayInfo = arrayInfos.getFirst(); if(values.length == 1) { if(values[0] instanceof Object[]) { @@ -91,8 +92,7 @@ public class DefaultPlcValueHandler implements PlcValueHandler { values = ((Collection<?>) values[0]).toArray(); } else if(values[0] instanceof PlcList) { values = ((PlcList) values[0]).getList().toArray(); - } else if(values[0] instanceof PlcRawByteArray) { - PlcRawByteArray plcRawByteArray = (PlcRawByteArray) values[0]; + } else if(values[0] instanceof PlcRawByteArray plcRawByteArray) { if(plcRawByteArray.getRaw().length != arrayInfo.getSize()) { throw new PlcRuntimeException(String.format("Expecting %d items, but got %d", arrayInfo.getSize(), plcRawByteArray.getRaw().length)); } @@ -126,6 +126,18 @@ public class DefaultPlcValueHandler implements PlcValueHandler { if(type == null) { return of(value); } + + // If this is not a List type, and we're passing in an array or a collection with exactly one element, + // get that element instead. + if (type != PlcValueType.List) { + if(value.getClass().isArray() && (Array.getLength(value) == 1)) { + value = Array.get(value, 0); + } + else if((value instanceof Collection<?> col) && (col.size() == 1)) { + value = col.iterator().next(); + } + } + switch (type) { case BOOL: return PlcBOOL.of(value); diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/LegacyPlcValueHandler.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/LegacyPlcValueHandler.java deleted file mode 100644 index 0343bc4434..0000000000 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/LegacyPlcValueHandler.java +++ /dev/null @@ -1,264 +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 - * - * 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.spi.values; - -import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; -import org.apache.plc4x.java.api.exceptions.PlcUnsupportedDataTypeException; -import org.apache.plc4x.java.api.model.PlcTag; -import org.apache.plc4x.java.api.value.PlcValue; - -import java.math.BigInteger; -import java.time.Duration; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.List; - -public class LegacyPlcValueHandler implements PlcValueHandler { - - public PlcValue newPlcValue(Object value) { - return of(new Object[]{value}); - } - - public PlcValue newPlcValue(Object[] values) { - return of(values); - } - - public PlcValue newPlcValue(PlcTag tag, Object value) { - return of(tag, new Object[]{value}); - } - - public PlcValue newPlcValue(PlcTag tag, Object[] values) { - return of(tag, values); - } - - public static PlcValue of(Object value) { - return of(new Object[]{value}); - } - - public static PlcValue of(List<?> value) { - return of(value.toArray()); - } - - public static PlcValue of(Object[] values) { - if (values.length != 1) { - PlcList list = new PlcList(); - for (Object value : values) { - list.add(of(new Object[]{value})); - } - return list; - } - Object value = values[0]; - if (value instanceof Boolean) { - return PlcBOOL.of(value); - } - if (value instanceof Byte) { - return PlcSINT.of(value); - } - if (value instanceof byte[]) { - return PlcRawByteArray.of(value); - } - if (value instanceof Short) { - return PlcINT.of(value); - } - if (value instanceof Integer) { - return PlcDINT.of(value); - } - if (value instanceof Long) { - return PlcLINT.of(value); - } - if (value instanceof BigInteger) { - return PlcLINT.of(value); - } - if (value instanceof Float) { - return PlcREAL.of(value); - } - if (value instanceof Double) { - return PlcLREAL.of(value); - } - if (value instanceof Duration) { - return new PlcTIME((Duration) value); - } - if (value instanceof LocalTime) { - return new PlcTIME_OF_DAY((LocalTime) value); - } - if (value instanceof LocalDate) { - return new PlcDATE((LocalDate) value); - } - if (value instanceof LocalDateTime) { - return new PlcDATE_AND_TIME((LocalDateTime) value); - } - if (value instanceof String) { - return new PlcSTRING((String) value); - } - if (value instanceof PlcValue) { - return (PlcValue) value; - } - throw new PlcUnsupportedDataTypeException("Data Type " + value.getClass() - + " Is not supported"); - } - - - public static PlcValue of(PlcTag tag, Object value) { - return of(tag, new Object[]{value}); - } - - - public static PlcValue of(PlcTag tag, Object[] values) { - if (values.length == 1) { - Object value = values[0]; - if(tag.getPlcValueType() == null) { - // TODO: This is a hacky shortcut .. - if(value instanceof PlcValue) { - return (PlcValue) value; - } - return new PlcNull(); - } - if (value instanceof PlcValue) { - PlcValue plcValue = (PlcValue) value; - if (plcValue.getPlcValueType() == tag.getPlcValueType()) { - return (PlcValue) value; - } else { - throw new PlcRuntimeException("Expected PlcValue of type " + tag.getPlcValueType().name() + " but got " + plcValue.getPlcValueType().name()); - } - } - switch (tag.getPlcValueType()) { - case BOOL: - return PlcBOOL.of(value); - case BYTE: - if(value instanceof Short) { - return new PlcBYTE((short) value); - } else if(value instanceof Integer) { - return new PlcBYTE(((Integer) value).shortValue()); - } else if(value instanceof Long) { - return new PlcBYTE(((Long) value).shortValue()); - } else if(value instanceof BigInteger) { - return new PlcBYTE(((BigInteger) value).shortValue()); - } else if(value instanceof String) { - try { - return new PlcBYTE(Short.valueOf((String) value)); - } catch (NumberFormatException e) { - throw new PlcRuntimeException("Value of " + value + " not parseable as Byte"); - } - } - throw new PlcRuntimeException("BYTE requires short"); - case SINT: - return PlcSINT.of(value); - case USINT: - return PlcUSINT.of(value); - case INT: - return PlcINT.of(value); - case UINT: - return PlcUINT.of(value); - case WORD: - if(value instanceof Short) { - return new PlcWORD((int) value); - } else if(value instanceof Integer) { - return new PlcWORD((int) value); - } else if(value instanceof Long) { - return new PlcWORD(((Long) value).intValue()); - } else if(value instanceof BigInteger) { - return new PlcWORD(((BigInteger) value).intValue()); - } else if(value instanceof String) { - try { - return new PlcWORD(Integer.valueOf((String) value)); - } catch (NumberFormatException e) { - throw new PlcRuntimeException("Value of " + value + " not parseable as Integer"); - } - } - throw new PlcRuntimeException("WORD requires int"); - case DINT: - return PlcDINT.of(value); - case UDINT: - return PlcUDINT.of(value); - case DWORD: - if(value instanceof Short) { - return new PlcDWORD((long) value); - } else if(value instanceof Integer) { - return new PlcDWORD((long) value); - } else if(value instanceof Long) { - return new PlcDWORD((long) value); - } else if(value instanceof BigInteger) { - return new PlcDWORD(((BigInteger) value).longValue()); - } else if(value instanceof String) { - try { - return new PlcDWORD(Long.valueOf((String) value)); - } catch (NumberFormatException e) { - throw new PlcRuntimeException("Value of " + value + " not parseable as Long"); - } - } - throw new PlcRuntimeException("DWORD requires long"); - case LINT: - return PlcLINT.of(value); - case ULINT: - return PlcULINT.of(value); - case LWORD: - if(value instanceof Short) { - return new PlcLWORD(BigInteger.valueOf((long) value)); - } else if(value instanceof Integer) { - return new PlcLWORD(BigInteger.valueOf((long) value)); - } else if(value instanceof Long) { - return new PlcLWORD(BigInteger.valueOf((long) value)); - } else if(value instanceof BigInteger) { - return new PlcLWORD((BigInteger) value); - } else if(value instanceof String) { - try { - return new PlcLWORD(new BigInteger((String) value)); - } catch (NumberFormatException e) { - throw new PlcRuntimeException("Value of " + value + " not parseable as BigInteger"); - } - } - throw new PlcRuntimeException("LWORD requires BigInteger"); - case REAL: - return PlcREAL.of(value); - case LREAL: - return PlcLREAL.of(value); - case CHAR: - return PlcCHAR.of(value); - case WCHAR: - return PlcWCHAR.of(value); - case STRING: - return PlcSTRING.of(value); - case WSTRING: - return PlcWSTRING.of(value); - case TIME: - return PlcTIME.of(value); - case DATE: - return PlcDATE.of(value); - case TIME_OF_DAY: - return PlcTIME_OF_DAY.of(value); - case DATE_AND_TIME: - return PlcDATE_AND_TIME.of(value); - default: - return customDataType(new Object[]{value}); - } - } else { - PlcList list = new PlcList(); - for (Object value : values) { - list.add(of(tag, new Object[]{value})); - } - return list; - } - } - - public static PlcValue customDataType(Object[] values) { - return of(values); - } -}
