You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by ld...@apache.org on 2022/08/10 11:18:50 UTC

[plc4x] branch splatch/ads-symbol-discovery created (now 0072c3fd2)

This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a change to branch splatch/ads-symbol-discovery
in repository https://gitbox.apache.org/repos/asf/plc4x.git


      at 0072c3fd2 chore(ads): Worked on the browse functionality

This branch includes the following new commits:

     new 7f1f52fd4 Prototype of ADS symbol scanner.
     new 0072c3fd2 chore(ads): Worked on the browse functionality

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[plc4x] 02/02: chore(ads): Worked on the browse functionality

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch splatch/ads-symbol-discovery
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 0072c3fd2776577baf506fa8317c08b5e6affaa6
Author: christoferdutz <ch...@c-ware.de>
AuthorDate: Wed Aug 3 08:46:28 2022 +0200

    chore(ads): Worked on the browse functionality
---
 .../plc4x/java/ads/protocol/AdsProtocolLogic.java  | 98 +++++++++++++++++++++-
 .../ads/AdsDiscoverySerializerParserTest.java      |  2 +-
 .../org/apache/plc4x/protocol/ads/AdsDriverIT.java |  4 +-
 .../protocol/ads/ProbeAdsDiscoveryCommands.java    |  8 +-
 .../resources/protocols/ads/ads-discovery.mspec    | 20 ++---
 .../ads/src/main/resources/protocols/ads/ads.mspec | 86 +++++++++++++++++++
 .../resources/protocols/ads/DriverTestsuite.xml    | 76 ++++++-----------
 7 files changed, 222 insertions(+), 72 deletions(-)

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 77d9b18af..0d4128e85 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
@@ -96,8 +96,102 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
 
     @Override
     public void onConnect(ConversationContext<AmsTCPPacket> context) {
-        // AMS/ADS doesn't know a concept of a "connect".
-        context.fireConnected();
+        LOGGER.debug("Fetching sizes of symbol and datatype table sizes.");
+        final CompletableFuture<Void> future = new CompletableFuture<>();
+        List<AdsDataTypeTableEntry> dataTypes = new ArrayList<>();
+        List<AdsSymbolTableEntry> symbols = new ArrayList<>();
+        // Initialize the request.
+        AdsData adsData = new AdsReadRequest(AdsSignificantGroupAddresses.TABLE_SIZES.getValue(), 0x00000000, 24);
+        AmsPacket amsPacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+            configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
+            CommandId.ADS_READ, DEFAULT_COMMAND_STATE, 0, getInvokeId(), adsData);
+        AmsTCPPacket amsTCPPacket = new AmsTCPPacket(amsPacket);
+        // Start a new request-transaction (Is ended in the response-handler)
+        RequestTransactionManager.RequestTransaction transaction = tm.startRequest();
+        transaction.submit(() -> context.sendRequest(amsTCPPacket)
+            .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+            .onTimeout(future::completeExceptionally)
+            .onError((p, e) -> future.completeExceptionally(e))
+            .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == amsPacket.getInvokeId())
+            .unwrap(response -> (AdsReadResponse) response.getUserdata().getData())
+            .handle(responseAdsData -> {
+                if (responseAdsData.getResult() == ReturnCode.OK) {
+                    ReadBuffer readBuffer = new ReadBufferByteBased(responseAdsData.getData());
+                    try {
+                        AdsTableSizes adsTableSizes = AdsTableSizes.staticParse(readBuffer);
+                        LOGGER.info("PLC contains {} symbols and {} datatypes", adsTableSizes.getSymbolCount(), adsTableSizes.getDataTypeCount());
+
+                        AdsData adsReadSymbolTableData = new AdsReadRequest(AdsSignificantGroupAddresses.SYMBOL_TABLE.getValue(), 0x00000000, adsTableSizes.getSymbolLength());
+                        AmsPacket amsReadSymbolTablePacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+                            configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
+                            CommandId.ADS_READ, DEFAULT_COMMAND_STATE, 0, getInvokeId(), adsReadSymbolTableData);
+                        AmsTCPPacket amsReadSymbolTableTCPPacket = new AmsTCPPacket(amsReadSymbolTablePacket);
+                        transaction.submit(() -> context.sendRequest(amsReadSymbolTableTCPPacket)
+                            .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+                            .onTimeout(future::completeExceptionally)
+                            .onError((p, e) -> future.completeExceptionally(e))
+                            .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == amsReadSymbolTablePacket.getInvokeId())
+                            .unwrap(response -> (AdsReadResponse) response.getUserdata().getData())
+                            .handle(responseAdsReadSymbolTableData -> {
+                                if (responseAdsData.getResult() == ReturnCode.OK) {
+                                    ReadBuffer rb2 = new ReadBufferByteBased(responseAdsReadSymbolTableData.getData());
+                                    for (int i = 0; i < adsTableSizes.getSymbolCount(); i++) {
+                                        try {
+                                            AdsSymbolTableEntry adsSymbolTableEntry = AdsSymbolTableEntry.staticParse(rb2);
+                                            System.out.println(adsSymbolTableEntry);
+                                            symbols.add(adsSymbolTableEntry);
+                                        } catch (ParseException e) {
+                                            throw new RuntimeException(e);
+                                        }
+                                    }
+                                    future.complete(null);
+                                }
+                            }));
+
+                        // TODO: Now we load the symbol-table and the datatype definitions.
+                        /*AdsData adsReadTypeTableData = new AdsReadRequest(AdsSignificantGroupAddresses.DATA_TYPE_TABLE.getValue(), 0x00000000, adsTableSizes.getDataTypeLength());
+                        AmsPacket amsReadTablePacket = new AmsPacket(configuration.getTargetAmsNetId(), configuration.getTargetAmsPort(),
+                            configuration.getSourceAmsNetId(), configuration.getSourceAmsPort(),
+                            CommandId.ADS_READ, DEFAULT_COMMAND_STATE, 0, getInvokeId(), adsReadTypeTableData);
+                        AmsTCPPacket amsReadTableTCPPacket = new AmsTCPPacket(amsReadTablePacket);
+                        transaction.submit(() -> context.sendRequest(amsReadTableTCPPacket)
+                            .expectResponse(AmsTCPPacket.class, Duration.ofMillis(configuration.getTimeoutRequest()))
+                            .onTimeout(future::completeExceptionally)
+                            .onError((p, e) -> future.completeExceptionally(e))
+                            .check(responseAmsPacket -> responseAmsPacket.getUserdata().getInvokeId() == amsReadTablePacket.getInvokeId())
+                            .unwrap(response -> (AdsReadResponse) response.getUserdata().getData())
+                            .handle(responseAdsReadTableData -> {
+                                if (responseAdsData.getResult() == ReturnCode.OK) {
+                                    // Parse the result.
+                                    ReadBuffer rb = new ReadBufferByteBased(responseAdsReadTableData.getData());
+                                    while (rb.hasMore(8)) {
+                                        try {
+                                            AdsDataTypeTableEntry adsDataTypeTableEntry = AdsDataTypeTableEntry.staticParse(rb);
+                                            System.out.println(adsDataTypeTableEntry);
+                                            dataTypes.add(adsDataTypeTableEntry);
+                                        } catch (ParseException e) {
+                                            throw new RuntimeException(e);
+                                        }
+                                    }
+                                }
+                            }));*/
+                    } catch (ParseException e) {
+                        future.completeExceptionally(new PlcException("Error loading the table sizes", e));
+                    }
+                } else {
+                    // TODO: Implement this correctly.
+                    future.completeExceptionally(new PlcException("Result is " + responseAdsData.getResult()));
+                }
+                // Finish the request-transaction.
+                transaction.endRequest();
+            }));
+        future.whenComplete((unused, throwable) -> {
+            if(throwable != null) {
+                LOGGER.error("Error fetching symbol and datatype table sizes");
+            } else {
+                context.fireConnected();
+            }
+        });
     }
 
     @Override
diff --git a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/AdsDiscoverySerializerParserTest.java b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/AdsDiscoverySerializerParserTest.java
index 77b0d1ac7..0bda07b2b 100644
--- a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/AdsDiscoverySerializerParserTest.java
+++ b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/AdsDiscoverySerializerParserTest.java
@@ -24,7 +24,7 @@ import org.junit.jupiter.api.Disabled;
 public class AdsDiscoverySerializerParserTest extends ParserSerializerTestsuiteRunner {
 
     public AdsDiscoverySerializerParserTest() {
-        super("/protocols/ads/AdsDiscoverySerializerTest.xml", true);
+        super("/protocols/ads/AdsDiscoverySerializerTest.xml");
     }
 
 }
diff --git a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/AdsDriverIT.java b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/AdsDriverIT.java
index 46c440588..973231ce1 100644
--- a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/AdsDriverIT.java
+++ b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/AdsDriverIT.java
@@ -19,11 +19,13 @@
 package org.apache.plc4x.protocol.ads;
 
 import org.apache.plc4x.test.driver.DriverTestsuiteRunner;
+import org.junit.jupiter.api.Disabled;
 
+@Disabled("I have to port the commands for reading the symbol-table first")
 public class AdsDriverIT extends DriverTestsuiteRunner {
 
     public AdsDriverIT() {
-        super("/protocols/ads/DriverTestsuite.xml");
+        super("/protocols/ads/DriverTestsuite.xml", true);
     }
 
 }
diff --git a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ProbeAdsDiscoveryCommands.java b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ProbeAdsDiscoveryCommands.java
index 241255d2c..051341849 100644
--- a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ProbeAdsDiscoveryCommands.java
+++ b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/ProbeAdsDiscoveryCommands.java
@@ -26,20 +26,22 @@ import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.util.Arrays;
+import java.util.Collections;
 
 public class ProbeAdsDiscoveryCommands {
 
     public static void main(String[] args) {
         // Create the discovery request message for this device.
         AmsNetId amsNetId = new AmsNetId((byte) 192, (byte) 168, (byte) 23, (byte) 200, (byte) 1, (byte) 1);
-        AdsDiscovery discoveryRequestMessage = new AdsDiscovery(1, Operation.ADD_OR_UPDATE_ROUTE_REQUEST, amsNetId, AdsPortNumbers.SYSTEM_SERVICE,
+        AdsDiscovery discoveryRequestMessage = new AdsDiscovery(1, Operation.UNKNOWN_REQUEST, amsNetId, AdsPortNumbers.SYSTEM_SERVICE,
             //Collections.emptyList()
             Arrays.asList(
-                new AdsDiscoveryBlockRouteName(new AmsString("route-name")),
+                /*new AdsDiscoveryBlockRouteName(new AmsString("route-name")),
                 new AdsDiscoveryBlockAmsNetId(amsNetId),
                 new AdsDiscoveryBlockUserName(new AmsString("username")),
                 new AdsDiscoveryBlockPassword(new AmsString("password")),
-                new AdsDiscoveryBlockHostName(new AmsString("host-name-or-ip"))
+                new AdsDiscoveryBlockHostName(new AmsString("host-name-or-ip"))*/
+                new AdsDiscoveryBlockAmsNetId(new AmsNetId((byte) 192, (byte) 168, (byte) 23, (byte) 20, (byte) 1, (byte) 1))
             ));
 
         try (DatagramSocket adsDiscoverySocket = new DatagramSocket(AdsDiscoveryConstants.ADSDISCOVERYUDPDEFAULTPORT)) {
diff --git a/protocols/ads/src/main/resources/protocols/ads/ads-discovery.mspec b/protocols/ads/src/main/resources/protocols/ads/ads-discovery.mspec
index 25b73f3a3..c49624e3a 100644
--- a/protocols/ads/src/main/resources/protocols/ads/ads-discovery.mspec
+++ b/protocols/ads/src/main/resources/protocols/ads/ads-discovery.mspec
@@ -113,27 +113,19 @@
 // without any blocks for any of the "UNKNOWN" request codes.
 // However all responses do contain the remote AmsNetId.
 [enum uint 32 Operation
-    ['0x00000000' UNKNOWN_1_REQUEST           ]
-    ['0x80000000' UNKNOWN_1_RESPONSE          ]
     ['0x00000001' DISCOVERY_REQUEST           ]
     ['0x80000001' DISCOVERY_RESPONSE          ]
-    ['0x00000002' UNKNOWN_2_REQUEST           ]
-    ['0x80000002' UNKNOWN_2_RESPONSE          ]
-    ['0x00000003' UNKNOWN_3_REQUEST           ]
-    ['0x80000003' UNKNOWN_3_RESPONSE          ]
-    ['0x00000004' UNKNOWN_4_REQUEST           ]
-    ['0x80000004' UNKNOWN_4_RESPONSE          ]
-    ['0x00000005' UNKNOWN_5_REQUEST           ]
-    ['0x80000005' UNKNOWN_5_RESPONSE          ]
     // Update information for an entry with the same AmsNetId
     ['0x00000006' ADD_OR_UPDATE_ROUTE_REQUEST ]
     ['0x80000006' ADD_OR_UPDATE_ROUTE_RESPONSE]
     ['0x00000007' DEL_ROUTE_REQUEST           ]
     ['0x80000007' DEL_ROUTE_RESPONSE          ]
-    ['0x00000008' UNKNOWN_8_REQUEST           ]
-    ['0x80000008' UNKNOWN_8_RESPONSE          ]
-    ['0x00000009' UNKNOWN_9_REQUEST           ]
-    ['0x80000009' UNKNOWN_9_RESPONSE          ]
+    // In contrast to the other message, I get an status block back when using this,
+    // just as if I was adding a route with invalid credentials. So possibly we might
+    // have something here ... I would guess something like a Enumerate Routes command
+    // For which I have seen in the documentation of TwinCat 2 and 3.
+    ['0x00000008' UNKNOWN_REQUEST             ]
+    ['0x80000008' UNKNOWN_RESPONSE            ]
 ]
 
 [enum uint 32 Status
diff --git a/protocols/ads/src/main/resources/protocols/ads/ads.mspec b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
index f8b69bb1d..a5d6bde5f 100644
--- a/protocols/ads/src/main/resources/protocols/ads/ads.mspec
+++ b/protocols/ads/src/main/resources/protocols/ads/ads.mspec
@@ -735,3 +735,89 @@
     ['0x274D' WSAECONNREFUSED]
     ['0x2751' WSAEHOSTUNREACH]
 ]
+
+// https://github.com/Beckhoff/ADS/blob/master/AdsLib/standalone/AdsDef.h
+// https://gitlab.com/xilix-systems-llc/go-native-ads/-/blob/master/ads.go#L145
+// https://gitlab.com/xilix-systems-llc/go-native-ads/-/blob/master/connection.go#L109
+// https://gitlab.com/xilix-systems-llc/go-native-ads/-/blob/master/symbols.go#L222
+[enum uint 32 AdsSignificantGroupAddresses
+    ['0x0000F00B' SYMBOL_TABLE   ]
+    ['0x0000F00E' DATA_TYPE_TABLE]
+    ['0x0000F00F' TABLE_SIZES    ]
+]
+
+[type AdsTableSizes byteOrder='LITTLE_ENDIAN'
+	[simple   uint 32 symbolCount   ]
+	[simple   uint 32 symbolLength  ]
+	[simple   uint 32 dataTypeCount ]
+	[simple   uint 32 dataTypeLength]
+	[simple   uint 32 extraCount    ]
+	[simple   uint 32 extraLength   ]
+]
+
+[type AdsSymbolTableEntry byteOrder='LITTLE_ENDIAN'
+  	[simple   uint 32                      entryLength                           ]
+    [simple   uint 32                      group                                 ]
+    [simple   uint 32                      offset                                ]
+    [simple   uint 32                      size                                  ]
+    [simple   uint 32                      dataType                              ]
+    // Start: Flags
+    // https://github.com/jisotalo/ads-server/blob/master/src/ads-commons.ts#L631
+    // Order of the bits if read Little-Endian and then accessing the bit flags
+    // 7 6 5 4 3 2 1 0  |  15 14 13 12 11 10 9 8  |  23 22 21 20 19 18 17 16 | 31 30 29 28 27 26 25 24
+    [simple   bit                          flagMethodDeref                       ]
+    [simple   bit                          flagItfMethodAccess                   ]
+    [simple   bit                          flagReadOnly                          ]
+    [simple   bit                          flagTComInterfacePointer              ]
+    [simple   bit                          flagTypeGuid                          ]
+    [simple   bit                          flagReferenceTo                       ]
+    [simple   bit                          flagBitValue                          ]
+    [simple   bit                          flagPersistent                        ]
+    [reserved uint 3                       '0x00'                                ]
+    [simple   bit                          flagExtendedFlags                     ]
+    [simple   bit                          flagInitOnReset                       ]
+    [simple   bit                          flagStatic                            ]
+    [simple   bit                          flagAttributes                        ]
+    [simple   bit                          flagContextMask                       ]
+    [reserved uint 16                      '0x0000'                              ]
+    // End: Flags
+    [implicit uint 16                      nameLength         'STR_LEN(name)'    ]
+    [implicit uint 16                      typeNameLength     'STR_LEN(typeName)']
+    [implicit uint 16                      commentLength      'STR_LEN(comment)' ]
+	[simple   vstring 'nameLength * 8'     name                                  ]
+	[const    uint 8                       nameTerminator     0x00               ]
+	[simple   vstring 'typeNameLength * 8' typeName                              ]
+	[const    uint 8                       typeNameTerminator 0x00               ]
+	[simple   vstring 'commentLength * 8'  comment                               ]
+	[const    uint 8                       commentTerminator  0x00               ]
+	// Gobbling up the rest, but it seems there is content in here, when looking
+	// at the data in wireshark, it seems to be related to the flags field.
+	// Will have to continue searching for more details on how to decode this.
+	// I would assume that we'll have some "optional" fields here which depend
+	// on values in the flags section.
+	[array    byte                         rest               count 'entryLength - curPos']
+]
+
+// https://gitlab.com/xilix-systems-llc/go-native-ads/-/blob/master/symbols.go#L15
+[type AdsDataTypeTableEntry byteOrder='LITTLE_ENDIAN'
+	[simple   uint 32                            entryLength                           ]
+	[simple   uint 32                            version                               ]
+	[simple   uint 32                            hashValue                             ]
+	[simple   uint 32                            typeHashValue                         ]
+	[simple   uint 32                            size                                  ]
+	[simple   uint 32                            offs                                  ]
+	[simple   uint 32                            dataType                              ]
+	[simple   uint 32                            flags                                 ]
+	[implicit uint 16                            nameLength         'STR_LEN(name)'    ]
+	[implicit uint 16                            typeNameLength     'STR_LEN(typeName)']
+	[implicit uint 16                            commentLength      'STR_LEN(comment)' ]
+	[simple   uint 16                            arrayDim                              ]
+	[simple   uint 16                            subItems                              ]
+	[simple   vstring '(nameLength - 1) * 8'     name                                  ]
+	[const    uint 8                             nameTerminator     0x00               ]
+	[simple   vstring '(typeNameLength - 1) * 8' typeName                              ]
+	[const    uint 8                             typeNameTerminator 0x00               ]
+	[simple   vstring '(commentLength - 1) * 8'  comment                               ]
+	[const    uint 8                             commentTerminator  0x00               ]
+	//[array    AdsDataTypeTableEntry              children           length               '']
+]
diff --git a/protocols/ads/src/test/resources/protocols/ads/DriverTestsuite.xml b/protocols/ads/src/test/resources/protocols/ads/DriverTestsuite.xml
index fb0aa1c2f..70a6dd51a 100644
--- a/protocols/ads/src/test/resources/protocols/ads/DriverTestsuite.xml
+++ b/protocols/ads/src/test/resources/protocols/ads/DriverTestsuite.xml
@@ -113,9 +113,9 @@
               <data>
                 <AdsData>
                   <AdsReadRequest>
-                    <indexGroup dataType="uint" bitLength="32">4040</indexGroup>
-                    <indexOffset dataType="uint" bitLength="32">8</indexOffset>
-                    <length dataType="uint" bitLength="32">1</length>
+                    <indexGroup dataType="uint" bitLength="32">61455</indexGroup>
+                    <indexOffset dataType="uint" bitLength="32">0</indexOffset>
+                    <length dataType="uint" bitLength="32">24</length>
                   </AdsReadRequest>
                 </AdsData>
               </data>
@@ -238,7 +238,7 @@
       <outgoing-plc-message name="Send Ads Read Request">
         <AmsTCPPacket>
           <reserved dataType="uint" bitLength="16">0</reserved>
-          <length dataType="uint" bitLength="32">72</length>
+          <length dataType="uint" bitLength="32">44</length>
           <userdata>
             <AmsPacket>
               <targetAmsNetId>
@@ -264,7 +264,7 @@
               </sourceAmsNetId>
               <sourceAmsPort dataType="uint" bitLength="16">48898</sourceAmsPort>
               <commandId>
-                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ_WRITE">9</CommandId>
+                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ">2</CommandId>
               </commandId>
               <state>
                 <State>
@@ -280,34 +280,16 @@
                   <reserved dataType="int" bitLength="7">0</reserved>
                 </State>
               </state>
-              <length dataType="uint" bitLength="32">40</length>
+              <length dataType="uint" bitLength="32">12</length>
               <errorCode dataType="uint" bitLength="32">0</errorCode>
               <invokeId dataType="uint" bitLength="32">1</invokeId>
               <data>
                 <AdsData>
-                  <AdsReadWriteRequest>
-                    <indexGroup dataType="uint" bitLength="32">61568</indexGroup>
-                    <indexOffset dataType="uint" bitLength="32">2</indexOffset>
-                    <readLength dataType="uint" bitLength="32">10</readLength>
-                    <writeLength dataType="uint" bitLength="32">24</writeLength>
-                    <items isList="true">
-                      <AdsMultiRequestItem>
-                        <AdsMultiRequestItemRead>
-                          <itemIndexGroup dataType="uint" bitLength="32">4040</itemIndexGroup>
-                          <itemIndexOffset dataType="uint" bitLength="32">8</itemIndexOffset>
-                          <itemReadLength dataType="uint" bitLength="32">1</itemReadLength>
-                        </AdsMultiRequestItemRead>
-                      </AdsMultiRequestItem>
-                      <AdsMultiRequestItem>
-                        <AdsMultiRequestItemRead>
-                          <itemIndexGroup dataType="uint" bitLength="32">4040</itemIndexGroup>
-                          <itemIndexOffset dataType="uint" bitLength="32">12</itemIndexOffset>
-                          <itemReadLength dataType="uint" bitLength="32">1</itemReadLength>
-                        </AdsMultiRequestItemRead>
-                      </AdsMultiRequestItem>
-                    </items>
-                    <data dataType="byte" bitLength="0">0x</data>
-                  </AdsReadWriteRequest>
+                  <AdsReadRequest>
+                    <indexGroup dataType="uint" bitLength="32">61455</indexGroup>
+                    <indexOffset dataType="uint" bitLength="32">0</indexOffset>
+                    <length dataType="uint" bitLength="32">24</length>
+                  </AdsReadRequest>
                 </AdsData>
               </data>
             </AmsPacket>
@@ -441,7 +423,7 @@
       <outgoing-plc-message name="Send Resolve Symbolic Address Request">
         <AmsTCPPacket>
           <reserved dataType="uint" bitLength="16">0</reserved>
-          <length dataType="uint" bitLength="32">74</length>
+          <length dataType="uint" bitLength="32">44</length>
           <userdata>
             <AmsPacket>
               <targetAmsNetId>
@@ -467,7 +449,7 @@
               </sourceAmsNetId>
               <sourceAmsPort dataType="uint" bitLength="16">48898</sourceAmsPort>
               <commandId>
-                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ_WRITE">9</CommandId>
+                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ">2</CommandId>
               </commandId>
               <state>
                 <State>
@@ -483,20 +465,16 @@
                   <reserved dataType="int" bitLength="7">0</reserved>
                 </State>
               </state>
-              <length dataType="uint" bitLength="32">42</length>
+              <length dataType="uint" bitLength="32">12</length>
               <errorCode dataType="uint" bitLength="32">0</errorCode>
               <invokeId dataType="uint" bitLength="32">1</invokeId>
               <data>
                 <AdsData>
-                  <AdsReadWriteRequest>
-                    <indexGroup dataType="uint" bitLength="32">61443</indexGroup>
+                  <AdsReadRequest>
+                    <indexGroup dataType="uint" bitLength="32">61455</indexGroup>
                     <indexOffset dataType="uint" bitLength="32">0</indexOffset>
-                    <readLength dataType="uint" bitLength="32">4</readLength>
-                    <writeLength dataType="uint" bitLength="32">26</writeLength>
-                    <items isList="true">
-                    </items>
-                    <data dataType="byte" bitLength="208">0x6d61696e2e665f74726967446174656947656c6573656e2e4d00</data>
-                  </AdsReadWriteRequest>
+                    <length dataType="uint" bitLength="32">24</length>
+                  </AdsReadRequest>
                 </AdsData>
               </data>
             </AmsPacket>
@@ -737,7 +715,7 @@
       <outgoing-plc-message name="Send Resolve Symbolic Address Request">
         <AmsTCPPacket>
           <reserved dataType="uint" bitLength="16">0</reserved>
-          <length dataType="uint" bitLength="32">74</length>
+          <length dataType="uint" bitLength="32">44</length>
           <userdata>
             <AmsPacket>
               <targetAmsNetId>
@@ -763,7 +741,7 @@
               </sourceAmsNetId>
               <sourceAmsPort dataType="uint" bitLength="16">48898</sourceAmsPort>
               <commandId>
-                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ_WRITE">9</CommandId>
+                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ">2</CommandId>
               </commandId>
               <state>
                 <State>
@@ -779,20 +757,16 @@
                   <reserved dataType="int" bitLength="7">0</reserved>
                 </State>
               </state>
-              <length dataType="uint" bitLength="32">42</length>
+              <length dataType="uint" bitLength="32">12</length>
               <errorCode dataType="uint" bitLength="32">0</errorCode>
               <invokeId dataType="uint" bitLength="32">1</invokeId>
               <data>
                 <AdsData>
-                  <AdsReadWriteRequest>
-                    <indexGroup dataType="uint" bitLength="32">61443</indexGroup>
+                  <AdsReadRequest>
+                    <indexGroup dataType="uint" bitLength="32">61455</indexGroup>
                     <indexOffset dataType="uint" bitLength="32">0</indexOffset>
-                    <readLength dataType="uint" bitLength="32">4</readLength>
-                    <writeLength dataType="uint" bitLength="32">26</writeLength>
-                    <items isList="true">
-                    </items>
-                    <data dataType="byte" bitLength="208">0x6d61696e2e665f74726967446174656947656c6573656e2e4d00</data>
-                  </AdsReadWriteRequest>
+                    <length dataType="uint" bitLength="32">24</length>
+                  </AdsReadRequest>
                 </AdsData>
               </data>
             </AmsPacket>


[plc4x] 01/02: Prototype of ADS symbol scanner.

Posted by ld...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ldywicki pushed a commit to branch splatch/ads-symbol-discovery
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 7f1f52fd4f52fa0072882747d6e3e9eb28faad5f
Author: Łukasz Dywicki <lu...@code-house.org>
AuthorDate: Tue Aug 2 02:25:29 2022 +0200

    Prototype of ADS symbol scanner.
    
    Signed-off-by: Łukasz Dywicki <lu...@code-house.org>
---
 .../org/apache/plc4x/protocol/ads/Scanner.java     | 151 +++++++++++++++++++++
 1 file changed, 151 insertions(+)

diff --git a/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/Scanner.java b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/Scanner.java
new file mode 100644
index 000000000..4c55e0d66
--- /dev/null
+++ b/plc4j/drivers/ads/src/test/java/org/apache/plc4x/protocol/ads/Scanner.java
@@ -0,0 +1,151 @@
+/*
+ * 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.protocol.ads;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.plc4x.java.PlcDriverManager;
+import org.apache.plc4x.java.ads.readwrite.AdsDataType;
+import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.metadata.PlcConnectionMetadata;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Scanner {
+
+    private static Logger logger = LoggerFactory.getLogger(Scanner.class);
+
+    public static void main(String[] args) throws Exception {
+        if (args.length != 5) {
+            System.out.println("Usage: java -jar ... [ip -address] [target ams] [target ams port] [source ams] [source ams port]");
+            System.out.println("All fields are required. AMS route must be created beforehand.");
+            System.out.println("Example parameter sequence:");
+            System.out.println("  192.168.2.251  39.42.54.209.1.1  851  192.168.2.232.1.1  851");
+            return;
+        }
+
+        String targetIp = args[0];
+        String targetAmsId = args[1];
+        String targetAmsPort = args[2];
+
+        String sourceAmsId = args[3];
+        String sourceAmsPort = args[4];
+
+        String connectionString = "ads:tcp://" + targetIp + "?targetAmsNetId=" + targetAmsId + "&targetAmsPort=" + targetAmsPort + "&sourceAmsNetId=" + sourceAmsId + "&sourceAmsPort=" + sourceAmsPort;
+        System.out.println("Launching connection " + connectionString);
+
+        // Establish a connection to the plc using the url provided as first argument
+        try (PlcConnection plcConnection = new PlcDriverManager().getConnection(connectionString)) {
+            PlcConnectionMetadata metadata = plcConnection.getMetadata();
+            System.out.println("read: " + metadata.canRead());
+            System.out.println("write: " + metadata.canWrite());
+
+            // read symbols
+            System.out.println("Reading symbol info");
+            PlcReadRequest.Builder readRequestBuilder = plcConnection.readRequestBuilder();
+            PlcReadRequest request = readRequestBuilder.addItem("SYM_UPLOADINFO2", "0xf00f/0x0:SINT[24]").build();
+            PlcReadResponse rsp = request.execute().get();
+            ByteBuffer buffer = toBuffer(rsp, "SYM_UPLOADINFO2");
+
+            //System.err.println("first answer " + Hex.dump(buffer.array()));
+            int symbolAnswerSize = buffer.getInt(4);
+            System.out.println("Expecting symbol table containing " + symbolAnswerSize + " bytes");
+
+            request = plcConnection.readRequestBuilder().addItem("SYM_UPLOAD", "0xf00b/0x0:SINT[" + symbolAnswerSize + "]").build();
+            PlcReadResponse readResponse = request.execute().get();
+            buffer = toBuffer(readResponse, "SYM_UPLOAD");
+            //System.err.println("second answer " + Hex.dump(buffer.array()));
+
+            System.out.println("      |           PLC4X Field        |          Hex          |                                                            ");
+            System.out.println("##### |          Query Syntax        |   Index   |   Offset  |   Type   | Name                                   | Size (B) | Type | Flag | Comment");
+            System.out.println("------+------------------------------+-----------+-----------+----------+----------------------------------------+----------+------+------+");
+
+            int index = 0;
+            int pos = 0;
+
+            List<String> supportedTypes = Arrays.stream(AdsDataType.values())
+                .map(AdsDataType::name)
+                .map(String::toUpperCase)
+                .map(s -> s + '\0')
+                .collect(Collectors.toList());
+
+            while (buffer.remaining() > 0) {
+                int sectionLen = buffer.getInt();
+
+                int group = buffer.getInt();
+                int offset = buffer.getInt();
+                int symbolSize = buffer.getInt();
+                int symbolType = buffer.getInt();
+                int symbolFlags = buffer.getInt();
+                short nameLength = (short) (buffer.getShort() + 1);
+                short typeLength = (short) (buffer.getShort() + 1);
+                short commentLength = (short) (buffer.getShort() + 1);
+
+                String name= slice(buffer, nameLength);
+                String type = slice(buffer, typeLength);
+                String comment = slice(buffer, commentLength);
+
+                if (supportedTypes.contains(type.toUpperCase())) {
+                    System.out.printf("%5s |", index++);
+                    System.out.printf("%30s |", "0x" + Integer.toHexString(group) + "/0x" + Integer.toHexString(offset) + ":" + type);
+                    System.out.printf("%10s |", "0x" + Integer.toHexString(group));
+                    System.out.printf("%10s |", "0x" + Integer.toHexString(offset));
+                    System.out.printf("%10s |", type);
+                    System.out.printf("%-40s |", name);
+                    System.out.printf("%9s |", symbolSize);
+                    System.out.printf("%5s |", symbolType);
+                    System.out.printf("%5s |", symbolFlags);
+                    System.out.println(comment);
+                }
+
+                pos += sectionLen;
+                buffer.position(pos);
+
+            }
+        }
+    }
+
+    private static String slice(ByteBuffer buffer, short length) {
+        byte[] arr = new byte[length];
+        buffer.get(arr);
+        return new String(arr, StandardCharsets.UTF_8);
+    }
+
+    private static ByteBuffer toBuffer(PlcReadResponse rsp, String fieldName) {
+        System.out.println(rsp.getFieldNames() + " " + rsp.getField(fieldName) + " " + rsp.getResponseCode(fieldName));
+        List<PlcValue> symbols = (List<PlcValue>) rsp.getObject(fieldName);
+        ByteBuffer buffer = ByteBuffer.allocate(symbols.size()).order(ByteOrder.LITTLE_ENDIAN);
+
+        for (PlcValue byteVal : symbols) {
+            byte byteValue = byteVal.getByte();
+            //System.err.println("data " + Hex.encodeHexString(new byte[] {byteValue}));
+            buffer.put(byteValue);
+        }
+        buffer.rewind();
+        return buffer;
+    }
+
+}