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 bcdb1e662d fix: Accepted the patch provided in order to make working 
unconnected requests work. feat: Added a connection parameter to 
"force-unconnected-operation" that forces the use of unconnected operations.
bcdb1e662d is described below

commit bcdb1e662dde89c08daf35799de1c5929ac0a6cc
Author: Christofer Dutz <cd...@apache.org>
AuthorDate: Fri Jul 12 14:09:08 2024 +0200

    fix: Accepted the patch provided in order to make working unconnected 
requests work.
    feat: Added a connection parameter to "force-unconnected-operation" that 
forces the use of unconnected operations.
---
 .../eip/base/configuration/EIPConfiguration.java   |  22 ++-
 .../java/eip/base/protocol/EipProtocolLogic.java   | 175 ++++++++++++---------
 .../logix/ManualEipLogixDriverUnconnectedTest.java |  89 +++++++++++
 3 files changed, 208 insertions(+), 78 deletions(-)

diff --git 
a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/configuration/EIPConfiguration.java
 
b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/configuration/EIPConfiguration.java
index b723942bc7..b926a61d56 100644
--- 
a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/configuration/EIPConfiguration.java
+++ 
b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/configuration/EIPConfiguration.java
@@ -21,22 +21,34 @@ package org.apache.plc4x.java.eip.base.configuration;
 import org.apache.plc4x.java.spi.configuration.PlcConnectionConfiguration;
 import 
org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
 import org.apache.plc4x.java.spi.configuration.annotations.Description;
+import org.apache.plc4x.java.spi.configuration.annotations.Since;
+import 
org.apache.plc4x.java.spi.configuration.annotations.defaults.BooleanDefaultValue;
+import 
org.apache.plc4x.java.spi.configuration.annotations.defaults.IntDefaultValue;
 import org.apache.plc4x.java.spi.generation.ByteOrder;
 
 public class EIPConfiguration implements PlcConnectionConfiguration {
 
     @ConfigurationParameter
     @Description("Without using routing information the backplane defaults to 
1. This is overridden if communicationPath is provided.")
+    @IntDefaultValue(1)
     private int backplane = 1;
 
     @ConfigurationParameter
     @Description("The slot within the backplane the CPU is located.")
+    @IntDefaultValue(0)
     private int slot = 0;
 
-    @ConfigurationParameter
+    @ConfigurationParameter("big-endian")
     @Description("Configure if the connection should be set to transport data 
in Big-Endian format, or not.")
+    @BooleanDefaultValue(true)
     private boolean bigEndian = true;
 
+    @ConfigurationParameter("force-unconnected-operation")
+    @Description("Forces the driver to use unconnected requests.")
+    @BooleanDefaultValue(false)
+    @Since("0.13.0")
+    private boolean forceUnconnectedOperation = false;
+
     public int getBackplane() {
         return backplane;
     }
@@ -61,4 +73,12 @@ public class EIPConfiguration implements 
PlcConnectionConfiguration {
         this.bigEndian = byteOrder == ByteOrder.BIG_ENDIAN;
     }
 
+    public boolean isForceUnconnectedOperation() {
+        return forceUnconnectedOperation;
+    }
+
+    public void setForceUnconnectedOperation(boolean 
forceUnconnectedOperation) {
+        this.forceUnconnectedOperation = forceUnconnectedOperation;
+    }
+
 }
diff --git 
a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/protocol/EipProtocolLogic.java
 
b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/protocol/EipProtocolLogic.java
index ffc3a904de..d5fead1556 100644
--- 
a/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/protocol/EipProtocolLogic.java
+++ 
b/plc4j/drivers/eip/src/main/java/org/apache/plc4x/java/eip/base/protocol/EipProtocolLogic.java
@@ -66,6 +66,7 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
 
     private static final byte[] DEFAULT_SENDER_CONTEXT = "PLC4X   
".getBytes(StandardCharsets.US_ASCII);
     private static final long EMPTY_SESSION_HANDLE = 0L;
+    private static final long EMPTY_INTERFACE_OPTIONS = 0L;
     private static final long EMPTY_INTERFACE_HANDLE = 0L;
     private NullAddressItem nullAddressItem;
     private byte[] senderContext;
@@ -375,7 +376,6 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
             });
     }
 
-
     @Override
     public void onDisconnect(ConversationContext<EipPacket> context) {
         if (this.connectionId != 0L) {
@@ -421,7 +421,6 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
         }
     }
 
-
     public void onDisconnectUnregisterSession(ConversationContext<EipPacket> 
context) {
         logger.debug("Sending Un RegisterSession EIP Package");
 
@@ -443,14 +442,13 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
     private CompletableFuture<PlcReadResponse> 
readWithoutMessageRouter(PlcReadRequest readRequest) {
         CompletableFuture<PlcReadResponse> future = new CompletableFuture<>();
         Map<String, ResponseItem<PlcValue>> values = new HashMap<>();
-
+        List<CompletableFuture<Void>> internalFutures = new ArrayList<>();
         PathSegment classSegment = new LogicalSegment(new ClassID((byte) 0, 
(short) 6));
         PathSegment instanceSegment = new LogicalSegment(new InstanceID((byte) 
0, (short) 1));
 
         DefaultPlcReadRequest request = (DefaultPlcReadRequest) readRequest;
         for (String tagName : request.getTagNames()) {
-            CompletableFuture<Boolean> internalFuture = new 
CompletableFuture<>();
-            RequestTransactionManager.RequestTransaction transaction = 
tm.startRequest();
+            CompletableFuture<Void> internalFuture = new CompletableFuture<>();
             EipTag eipTag = (EipTag) request.getTag(tagName);
             String tag = eipTag.getTag();
 
@@ -466,24 +464,24 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
                     (byte) this.configuration.getBackplane(),
                     (byte) this.configuration.getSlot());
 
-                List<TypeId> typeIds = new ArrayList<>(2);
-
-                typeIds.add(nullAddressItem);
-                typeIds.add(new UnConnectedDataItem(requestItem));
+                List<TypeId> typeIds = Arrays.asList(
+                    nullAddressItem,
+                    new UnConnectedDataItem(requestItem));
 
-                CipRRData pkt = new CipRRData(
+                CipRRData rrdata = new CipRRData(
                     sessionHandle,
                     CIPStatus.Success.getValue(),
                     DEFAULT_SENDER_CONTEXT,
                     0L,
-                    0L,
+                    EMPTY_INTERFACE_HANDLE,
                     0,
                     typeIds);
 
-                transaction.submit(() -> context.sendRequest(pkt)
+                RequestTransactionManager.RequestTransaction transaction = 
tm.startRequest();
+                transaction.submit(() -> context.sendRequest(rrdata)
                     .expectResponse(EipPacket.class, REQUEST_TIMEOUT)
-                    .onTimeout(future::completeExceptionally)
-                    .onError((p, e) -> future.completeExceptionally(e))
+                    .onTimeout(internalFuture::completeExceptionally)
+                    .onError((p, e) -> internalFuture.completeExceptionally(e))
                     .check(p -> p instanceof CipRRData)
                     .unwrap(p -> (CipRRData) p)
                     .check(p -> p.getSessionHandle() == sessionHandle)
@@ -492,18 +490,22 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
                         UnConnectedDataItem dataItem = (UnConnectedDataItem) 
responseTypeIds.get(1);
                         Map<String, ResponseItem<PlcValue>> readResponse = 
decodeSingleReadResponse(dataItem.getService(), tagName, eipTag);
                         values.putAll(readResponse);
-                        internalFuture.complete(true);
-                        // Finish the request-transaction.
+                        internalFuture.complete(null);
                         transaction.endRequest();
                     }));
+                internalFutures.add(internalFuture);
             } catch (SerializationException e) {
-                e.printStackTrace();
+                internalFuture.completeExceptionally(new 
PlcRuntimeException("Failed to read field"));
             }
         }
 
-        // TODO: This seems to be blocking here ... we should probably do this 
asynchronously
-        PlcReadResponse readResponse = new DefaultPlcReadResponse(readRequest, 
values);
-        future.complete(readResponse);
+        CompletableFuture.allOf(internalFutures.toArray(new 
CompletableFuture[0])).thenRun(() -> {
+            PlcReadResponse readResponse = new 
DefaultPlcReadResponse(readRequest, values);
+            future.complete(readResponse);
+        }).exceptionally(e -> {
+            future.completeExceptionally(e);
+            return null;
+        });
 
         return future;
     }
@@ -526,15 +528,21 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
                     toAnsi(tag),
                     1);
 
-                CipUnconnectedRequest unreq = new CipUnconnectedRequest(
+                CipUnconnectedRequest requestItem = new CipUnconnectedRequest(
                     classSegment,
                     instanceSegment,
                     req,
                     (byte) this.configuration.getBackplane(),
                     (byte) this.configuration.getSlot());
-                requests.add(unreq);
+
+                // TODO: Possibly check if adding this would make the 
request/response exceed some
+                //  protocol limits and possibly split up into multiple 
requests.
+                requests.add(requestItem);
             } catch (SerializationException e) {
-                e.printStackTrace();
+                // TODO: Instead of failing the entire request it might be 
better to return a failure
+                //  status for only this item.
+                future.completeExceptionally(new PlcRuntimeException("Failed 
to read field", e));
+                return future;
             }
         }
 
@@ -600,10 +608,14 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
                 CipReadRequest req = new CipReadRequest(
                     toAnsi(tag),
                     1);
-
+                // TODO: Possibly check if adding this would make the 
request/response exceed some
+                //  protocol limits and possibly split up into multiple 
requests.
                 requests.add(req);
             } catch (SerializationException e) {
-                e.printStackTrace();
+                // TODO: Instead of failing the entire request it might be 
better to return a failure
+                //  status for only this item.
+                future.completeExceptionally(new PlcRuntimeException("Failed 
to read field", e));
+                return future;
             }
         }
 
@@ -661,7 +673,7 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
     @Override
     public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) 
{
         CompletableFuture<PlcReadResponse> future;
-        if (!this.useMessageRouter && !this.useConnectionManager) {
+        if (configuration.isForceUnconnectedOperation() || 
(!this.useMessageRouter && !this.useConnectionManager)) {
             future = readWithoutMessageRouter(readRequest);
         } else if (this.useMessageRouter && !this.useConnectionManager) {
             future = readWithoutConnectionManager(readRequest);
@@ -887,74 +899,79 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
     public CompletableFuture<PlcWriteResponse> 
writeWithoutMessageRouter(PlcWriteRequest writeRequest) {
         CompletableFuture<PlcWriteResponse> future = new CompletableFuture<>();
         DefaultPlcWriteRequest request = (DefaultPlcWriteRequest) writeRequest;
-        List<CipWriteRequest> items = new 
ArrayList<>(writeRequest.getNumberOfTags());
+        List<CompletableFuture<Void>> internalFutures = new ArrayList<>();
         PathSegment classSegment = new LogicalSegment(new ClassID((byte) 0, 
(short) 6));
         PathSegment instanceSegment = new LogicalSegment(new InstanceID((byte) 
0, (short) 1));
         Map<String, PlcResponseCode> values = new HashMap<>();
 
         for (String fieldName : writeRequest.getTagNames()) {
+            CompletableFuture<Void> internalFuture = new CompletableFuture<>();
             final EipTag field = (EipTag) request.getTag(fieldName);
             final PlcValue value = request.getPlcValue(fieldName);
             String tag = field.getTag();
             int elements = Math.max(field.getElementNb(), 1);
 
-            byte[] data = encodeValue(value, field.getType());
-            CipWriteRequest writeReq = null;
             try {
-                writeReq = new CipWriteRequest(toAnsi(tag), field.getType(), 
elements, data);
-            } catch (SerializationException e) {
-                e.printStackTrace();
-            }
-            CompletableFuture<Boolean> internalFuture = new 
CompletableFuture<>();
-            RequestTransactionManager.RequestTransaction transaction = 
tm.startRequest();
-
-            tm.startRequest();
+                byte[] data = encodeValue(value, field.getType());
+                CipWriteRequest writeReq = new CipWriteRequest(
+                    toAnsi(tag),
+                    field.getType(),
+                    elements,
+                    data);
 
-            UnConnectedDataItem exchange = new UnConnectedDataItem(
-                new CipUnconnectedRequest(
+                CipUnconnectedRequest requestItem = new CipUnconnectedRequest(
                     classSegment,
                     instanceSegment,
                     writeReq,
                     (byte) configuration.getBackplane(),
-                    (byte) configuration.getSlot()));
+                    (byte) configuration.getSlot());
 
-            List<TypeId> typeIds = Arrays.asList(nullAddressItem, exchange);
+                List<TypeId> typeIds = Arrays.asList(
+                    nullAddressItem,
+                    new UnConnectedDataItem(requestItem));
 
-            CipRRData rrdata = new CipRRData(
-                sessionHandle,
-                0L,
-                senderContext,
-                0L,
-                EMPTY_INTERFACE_HANDLE,
-                0,
-                typeIds);
+                CipRRData rrdata = new CipRRData(
+                    sessionHandle,
+                    0L,
+                    // TODO: Check if this could also be the 
DEFAULT_SENDER_CONTEXT
+                    senderContext,
+                    EMPTY_INTERFACE_OPTIONS,
+                    EMPTY_INTERFACE_HANDLE,
+                    0,
+                    typeIds);
 
-            transaction.submit(() -> context.sendRequest(rrdata)
-                .expectResponse(EipPacket.class, REQUEST_TIMEOUT)
-                .onTimeout(future::completeExceptionally)
-                .onError((p, e) -> future.completeExceptionally(e))
-                .check(p -> p instanceof CipRRData).unwrap(p -> (CipRRData) p)
-                .check(p -> p.getSessionHandle() == sessionHandle)
-                //.check(p -> p.getSenderContext() == senderContext)
-                .check(p -> ((UnConnectedDataItem) 
p.getTypeIds().get(1)).getService() instanceof CipWriteResponse)
-                .unwrap(p -> (CipWriteResponse) ((UnConnectedDataItem) 
p.getTypeIds().get(1)).getService())
-                .handle(p -> {
-                    Map<String, PlcResponseCode> responseItem = 
decodeSingleWriteResponse(p, fieldName);
-                    values.putAll(responseItem);
-                    internalFuture.complete(true);
-                    transaction.endRequest();
-                })
-            );
-            try {
-                internalFuture.get(REQUEST_TIMEOUT.toMillis(), 
TimeUnit.MILLISECONDS);
-            } catch (InterruptedException | ExecutionException | 
TimeoutException e) {
-                future.completeExceptionally(new PlcRuntimeException("Failed 
to read field"));
+                RequestTransactionManager.RequestTransaction transaction = 
tm.startRequest();
+                transaction.submit(() -> context.sendRequest(rrdata)
+                    .expectResponse(EipPacket.class, REQUEST_TIMEOUT)
+                    .onTimeout(internalFuture::completeExceptionally)
+                    .onError((p, e) -> internalFuture.completeExceptionally(e))
+                    .check(p -> p instanceof CipRRData)
+                    .unwrap(p -> (CipRRData) p)
+                    .check(p -> p.getSessionHandle() == sessionHandle)
+                    //.check(p -> p.getSenderContext() == senderContext)
+                    .check(p -> ((UnConnectedDataItem) 
p.getTypeIds().get(1)).getService() instanceof CipWriteResponse)
+                    .unwrap(p -> (CipWriteResponse) ((UnConnectedDataItem) 
p.getTypeIds().get(1)).getService())
+                    .handle(p -> {
+                        Map<String, PlcResponseCode> responseItem = 
decodeSingleWriteResponse(p, fieldName);
+                        values.putAll(responseItem);
+                        internalFuture.complete(null);
+                        transaction.endRequest();
+                    })
+                );
+                internalFutures.add(internalFuture);
+            } catch (SerializationException e) {
+                internalFuture.completeExceptionally(new 
PlcRuntimeException("Failed to read field"));
             }
 
+            CompletableFuture.allOf(internalFutures.toArray(new 
CompletableFuture[0])).thenRun(() -> {
+                PlcWriteResponse readResponse = new 
DefaultPlcWriteResponse(writeRequest, values);
+                future.complete(readResponse);
+            }).exceptionally(e -> {
+                future.completeExceptionally(e);
+                return null;
+            });
         }
-        // TODO: This seems to be blocking here ... we should probably do this 
asynchronously
-        PlcWriteResponse response = new DefaultPlcWriteResponse(writeRequest, 
values);
-        future.complete(response);
+
         return future;
     }
 
@@ -973,9 +990,11 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
                 CipWriteRequest writeReq = new CipWriteRequest(toAnsi(tag), 
field.getType(), elements, data);
                 items.add(writeReq);
             } catch (SerializationException e) {
-                e.printStackTrace();
+                // TODO: Instead of failing the entire request it might be 
better to return a failure
+                //  status for only this item.
+                future.completeExceptionally(new PlcRuntimeException("Failed 
to write field", e));
+                return future;
             }
-
         }
 
         RequestTransactionManager.RequestTransaction transaction = 
tm.startRequest();
@@ -1096,9 +1115,11 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
                 CipWriteRequest writeReq = new CipWriteRequest(toAnsi(tag), 
field.getType(), elements, data);
                 items.add(writeReq);
             } catch (SerializationException e) {
-                e.printStackTrace();
+                // TODO: Instead of failing the entire request it might be 
better to return a failure
+                //  status for only this item.
+                future.completeExceptionally(new PlcRuntimeException("Failed 
to write field", e));
+                return future;
             }
-
         }
 
         RequestTransactionManager.RequestTransaction transaction = 
tm.startRequest();
@@ -1192,7 +1213,7 @@ public class EipProtocolLogic extends 
Plc4xProtocolBase<EipPacket> implements Ha
     @Override
     public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest 
writeRequest) {
         CompletableFuture<PlcWriteResponse> future;
-        if (!this.useMessageRouter && !this.useConnectionManager) {
+        if (configuration.isForceUnconnectedOperation() || 
(!this.useMessageRouter && !this.useConnectionManager)) {
             future = writeWithoutMessageRouter(writeRequest);
         } else if (this.useMessageRouter && !this.useConnectionManager) {
             future = writeWithoutConnectionManager(writeRequest);
diff --git 
a/plc4j/drivers/eip/src/test/java/org/apache/plc4x/java/eip/logix/ManualEipLogixDriverUnconnectedTest.java
 
b/plc4j/drivers/eip/src/test/java/org/apache/plc4x/java/eip/logix/ManualEipLogixDriverUnconnectedTest.java
new file mode 100644
index 0000000000..e0eec4e2eb
--- /dev/null
+++ 
b/plc4j/drivers/eip/src/test/java/org/apache/plc4x/java/eip/logix/ManualEipLogixDriverUnconnectedTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.eip.logix;
+
+import org.apache.plc4x.java.spi.values.PlcBOOL;
+import org.apache.plc4x.java.spi.values.PlcDINT;
+import org.apache.plc4x.java.spi.values.PlcINT;
+import org.apache.plc4x.java.spi.values.PlcREAL;
+import org.apache.plc4x.java.spi.values.PlcSINT;
+import org.apache.plc4x.test.manual.ManualTest;
+
+public class ManualEipLogixDriverUnconnectedTest extends ManualTest {
+
+    /*
+     * Test program code on the PLC with the test-data.
+     *
+     * Located in "main"
+     *
+
+    hurz_BOOL  := TRUE;
+       hurz_BYTE  := 42;
+       hurz_WORD  := 42424;
+       hurz_DWORD := 4242442424;
+       hurz_LWORD := 4242442424242424242;
+       hurz_SINT  := -42;
+       hurz_USINT := 42;
+       hurz_INT   := -2424;
+       hurz_UINT  := 42424;
+       hurz_DINT  := -242442424;
+       hurz_UDINT := 4242442424;
+       hurz_LINT  := -4242442424242424242;
+       hurz_ULINT := 4242442424242424242;
+       hurz_REAL  := 3.14159265359;
+       hurz_LREAL := 2.71828182846;
+       hurz_TIME  := T#1S234MS;
+       hurz_LTIME := LTIME#1000D15H23M12S34MS2US44NS;
+       hurz_DATE  := D#1998-03-28;
+       //hurz_LDATE:LDATE;
+       hurz_TIME_OF_DAY        := TIME_OF_DAY#15:36:30.123;
+       hurz_TOD                := TOD#16:17:18.123;
+       //hurz_LTIME_OF_DAY:LTIME_OF_DAY;
+       //hurz_LTOD:LTOD;
+       hurz_DATE_AND_TIME      := DATE_AND_TIME#1996-05-06-15:36:30;
+       hurz_DT                         := DT#1972-03-29-00:00:00;
+       //hurz_LDATE_AND_TIME:LDATE_AND_TIME;
+       //hurz_LDT:LDT;
+       hurz_STRING                     := 'hurz';
+       hurz_WSTRING            := "wolf";
+
+     *
+     */
+
+    public ManualEipLogixDriverUnconnectedTest(String connectionString) {
+        super(connectionString, true, false, true, 10);
+    }
+
+    public static void main(String[] args) throws Exception {
+        ManualEipLogixDriverUnconnectedTest test = new 
ManualEipLogixDriverUnconnectedTest("logix://192.168.23.40?force-unconnected-operation=true");
+        // This is the very limited number of types my controller supports.
+        test.addTestCase("hurz_BOOL", new PlcBOOL(true));
+        test.addTestCase("hurz_SINT", new PlcSINT(-42));
+        test.addTestCase("hurz_INT", new PlcINT(-2424));
+        test.addTestCase("hurz_DINT", new PlcDINT(-242442424));
+        test.addTestCase("hurz_REAL", new PlcREAL(3.141593F));
+        //test.addTestCase("hurz_UDT", new PlcStruct());
+
+        long start = System.currentTimeMillis();
+        test.run();
+        long end = System.currentTimeMillis();
+        System.out.printf("Finished in %d ms", end - start);
+    }
+
+}

Reply via email to