You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by hu...@apache.org on 2022/10/06 15:23:45 UTC

[plc4x] branch plc4j/profinet created (now e99d97c6b)

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

hutcheb pushed a change to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git


      at e99d97c6b fix(plc4j/profinet): Continued to split device logic out.

This branch includes the following new commits:

     new afeb1a491 fix(plc4j/profinet): Fix to remove spaces from the generated connection string.
     new b55c5d225 fix(plc4j(profinet): Connections are working for my setup
     new bf77f0d95 fix(plc4j(profinet): Add Dummy Advanced Connection Write Request
     new 83becf059 fix(plc4j(profinet): Started to add the LLDP broadcast
     new bb01815f8 fix(plc4j(profinet): Continued to add the LLDP broadcast
     new d133842ce fix(plc4j(profinet): Finished the connection setup.
     new dcdad3137 feat(plc4j/profinet): Fixed a few minor issues and added the Application Ready packet
     new 6f5b27b2b feat(plc4j/profinet): Escape spaces using %20
     new d1c18db45 feat(plc4j/): split out dcp and lldp tasks, so they can be processed separately.
     new 40d4cc626 fix(plc4j/profinet): Identified that the Application Ready request comes from the device.
     new f90225bd4 fix(plc4j/profinet): Cleaned up the message send and receive interface
     new 36b3260cc chore(plc4j/profinet): escape the device name and type within the connection string
     new e99d97c6b fix(plc4j/profinet): Continued to split device logic out.

The 13 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] 06/13: fix(plc4j(profinet): Finished the connection setup.

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit d133842cef1e39ece146eab300435b6edfe94987
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Fri Sep 9 11:20:59 2022 -0600

    fix(plc4j(profinet): Finished the connection setup.
---
 .../profinet/discovery/ProfinetPlcDiscoverer.java  | 144 +++++++++++++++++----
 .../profinet/protocol/ProfinetProtocolLogic.java   |  51 +++++++-
 .../profinet/readwrite/utils/StaticHelper.java     |  33 +++++
 .../resources/protocols/profinet/profinet.mspec    |  58 ++++++---
 4 files changed, 243 insertions(+), 43 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index 96b2dba67..35681f795 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -18,6 +18,8 @@
  */
 package org.apache.plc4x.java.profinet.discovery;
 
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
 import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryItemHandler;
@@ -47,6 +49,8 @@ import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.function.Function;
+import java.util.function.IntBinaryOperator;
 
 public class ProfinetPlcDiscoverer implements PlcDiscoverer {
 
@@ -56,6 +60,8 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
     // The constants for the different block names and their actual meaning.
     private static final String DEVICE_TYPE_NAME = "DEVICE_PROPERTIES_OPTION-1";
     private static final String DEVICE_NAME_OF_STATION = "DEVICE_PROPERTIES_OPTION-2";
+    private static final String PLC4X_LLDP_IDENTIFIER = "PLC4X PROFINET Controller Client";
+    private static final String PLC4X_LLDP_PORT = "port001.plc4x";
     private static final String DEVICE_ID = "DEVICE_PROPERTIES_OPTION-3";
     private static final String DEVICE_ROLE = "DEVICE_PROPERTIES_OPTION-4";
     private static final String DEVICE_OPTIONS = "DEVICE_PROPERTIES_OPTION-5";
@@ -327,14 +333,14 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
                                         ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
                                         try {
                                             Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
-                                            PnDcp_Pdu pdu;
+                                            Lldp_Pdu pdu;
                                             // Access the pdu data (either directly or by
                                             // unpacking the content of the VLAN packet.
                                             if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
                                                 Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
-                                                pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
+                                                pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
                                             } else {
-                                                pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
+                                                pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
                                             }
                                             // Inspect the PDU itself
                                             // (in this case we only process identify response packets)
@@ -351,28 +357,102 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
                         Task t = new Task(handle, listener);
                         pool.execute(t);
 
-                        // Construct and send the LLDP Probe
+                        Function<Object, Boolean> lldpTimer =
+                            message -> {
+                                // Construct and send the LLDP Probe
+                                TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
+                                    new TlvProfibusSubTypePortStatus(0x00)
+                                );
+
+                                TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
+                                    new TlvProfibusSubTypeChassisMac(new MacAddress(linkLayerAddress.getAddress()))
+                                );
+
+                                TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
+                                    (short) 0x01,
+                                    (short) 0x03,
+                                    0x0020,
+                                    0x0010
+                                );
+
+                                Ethernet_Frame identificationRequest = null;
+                                try {
+                                    identificationRequest = new Ethernet_Frame(
+                                        // Pre-Defined LLDP discovery MAC address
+                                        new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
+                                        toPlc4xMacAddress(macAddress),
+                                        new Ethernet_FramePayload_LLDP(
+                                            new Lldp_Pdu(
+                                                Arrays.asList(
+                                                    new TlvChassisId(
+                                                        PLC4X_LLDP_IDENTIFIER.length() + 1,
+                                                        (short) 7,
+                                                        PLC4X_LLDP_IDENTIFIER
+                                                    ),
+                                                    new TlvPortId(
+                                                        PLC4X_LLDP_PORT.length() + 1,
+                                                        (short) 7,
+                                                        PLC4X_LLDP_PORT
+                                                    ),
+                                                    new TlvTimeToLive(2, 20),
+                                                    new TlvOrganizationSpecific(
+                                                        portStatus.getLengthInBytes(),
+                                                        portStatus
+                                                    ),
+                                                    new TlvOrganizationSpecific(
+                                                        chassisMac.getLengthInBytes(),
+                                                        chassisMac
+                                                    ),
+                                                    new TlvOrganizationSpecific(
+                                                        ieee.getLengthInBytes(),
+                                                        ieee
+                                                    ),
+                                                    new TlvManagementAddress(
+                                                        12,
+                                                        ManagementAddressSubType.IPV4,
+                                                        new IpAddress(Hex.decodeHex("c0a8006e")),
+                                                        (short) 0x03,
+                                                        0x01L,
+                                                        (short) 0x00
+                                                    ),
+                                                    new EndOfLldp(0)
+                                                )
+                                            )));
+                                } catch (DecoderException e) {
+                                    throw new RuntimeException(e);
+                                }
+                                WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
+                                try {
+                                    identificationRequest.serialize(buffer);
+                                } catch (SerializationException e) {
+                                    throw new RuntimeException(e);
+                                }
+                                Packet packet = null;
+                                try {
+                                    packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
+                                } catch (IllegalRawDataException e) {
+                                    throw new RuntimeException(e);
+                                }
+                                try {
+                                    handle.sendPacket(packet);
+                                } catch (PcapNativeException e) {
+                                    throw new RuntimeException(e);
+                                } catch (NotOpenException e) {
+                                    throw new RuntimeException(e);
+                                }
+                                return null;
+                            };
+                        Timer timer = new Timer();
 
-                        Ethernet_Frame identificationRequest = new Ethernet_Frame(
-                            // Pre-Defined PROFINET discovery MAC address
-                            new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
-                            toPlc4xMacAddress(macAddress),
-                            new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
-                                new Ethernet_FramePayload_PnDcp(
-                                    new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
-                                        1,
-                                        256,
-                                        Collections.singletonList(
-                                            new PnDcp_Block_ALLSelector()
-                                        )))));
-                        WriteBufferByteBased buffer = new WriteBufferByteBased(34);
-                        identificationRequest.serialize(buffer);
-                        Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
-                        handle.sendPacket(packet);
+                        // Schedule to run after every 3 second(3000 millisecond)
+                        timer.scheduleAtFixedRate(
+                            new LLDPTask(handle, lldpTimer),
+                            3000,
+                            3000);
                     }
                 }
             }
-        } catch (IllegalRawDataException | NotOpenException | PcapNativeException | SerializationException e) {
+        } catch (NotOpenException | PcapNativeException e) {
             logger.error("Got an exception while processing raw socket data", e);
 
             for (PcapHandle openHandle : openHandles) {
@@ -411,10 +491,28 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         }
     }
 
+    private static class LLDPTask extends TimerTask {
+
+        private final Logger logger = LoggerFactory.getLogger(Task.class);
+
+        private final PcapHandle handle;
+        private final Function<Object, Boolean> operator;
+
+        public LLDPTask(PcapHandle handle, Function<Object, Boolean> operator) {
+            this.handle = handle;
+            this.operator = operator;
+        }
+
+        @Override
+        public void run() {
+            operator.apply(null);
+        }
+    }
+
     public static void main(String[] args) throws Exception {
         ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer();
-        discoverer.discover(null);
-
+        //discoverer.discover(null);
+        discoverer.lldpProbe();
         Thread.sleep(10000);
     }
 
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
index 8e4e666ab..d3c1d1024 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
@@ -184,6 +184,31 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
 
             udpSocket.send(connectRequestPacket);
 
+            // Receive the response.
+            resultBuffer = new byte[profinetAdvancedConnectionWriteRequest.getLengthInBytes()];
+            connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
+            udpSocket.receive(connectResponsePacket);
+
+
+            // Create the packet
+            final DceRpc_Packet profinetAdvancedConnectionParameterEnd = createProfinetAdvancedConnectionParameterEnd();
+            // Serialize it to a byte-payload
+            writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionParameterEnd.getLengthInBytes());
+            profinetAdvancedConnectionParameterEnd.serialize(writeBuffer);
+            // Create a udp packet.
+            connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
+            connectRequestPacket.setAddress(remoteAddress.getAddress());
+            connectRequestPacket.setPort(remoteAddress.getPort());
+            // Send it.
+
+            udpSocket.send(connectRequestPacket);
+
+            // Receive the response.
+            resultBuffer = new byte[profinetAdvancedConnectionParameterEnd.getLengthInBytes()];
+            connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
+            udpSocket.receive(connectResponsePacket);
+
+
         } catch (SerializationException | IOException | PlcException | ParseException e) {
             logger.error("Error", e);
         }
@@ -255,7 +280,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                             // This actually needs to be set to this value and not the real port number.
                             0x8892,
                             // It seems that it must be set to this value, or it won't work.
-                            "controller"),
+                            "plc4x"),
                         new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.INPUT_CR,
                             0x0001,
                             0x8892,
@@ -383,13 +408,35 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                             (short) 1,
                             (short) 0,
                             new PascalString("port-001"),
-                            new PascalString("controller")
+                            new PascalString("plc4x")
                         )
                     )
                 ))
         );
     }
 
+    private DceRpc_Packet createProfinetAdvancedConnectionParameterEnd() throws PlcException {
+
+        return new DceRpc_Packet(
+            DceRpc_PacketType.REQUEST, true, false, false,
+            IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
+            new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
+            new DceRpc_InterfaceUuid_DeviceInterface(),
+            profinetDriverContext.getDceRpcActivityUuid(),
+            0, 1, DceRpc_Operation.CONTROL,
+            new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+                Arrays.asList(
+                    new PnIoCm_Control_Request(
+                        (short) 1,
+                        (short) 0,
+                        ARUUID,
+                        0x0001,
+                        0x0001
+                    )
+                ))
+        );
+    }
+
     protected static DceRpc_ActivityUuid generateActivityUuid() {
         UUID number = UUID.randomUUID();
         try {
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/readwrite/utils/StaticHelper.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/readwrite/utils/StaticHelper.java
index 97678c957..7331df69b 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/readwrite/utils/StaticHelper.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/readwrite/utils/StaticHelper.java
@@ -19,7 +19,11 @@
 package org.apache.plc4x.java.profinet.readwrite.utils;
 
 import org.apache.plc4x.java.profinet.readwrite.IpAddress;
+import org.apache.plc4x.java.profinet.readwrite.LldpUnit;
 import org.apache.plc4x.java.profinet.readwrite.PnDcp_FrameId;
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.List;
 
 public class StaticHelper {
 
@@ -153,4 +157,33 @@ public class StaticHelper {
         return PnDcp_FrameId.RESERVED;
     }
 
+    public static boolean isSysexEnd(ReadBuffer io) {
+        byte[] test = ((ReadBufferByteBased) io).getBytes(io.getPos(), io.getPos() + 2);
+        return ((ReadBufferByteBased) io).getBytes(io.getPos(), io.getPos() + 2)[0] == (byte) 0x00;
+    }
+
+    public static LldpUnit parseSysexString(ReadBuffer io) {
+        try {
+            LldpUnit unit = LldpUnit.staticParse(io);
+            return unit;
+        } catch (ParseException e) {
+            return null;
+        }
+    }
+
+    public static void serializeSysexString(WriteBuffer io, LldpUnit unit) {
+        try {
+            unit.serialize(io);
+        } catch (SerializationException e) {
+        }
+    }
+
+    public static int lengthSysexString(List<LldpUnit> data) {
+        int lengthInBytes = 0;
+        for (LldpUnit unit : data) {
+            lengthInBytes += unit.getLengthInBytes();
+        }
+        return lengthInBytes;
+    }
+
 }
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 19dcdcfee..57f3ced3a 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -83,34 +83,34 @@
             [simple PnDcp_Pdu             pdu                                                        ]
         ]
         ['0x88cc' Ethernet_FramePayload_LLDP
-            [simple Lldp_Pdu             pdu                                                        ]
+            [simple Lldp_Pdu      pdu                                               ]
         ]
     ]
 ]
 
 [type Lldp_Pdu
-    [array      LldpUnit                lldpParameters]
+    [manualArray LldpUnit lldpParameters terminated 'STATIC_CALL("isSysexEnd", readBuffer)' 'STATIC_CALL("parseSysexString", readBuffer)' 'STATIC_CALL("serializeSysexString", writeBuffer, _value)' 'STATIC_CALL("lengthSysexString", lldpParameters)']
 ]
 
 [discriminatedType LldpUnit
-    [disciminator     uint 7                  tlvId                               ]
-    [implicit   uint 9                  tlvIdLength       'lengthInBytes'         ]                      ]
-    [typeSwitch idSubType
+    [discriminator     TlvType                  tlvId                                ]
+    [simple            uint 9                   tlvIdLength                          ]
+    [typeSwitch tlvId
         ['END_OF_LLDP'  EndOfLldp
-        [
-        ['CHASSIS_ID'   TlvChassisId
-            [simple     uint 8          chassisIdSubType                           ]
-            [simple     vstring '(tlvIdLength * 8) +  1' chassisId                 ]
         ]
-        ['PORT_ID'   TlvPortId
+        ['CHASSIS_ID'   TlvChassisId(uint 9 tlvIdLength)
+            [simple     uint 8                        chassisIdSubType               ]
+            [simple     vstring     '(tlvIdLength - 1) * 8' chassisId                      ]
+        ]
+        ['PORT_ID'   TlvPortId(uint 9 tlvIdLength)
             [simple     uint 8          portIdSubType                              ]
-            [simple     vstring '(tlvIdLength * 8) +  1' portId                    ]
+            [simple     vstring     '(tlvIdLength - 1) * 8' portId                           ]
         ]
-        ['PORT_ID'   TlvPortId
-            [simple     uint 16         tlvTimeToLive                              ]
+        ['TIME_TO_LIVE'   TlvTimeToLive
+            [simple     uint 16         tlvTimeToLiveUnit                          ]
         ]
         ['MANAGEMENT_ADDRESS' TlvManagementAddress
-            [implicit   uint 8          addressStringLength                        ]
+            [implicit   uint 8          addressStringLength    '5' ]
             [simple     ManagementAddressSubType  addressSubType                   ]
             [simple     IpAddress       ipAddress                                  ]
             [simple     uint 8          interfaceSubType                           ]
@@ -121,15 +121,15 @@
             [simple     TlvOrganizationSpecificUnit     organizationSpecificUnit   ]
         ]
     ]
-[
+]
 
-[type TlvOrganizationSpecificUnit(uint 9 unitLength)
+[discriminatedType TlvOrganizationSpecificUnit
     [discriminator      uint 24         uniqueCode]
     [typeSwitch uniqueCode
         ['0x000ECF' TlvOrgSpecificProfibus
             [simple     TlvOrgSpecificProfibusUnit      specificUnit               ]
         ]
-        [´0x00120F' TlvOrgSpecificIeee8023
+        ['0x00120F' TlvOrgSpecificIeee8023
             [simple     uint 8                          subType                    ]
             [simple     uint 8                          negotiationSupport         ]
             [simple     uint 16                         negotiationCapability      ]
@@ -141,12 +141,18 @@
 [discriminatedType TlvOrgSpecificProfibusUnit
     [discriminator  TlvProfibusSubType  subType]
     [typeSwitch subType
+        ['PORT_STATUS'  TlvProfibusSubTypePortStatus
+            [simple     uint 16                         rtClassPortStatus]
+        ]
+        ['CHASSIS_MAC'  TlvProfibusSubTypeChassisMac
+            [simple     MacAddress                      macAddress]
+        ]
     ]
 ]
 
 [enum   TlvProfibusSubType
     ['0x02' PORT_STATUS]
-    [´0x05' CHASSIS_MAC]
+    ['0x05' CHASSIS_MAC]
 ]
 
 // 4.10.3.2
@@ -823,6 +829,22 @@
             [simple   MacAddress             cmResponderMacAddr                                     ]
             [simple   uint 16                responderUDPRTPort                                     ]
         ]
+        ['IOD_CONTROL_REQ' PnIoCm_Control_Request
+            [reserved uint 16                         '0x0000'                                         ]
+            [simple   Uuid                            arUuid                                                 ]
+            [simple   uint 16                         sessionKey                                             ]
+            [reserved uint 16                         '0x0000'                                         ]
+            [simple   uint 16                         controlCommand                                         ]
+            [reserved uint 16                         '0x0000'                                         ]
+        ]
+        ['IOD_CONTROL_RES' PnIoCm_Control_Response
+            [reserved uint 16                         '0x0000'                                         ]
+            [simple   Uuid                            arUuid                                                 ]
+            [simple   uint 16                         sessionKey                                             ]
+            [reserved uint 16                         '0x0000'                                         ]
+            [simple   uint 16                         controlCommand                                         ]
+            [reserved uint 16                         '0x0000'                                         ]
+        ]
         ['IO_CR_BLOCK_REQ' PnIoCm_Block_IoCrReq
             [simple PnIoCm_IoCrType          ioCrType                                               ]
             [simple uint 16                  ioCrReference                                          ]


[plc4x] 11/13: fix(plc4j/profinet): Cleaned up the message send and receive interface

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit f90225bd43321ff7c9f014e6fd73344148b54fbe
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 18 13:30:24 2022 -0600

    fix(plc4j/profinet): Cleaned up the message send and receive interface
---
 .../java/api/messages/PlcDiscoveryItemHandler.java |   2 +
 .../profinet/config/ProfinetConfiguration.java     |  23 ++
 .../java/profinet/device/ProfinetCallable.java     |  10 +
 .../plc4x/java/profinet/device/ProfinetDevice.java | 301 +++++++++++++++++++++
 .../device/ProfinetDeviceMessageHandler.java       |  51 ++++
 .../profinet/device/ProfinetMessageWrapper.java    |  48 ++++
 .../profinet/protocol/ProfinetProtocolLogic.java   | 133 ++++-----
 .../resources/protocols/profinet/profinet.mspec    |   8 +
 8 files changed, 511 insertions(+), 65 deletions(-)

diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java
index bb2bb0196..9aa33d4ee 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java
@@ -18,6 +18,8 @@
  */
 package org.apache.plc4x.java.api.messages;
 
+import java.util.List;
+
 public interface PlcDiscoveryItemHandler {
 
     void handle(PlcDiscoveryItem discoveryItem);
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/config/ProfinetConfiguration.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/config/ProfinetConfiguration.java
index ee3c0c1c3..e262b7ce6 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/config/ProfinetConfiguration.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/config/ProfinetConfiguration.java
@@ -18,10 +18,21 @@
  */
 package org.apache.plc4x.java.profinet.config;
 
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.profinet.device.ProfinetDevice;
+import org.apache.plc4x.java.profinet.readwrite.MacAddress;
 import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
+import org.apache.plc4x.java.spi.configuration.annotations.defaults.BooleanDefaultValue;
+import org.apache.plc4x.java.spi.configuration.annotations.defaults.StringDefaultValue;
 import org.apache.plc4x.java.transport.rawsocket.RawSocketTransportConfiguration;
 import org.apache.plc4x.java.utils.pcap.netty.handlers.PacketHandler;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
 public class ProfinetConfiguration implements Configuration, RawSocketTransportConfiguration {
 
     @Override
@@ -44,6 +55,18 @@ public class ProfinetConfiguration implements Configuration, RawSocketTransportC
         return null;
     }
 
+    @ConfigurationParameter("devices")
+    @StringDefaultValue("")
+    private String devices;
+
+    public HashMap<MacAddress, ProfinetDevice> configuredDevices = new HashMap<>();
+
+    public void setDevices(String sDevices) throws DecoderException {
+        // TODO:- Add support for passing in configured devices.
+        MacAddress macAddress = new MacAddress(Hex.decodeHex("005056c00001"));
+        configuredDevices.put(macAddress, new ProfinetDevice(macAddress));
+    }
+
     @Override
     public String toString() {
         return "Configuration{" +
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java
new file mode 100644
index 000000000..fd117c9f4
--- /dev/null
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java
@@ -0,0 +1,10 @@
+package org.apache.plc4x.java.profinet.device;
+
+import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.profinet.readwrite.DceRpc_Packet;
+
+public interface ProfinetCallable {
+    void handle(DceRpc_Packet packet) throws PlcException;
+
+    DceRpc_Packet create() throws PlcException;
+}
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java
new file mode 100644
index 000000000..80f10d961
--- /dev/null
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java
@@ -0,0 +1,301 @@
+/*
+ * 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.profinet.device;
+
+import io.netty.channel.Channel;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
+import org.apache.plc4x.java.profinet.protocol.ProfinetProtocolLogic;
+import org.apache.plc4x.java.profinet.readwrite.*;
+import org.apache.plc4x.java.spi.ConversationContext;
+import org.apache.plc4x.java.spi.generation.*;
+import org.apache.plc4x.java.utils.rawsockets.netty.RawSocketChannel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ProfinetDevice {
+
+    private static final int DEFAULT_UDP_PORT = 34964;
+    private final Logger logger = LoggerFactory.getLogger(ProfinetDevice.class);
+    private final DceRpc_ActivityUuid uuid;
+
+    private DatagramSocket udpSocket;
+    private RawSocketChannel rawSocketChannel;
+    private Channel channel;
+    private final MacAddress macAddress;
+    private ConversationContext<Ethernet_Frame> context;
+    private ProfinetDeviceState state = ProfinetDeviceState.IDLE;
+    private Lldp_Pdu lldpPdu = null;
+    private PnDcp_Pdu dcpPdu = null;
+    private String ipAddress;
+    private String portId;
+
+    private AtomicInteger sessionKeyGenerator = new AtomicInteger(1);
+
+
+    private void closeUDPSocket() {
+        // Handle the closing of the connection, might need to send some messages beforehand.
+        if (udpSocket != null && !udpSocket.isConnected()) {
+            udpSocket.close();
+            context.getChannel().close();
+        }
+    }
+
+    private boolean createUDPSocket() {
+        if (state != ProfinetDeviceState.IDLE) {
+            closeUDPSocket();
+        }
+        if (!(channel instanceof RawSocketChannel)) {
+            logger.warn("Expected a 'raw' transport, closing channel...");
+            closeUDPSocket();
+            return false;
+        }
+
+        rawSocketChannel = (RawSocketChannel) channel;
+
+        // Create an udp socket
+        try {
+            udpSocket = new DatagramSocket();
+        } catch (SocketException e) {
+            logger.warn("Unable to create udp socket " + e.getMessage());
+            closeUDPSocket();
+            return false;
+        }
+        return true;
+    }
+
+    public boolean onConnect() {
+        if (!createUDPSocket()) {
+            // Unable to create UDP connection
+            return false;
+        }
+
+        ProfinetMessageWrapper.sendMessage(
+            new CreateConnection(),
+            this
+        );
+
+        return false;
+    }
+
+    private int generateSessionKey() {
+        // Generate a new session key.
+        Integer sessionKey = sessionKeyGenerator.getAndIncrement();
+        // Reset the session key as soon as it reaches the max for a 16 bit uint
+        if (sessionKeyGenerator.get() == 0xFFFF) {
+            sessionKeyGenerator.set(1);
+        }
+        return sessionKey;
+    }
+
+    public boolean hasLldpPdu() {
+        if (lldpPdu != null) {
+            return true;
+        }
+        return false;
+    }
+
+    public boolean hasDcpPdu() {
+        if (dcpPdu != null) {
+            return true;
+        }
+        return false;
+    }
+
+    public void handle(PlcDiscoveryItem item) {
+        logger.debug("Received Discovered item at device");
+        if (item.getOptions().containsKey("IpAddress")) {
+            this.ipAddress = item.getOptions().get("IpAddress");
+        }
+        if (item.getOptions().containsKey("PortId")) {
+            this.portId = item.getOptions().get("PortId");
+        }
+    }
+
+    public void setContext(ConversationContext<Ethernet_Frame> context) {
+        this.context = context;
+        channel = context.getChannel();
+    }
+
+    public ProfinetDevice(MacAddress macAddress) {
+        this.macAddress = macAddress;
+        // Generate a new Activity Id, which will be used throughout the connection.
+        this.uuid = generateActivityUuid();
+    }
+
+    protected static DceRpc_ActivityUuid generateActivityUuid() {
+        UUID number = UUID.randomUUID();
+        try {
+            WriteBufferByteBased wb = new WriteBufferByteBased(128);
+            wb.writeLong(64, number.getMostSignificantBits());
+            wb.writeLong(64, number.getLeastSignificantBits());
+
+            ReadBuffer rb = new ReadBufferByteBased(wb.getData());
+            return new DceRpc_ActivityUuid(rb.readLong(32), rb.readInt(16), rb.readInt(16), rb.readByteArray(8));
+        } catch (SerializationException | ParseException e) {
+            // Ignore ... this should actually never happen.
+        }
+        return null;
+    }
+
+    public DatagramSocket getUdpSocket() {
+        return this.udpSocket;
+    }
+
+    public InetAddress getIpAddress() throws UnknownHostException {
+        return InetAddress.getByName(this.ipAddress);
+    }
+
+    public int getPort() {
+        return DEFAULT_UDP_PORT;
+    }
+
+    public class CreateConnection implements ProfinetCallable {
+
+        public DceRpc_Packet create() throws PlcException {
+            try {
+                return new DceRpc_Packet(
+                    DceRpc_PacketType.REQUEST, true, false, false,
+                    IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
+                    new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
+                    new DceRpc_InterfaceUuid_DeviceInterface(),
+                    ProfinetDevice.this.uuid,
+                    0, 0, DceRpc_Operation.CONNECT,
+                    new PnIoCm_Packet_Req(16696, 16696, 0, 0,
+                        Arrays.asList(
+                            new PnIoCm_Block_ArReq((short) 1, (short) 0, PnIoCm_ArType.IO_CONTROLLER,
+                                new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51")),
+                                ProfinetDevice.this.generateSessionKey(),
+                                ProfinetDevice.this.macAddress,
+                                new Uuid(Hex.decodeHex("dea000006c9711d1827100640008002a")),
+                                false, true, false,
+                                false, PnIoCm_CompanionArType.SINGLE_AR, false,
+                                true, false, PnIoCm_State.ACTIVE,
+                                600,
+                                // This actually needs to be set to this value and not the real port number.
+                                0x8892,
+                                // It seems that it must be set to this value, or it won't work.
+                                "plc4x"),
+                            new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.INPUT_CR,
+                                0x0001,
+                                0x8892,
+                                false, false,
+                                false, false, PnIoCm_RtClass.RT_CLASS_2, 40,
+                                0xBBF0, 128, 8, 1, 0, 0xffffffff,
+                                50, 50, 0xC000,
+                                new org.apache.plc4x.java.profinet.readwrite.MacAddress(Hex.decodeHex("000000000000")),
+                                Collections.singletonList(
+                                    new PnIoCm_IoCrBlockReqApi(
+                                        Arrays.asList(
+                                            new PnIoCm_IoDataObject(0, 0x0001, 0),
+                                            new PnIoCm_IoDataObject(0, 0x8000, 1),
+                                            new PnIoCm_IoDataObject(0, 0x8001, 2)
+                                        ),
+                                        new ArrayList<PnIoCm_IoCs>(0))
+                                )),
+                            new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.OUTPUT_CR,
+                                0x0002, 0x8892, false, false,
+                                false, false, PnIoCm_RtClass.RT_CLASS_2, 40,
+                                0xFFFF, 128, 8, 1, 0, 0xffffffff,
+                                50, 50, 0xC000,
+                                new MacAddress(Hex.decodeHex("000000000000")),
+                                Collections.singletonList(
+                                    new PnIoCm_IoCrBlockReqApi(
+                                        new ArrayList<PnIoCm_IoDataObject>(0),
+                                        Arrays.asList(
+                                            new PnIoCm_IoCs(0, 0x0001, 0),
+                                            new PnIoCm_IoCs(0, 0x8000, 1),
+                                            new PnIoCm_IoCs(0, 0x8001, 2)
+                                        )
+                                    )
+                                )
+                            ),
+                            new PnIoCm_Block_ExpectedSubmoduleReq((short) 1, (short) 0,
+                                Collections.singletonList(
+                                    new PnIoCm_ExpectedSubmoduleBlockReqApi(0,
+                                        0x00000001, 0x00000000,
+                                        Arrays.asList(
+                                            new PnIoCm_Submodule_NoInputNoOutputData(0x0001,
+                                                0x00000001, false, false,
+                                                false, false),
+                                            new PnIoCm_Submodule_NoInputNoOutputData(0x8000,
+                                                0x00008000, false, false,
+                                                false, false),
+                                            new PnIoCm_Submodule_NoInputNoOutputData(0x8001,
+                                                0x00008001, false, false,
+                                                false, false)
+                                        )
+                                    )
+                                )
+                            ),
+                            new PnIoCm_Block_AlarmCrReq((short) 1, (short) 0,
+                                PnIoCm_AlarmCrType.ALARM_CR, 0x8892, false, false, 1, 3,
+                                0x0000, 200, 0xC000, 0xA000)
+                        ))
+                );
+
+            /*// Build the UDP/IP/EthernetFrame to transport the package.
+            return new Ethernet_Frame(profinetDriverContext.getRemoteMacAddress(), profinetDriverContext.getLocalMacAddress(),
+                new Ethernet_FramePayload_IPv4(ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE), (short) 64,
+                    profinetDriverContext.getLocalIpAddress(), profinetDriverContext.getRemoteIpAddress(),
+                    new Udp_Packet(profinetDriverContext.getLocalUdpPort(), profinetDriverContext.getRemoteUdpPort(),
+                        dceRpcConnectionRequest)));*/
+            } catch (DecoderException e) {
+                throw new PlcException("Error creating connection request", e);
+            }
+        }
+
+        public void handle(DceRpc_Packet dceRpc_packet) throws PlcException {
+            if ((dceRpc_packet.getOperation() == DceRpc_Operation.CONNECT) && (dceRpc_packet.getPacketType() == DceRpc_PacketType.RESPONSE)) {
+                if (dceRpc_packet.getPayload().getPacketType() == DceRpc_PacketType.RESPONSE) {
+
+                    // Get the remote MAC address and store it in the context.
+                    final PnIoCm_Packet_Res connectResponse = (PnIoCm_Packet_Res) dceRpc_packet.getPayload();
+                    if ((connectResponse.getBlocks().size() > 0) && (connectResponse.getBlocks().get(0) instanceof PnIoCm_Block_ArRes)) {
+                        final PnIoCm_Block_ArRes pnIoCm_block_arRes = (PnIoCm_Block_ArRes) connectResponse.getBlocks().get(0);
+
+                        // Update the raw-socket transports filter expression.
+                        ((RawSocketChannel) channel).setRemoteMacAddress(org.pcap4j.util.MacAddress.getByAddress(macAddress.getAddress()));
+                    } else {
+                        throw new PlcException("Unexpected type of first block.");
+                    }
+                } else {
+                    throw new PlcException("Unexpected response");
+                }
+            } else if (dceRpc_packet.getPacketType() == DceRpc_PacketType.REJECT) {
+                throw new PlcException("Device rejected connection request");
+            } else {
+                throw new PlcException("Unexpected response");
+            }
+        }
+    }
+}
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDeviceMessageHandler.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDeviceMessageHandler.java
new file mode 100644
index 000000000..b737801fa
--- /dev/null
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDeviceMessageHandler.java
@@ -0,0 +1,51 @@
+/*
+ * 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.profinet.device;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryItemHandler;
+import org.apache.plc4x.java.profinet.readwrite.MacAddress;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ProfinetDeviceMessageHandler implements PlcDiscoveryItemHandler {
+
+    private HashMap<MacAddress, ProfinetDevice> configuredDevices;
+
+    @Override
+    public void handle(PlcDiscoveryItem discoveryItem) {
+        try {
+            MacAddress macAddress = new MacAddress(Hex.decodeHex(discoveryItem.getOptions().get("MacAddress")));
+            if (configuredDevices.containsKey(macAddress)) {
+                configuredDevices.get(macAddress).handle(discoveryItem);
+            }
+        } catch (DecoderException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void setConfiguredDevices(HashMap<MacAddress, ProfinetDevice> configuredDevices) {
+        this.configuredDevices = configuredDevices;
+    }
+}
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java
new file mode 100644
index 000000000..bee0ae8d3
--- /dev/null
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java
@@ -0,0 +1,48 @@
+package org.apache.plc4x.java.profinet.device;
+
+import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.profinet.readwrite.*;
+import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
+import org.apache.plc4x.java.spi.generation.SerializationException;
+import org.apache.plc4x.java.spi.generation.WriteBufferByteBased;
+import org.apache.plc4x.java.utils.rawsockets.netty.RawSocketChannel;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+
+public class ProfinetMessageWrapper {
+
+    public static void sendMessage(ProfinetCallable callable, ProfinetDevice context) throws RuntimeException {
+        try {
+            DceRpc_Packet packet = callable.create();
+            // Serialize it to a byte-payload
+            WriteBufferByteBased writeBuffer = new WriteBufferByteBased(packet.getLengthInBytes());
+            packet.serialize(writeBuffer);
+            // Create a udp packet.
+            DatagramPacket connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
+            connectRequestPacket.setAddress(context.getIpAddress());
+            connectRequestPacket.setPort(context.getPort());
+
+            // Send it.
+            context.getUdpSocket().send(connectRequestPacket);
+
+            // Receive the response.
+            byte[] resultBuffer = new byte[packet.getLengthInBytes()];
+            DatagramPacket connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
+            context.getUdpSocket().receive(connectResponsePacket);
+            ReadBufferByteBased readBuffer = new ReadBufferByteBased(resultBuffer);
+            final DceRpc_Packet dceRpc_packet = DceRpc_Packet.staticParse(readBuffer);
+            callable.handle(dceRpc_packet);
+        } catch (SerializationException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        } catch (PlcException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+}
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
index efbac95c4..fe931765c 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
@@ -24,13 +24,20 @@ import org.apache.commons.codec.binary.Hex;
 import org.apache.commons.lang3.NotImplementedException;
 import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.messages.*;
+import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
+import org.apache.plc4x.java.api.model.PlcSubscriptionHandle;
+import org.apache.plc4x.java.profinet.config.ProfinetConfiguration;
 import org.apache.plc4x.java.profinet.context.ProfinetDriverContext;
+import org.apache.plc4x.java.profinet.device.ProfinetDevice;
+import org.apache.plc4x.java.profinet.device.ProfinetDeviceMessageHandler;
 import org.apache.plc4x.java.profinet.discovery.ProfinetPlcDiscoverer;
 import org.apache.plc4x.java.profinet.readwrite.*;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.apache.plc4x.java.spi.configuration.HasConfiguration;
 import org.apache.plc4x.java.spi.generation.*;
 import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryRequest;
+import org.apache.plc4x.java.spi.messages.PlcSubscriber;
 import org.apache.plc4x.java.utils.rawsockets.netty.RawSocketChannel;
 import org.pcap4j.core.PcapAddress;
 import org.pcap4j.core.PcapNativeException;
@@ -45,13 +52,11 @@ import java.time.Duration;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 
-public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
+public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> implements HasConfiguration<ProfinetConfiguration>, PlcSubscriber {
 
     public static final Duration REQUEST_TIMEOUT = Duration.ofMillis(10000);
-
-    private static AtomicInteger sessionKeyGenerator = new AtomicInteger(1);
-
     private final Logger logger = LoggerFactory.getLogger(ProfinetProtocolLogic.class);
 
     private ProfinetDriverContext profinetDriverContext;
@@ -61,6 +66,10 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
     private RawSocketChannel rawSocketChannel;
     private Channel channel;
 
+    private ProfinetDeviceMessageHandler handler = new ProfinetDeviceMessageHandler();
+
+    private ProfinetConfiguration configuration;
+
     private static final Uuid ARUUID;
 
     static {
@@ -72,53 +81,66 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
     }
 
     @Override
-    public void setContext(ConversationContext<Ethernet_Frame> context) {
-        super.setContext(context);
-        this.profinetDriverContext = (ProfinetDriverContext) driverContext;
+    public void setConfiguration(ProfinetConfiguration configuration) {
+        this.configuration = configuration;
+        this.handler.setConfiguredDevices(configuration.configuredDevices);
     }
 
     @Override
-    public void onConnect(ConversationContext<Ethernet_Frame> context) {
-        channel = context.getChannel();
-        connected = false;
-        if (!(channel instanceof RawSocketChannel)) {
-            logger.warn("Expected a 'raw' transport, closing channel...");
-            context.getChannel().close();
-            return;
+    public void setContext(ConversationContext<Ethernet_Frame> context) {
+        super.setContext(context);
+        this.profinetDriverContext = (ProfinetDriverContext) driverContext;
+        for (Map.Entry<MacAddress, ProfinetDevice> device : configuration.configuredDevices.entrySet()) {
+            device.getValue().setContext(context);
         }
-
-        rawSocketChannel = (RawSocketChannel) channel;
-
-        // Create an udp socket
         try {
-            udpSocket = new DatagramSocket();
-        } catch (SocketException e) {
-            logger.warn("Unable to create udp socket " + e.getMessage());
-            context.getChannel().close();
-            return;
+            onDeviceDiscovery();
+        } catch (InterruptedException e) {
         }
+    }
 
+    private void onDeviceDiscovery() throws InterruptedException {
         ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer();
         DefaultPlcDiscoveryRequest request = new DefaultPlcDiscoveryRequest(
             discoverer,
             new LinkedHashMap<>()
         );
 
-        discoverer.ongoingDiscoverWithHandler(
+        // TODO:- Add handler for un-requested messages
+        discoverer.discoverWithHandler(
             request,
-            null,
-            5000L,
-            30000L
+            handler
         );
+        waitForDeviceDiscovery();
+    }
 
-        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-        // Initialize some important datastructures, that will be used a lot.
+    private void waitForDeviceDiscovery() throws InterruptedException {
+        // Once we receive an LLDP and PN-DCP message for each device move on.
+        boolean discovered = false;
+        int count = 0;
+        while (!discovered) {
+            discovered = true;
+            for (Map.Entry<MacAddress, ProfinetDevice> device : configuration.configuredDevices.entrySet()) {
+                if (!device.getValue().hasLldpPdu() || !device.getValue().hasDcpPdu()) {
+                    discovered = false;
+                }
+            }
+            if (!discovered) {
+                Thread.sleep(3000L);
+                count += 1;
+            }
+            if (count > 5) {
+                break;
+            }
+        }
+    }
 
-        // Generate a new Activity Id, which will be used throughout the connection.
-        profinetDriverContext.setDceRpcActivityUuid(generateActivityUuid());
+    @Override
+    public void onConnect(ConversationContext<Ethernet_Frame> context) {
 
+        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+        // Initialize some important datastructures, that will be used a lot.
         // TODO: Possibly we can remove the ARP lookup and simply use the mac address in the connection-response.
-
         // Local connectivity attributes
         profinetDriverContext.setLocalMacAddress(new MacAddress(rawSocketChannel.getLocalMacAddress().getAddress()));
         final InetSocketAddress localAddress = (InetSocketAddress) rawSocketChannel.getLocalAddress();
@@ -127,24 +149,8 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
         // Use the port of the udp socket
         profinetDriverContext.setLocalUdpPort(udpSocket.getPort());
 
-        // Remote connectivity attributes
-        byte[] macAddress = null;
-        try {
-            macAddress = Hex.decodeHex("000000000000");
-        } catch (DecoderException e) {
-            // Ignore this.
-        }
-        profinetDriverContext.setRemoteMacAddress(new MacAddress(macAddress));
-        final InetSocketAddress remoteAddress = (InetSocketAddress) rawSocketChannel.getRemoteAddress();
-        Inet4Address remoteIpAddress = (Inet4Address) remoteAddress.getAddress();
-        profinetDriverContext.setRemoteIpAddress(new IpAddress(remoteIpAddress.getAddress()));
-        profinetDriverContext.setRemoteUdpPort(remoteAddress.getPort());
-
-        // Generate a new session key.
-        profinetDriverContext.setSessionKey(sessionKeyGenerator.getAndIncrement());
-        // Reset the session key as soon as it reaches the max for a 16 bit uint
-        if (sessionKeyGenerator.get() == 0xFFFF) {
-            sessionKeyGenerator.set(1);
+        for (Map.Entry<MacAddress, ProfinetDevice> device : configuration.configuredDevices.entrySet()) {
+            device.getValue().onConnect();
         }
 
         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -350,7 +356,17 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
         return future;
     }
 
-        @Override
+    @Override
+    public PlcConsumerRegistration register(Consumer<PlcSubscriptionEvent> consumer, Collection<PlcSubscriptionHandle> handles) {
+        return null;
+    }
+
+    @Override
+    public void unregister(PlcConsumerRegistration registration) {
+
+    }
+
+    @Override
     protected void decode(ConversationContext<Ethernet_Frame> context, Ethernet_Frame msg) throws Exception {
         super.decode(context, msg);
     }
@@ -556,19 +572,6 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
         );
     }
 
-    protected static DceRpc_ActivityUuid generateActivityUuid() {
-        UUID number = UUID.randomUUID();
-        try {
-            WriteBufferByteBased wb = new WriteBufferByteBased(128);
-            wb.writeLong(64, number.getMostSignificantBits());
-            wb.writeLong(64, number.getLeastSignificantBits());
-
-            ReadBuffer rb = new ReadBufferByteBased(wb.getData());
-            return new DceRpc_ActivityUuid(rb.readLong(32), rb.readInt(16), rb.readInt(16), rb.readByteArray(8));
-        } catch (SerializationException | ParseException e) {
-            // Ignore ... this should actually never happen.
-        }
-        return null;
-    }
+
 
 }
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 9a964a6e1..a0d8f1ecd 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -1049,6 +1049,14 @@
     ['0x8112' IOX_BLOCK_RES               ]
 ]
 
+[enum uint 16 ProfinetDeviceState
+    ['0x00'     IDLE]
+    ['0x01'     STARTUP]
+    ['0x02'     PRMEND]
+    ['0x03'     APPLRDY]
+    ['0x04'     ABORT]
+]
+
 [enum uint 16 PnIoCm_ArType
     ['0x0001' IO_CONTROLLER]
 ]


[plc4x] 09/13: feat(plc4j/): split out dcp and lldp tasks, so they can be processed separately.

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit d1c18db45f5334e1ddc6d529008fb531fc217722
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Fri Sep 16 15:53:03 2022 -0600

    feat(plc4j/): split out dcp and lldp tasks, so they can be processed separately.
---
 .../profinet/discovery/ProfinetPlcDiscoverer.java  | 755 +++++++++++----------
 1 file changed, 387 insertions(+), 368 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index 4486e1abc..bc7ef865d 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -20,7 +20,6 @@ package org.apache.plc4x.java.profinet.discovery;
 
 import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.binary.Hex;
-import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryItemHandler;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
@@ -50,7 +49,6 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.function.Function;
-import java.util.function.IntBinaryOperator;
 
 public class ProfinetPlcDiscoverer implements PlcDiscoverer {
 
@@ -68,6 +66,12 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
     private static final String DEVICE_INSTANCE = "DEVICE_PROPERTIES_OPTION-7";
     private static final String IP_OPTION_IP = "IP_OPTION-2";
 
+    ExecutorService pool = Executors.newSingleThreadExecutor();
+    Map<MacAddress, PcapHandle> openHandles = new HashMap<>();
+    List<PlcDiscoveryItem> values = new ArrayList<>();
+
+    Set<Timer> periodicTimers = new HashSet<>();
+
     private final Logger logger = LoggerFactory.getLogger(ProfinetPlcDiscoverer.class);
 
     @Override
@@ -75,200 +79,49 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         return discoverWithHandler(discoveryRequest, null);
     }
 
-    public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
-        CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
-        Set<PcapHandle> openHandles = new HashSet<>();
-        List<PlcDiscoveryItem> values = new ArrayList<>();
+    public void openDiscoverHandles() {
         try {
             for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
                 // It turned out on some MAC network devices without any ip addresses
                 // the compiling of the filter expression was causing errors. As
                 // currently there was no other way to detect this, this check seems
                 // to be sufficient.
-                if(dev.getAddresses().size() == 0) {
+                if (dev.getAddresses().size() == 0) {
                     continue;
                 }
                 if (!dev.isLoopBack()) {
                     for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
                         org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
                         PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
-                        openHandles.add(handle);
+                        openHandles.put(toPlc4xMacAddress(macAddress), handle);
 
-                        ExecutorService pool = Executors.newSingleThreadExecutor();
-
-                        // Only react on PROFINET DCP packets targeted at our current MAC address.
+                        // Only react on PROFINET DCP or LLDP packets targeted at our current MAC address.
                         handle.setFilter(
-                            "((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")",
+                            "(((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")) or (ether proto 0x88cc)",
                             BpfProgram.BpfCompileMode.OPTIMIZE);
-
-                        PacketListener listener =
-                            packet -> {
-                                // EthernetPacket is the highest level of abstraction we can be expecting.
-                                // Everything inside this we will have to decode ourselves.
-                                if (packet instanceof EthernetPacket) {
-                                    EthernetPacket ethernetPacket = (EthernetPacket) packet;
-                                    boolean isPnPacket = false;
-                                    // I have observed some times the ethernet packets being wrapped inside a VLAN
-                                    // Packet, in this case we simply unpack the content.
-                                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
-                                        Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
-                                        if (PN_EtherType.equals(vlanPacket.getHeader().getType())) {
-                                            isPnPacket = true;
-                                        }
-                                    } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType())) {
-                                        isPnPacket = true;
-                                    }
-
-                                    // It's a PROFINET packet.
-                                    if (isPnPacket) {
-                                        ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
-                                        try {
-                                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
-                                            PnDcp_Pdu pdu;
-                                            // Access the pdu data (either directly or by
-                                            // unpacking the content of the VLAN packet.
-                                            if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
-                                                Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
-                                                pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
-                                            } else {
-                                                pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
-                                            }
-                                            // Inspect the PDU itself
-                                            // (in this case we only process identify response packets)
-                                            if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
-                                                PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
-
-                                                Map<String, PnDcp_Block> blocks = new HashMap<>();
-                                                for (PnDcp_Block block : identifyResPDU.getBlocks()) {
-                                                    String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
-                                                    blocks.put(blockName, block);
-                                                }
-
-                                                // The mac address of the device we found
-                                                org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
-                                                // The mac address of the local network device
-                                                org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
-
-                                                String deviceTypeName = "unknown";
-                                                if (blocks.containsKey(DEVICE_TYPE_NAME)) {
-                                                    PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
-                                                    deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
-                                                }
-
-                                                String deviceName = "unknown";
-                                                if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
-                                                    PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
-                                                    deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
-                                                }
-
-                                                String role = "unknown";
-                                                if (blocks.containsKey(DEVICE_ROLE)) {
-                                                    role = "";
-                                                    PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
-                                                    if (block.getPnioSupervisor()) {
-                                                        role += ",SUPERVISOR";
-                                                    }
-                                                    if (block.getPnioMultidevive()) {
-                                                        role += ",MULTIDEVICE";
-                                                    }
-                                                    if (block.getPnioController()) {
-                                                        role += ",CONTROLLER";
-                                                    }
-                                                    if (block.getPnioDevice()) {
-                                                        role += ",DEVICE";
-                                                    }
-                                                    // Cut off the first comma
-                                                    if (role.length() > 0) {
-                                                        role = role.substring(1);
-                                                    } else {
-                                                        role = "unknown";
-                                                    }
-                                                }
-
-                                                String remoteIpAddress = "unknown";
-                                                String remoteSubnetMask = "unknown";
-                                                if (blocks.containsKey(IP_OPTION_IP)) {
-                                                    PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
-                                                    try {
-                                                        InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
-                                                        remoteIpAddress = addr.getHostAddress();
-                                                        InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
-                                                        remoteSubnetMask = netMask.getHostAddress();
-                                                    } catch (UnknownHostException e) {
-                                                        remoteIpAddress = "invalid";
-                                                    }
-                                                }
-
-                                                // Get the Vendor Id and the Device Id
-                                                String vendorId = "unknown";
-                                                String deviceId = "unknown";
-                                                if (blocks.containsKey(DEVICE_ID)) {
-                                                    PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
-                                                    vendorId = String.format("%04X", block.getVendorId());
-                                                    deviceId = String.format("%04X", block.getDeviceId());
-                                                }
-
-                                                Map<String, String> options = new HashMap<>();
-                                                options.put("remoteIpAddress", remoteIpAddress);
-                                                options.put("remoteSubnetMask", remoteSubnetMask);
-                                                options.put("remoteMacAddress", srcAddr.toString());
-                                                options.put("localMacAddress", dstAddr.toString());
-                                                options.put("deviceTypeName", deviceTypeName);
-                                                options.put("deviceName", deviceName);
-                                                options.put("vendorId", vendorId);
-                                                options.put("deviceId", deviceId);
-                                                options.put("role", role);
-                                                String name = deviceTypeName + " - " + deviceName;
-                                                PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
-                                                    ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
-                                                    remoteIpAddress, options, name, Collections.emptyMap());
-                                                values.add(value);
-
-                                                // If we have a discovery handler, pass it to the handler callback
-                                                if (handler != null) {
-                                                    handler.handle(value);
-                                                }
-
-                                                logger.debug("Found new device: '{}' with connection-url '{}'",
-                                                    value.getName(), value.getConnectionUrl());
-                                            }
-                                        } catch (ParseException e) {
-                                            logger.error("Got error decoding packet", e);
-                                        }
-                                    }
-                                }
-                            };
-                        Task t = new Task(handle, listener);
-                        pool.execute(t);
-
-                        // Construct and send the search request.
-                        Ethernet_Frame identificationRequest = new Ethernet_Frame(
-                            // Pre-Defined PROFINET discovery MAC address
-                            new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
-                            toPlc4xMacAddress(macAddress),
-                            new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
-                                new Ethernet_FramePayload_PnDcp(
-                                    new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
-                                        1,
-                                        256,
-                                        Collections.singletonList(
-                                            new PnDcp_Block_ALLSelector()
-                                        )))));
-                        WriteBufferByteBased buffer = new WriteBufferByteBased(34);
-                        identificationRequest.serialize(buffer);
-                        Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
-                        handle.sendPacket(packet);
                     }
                 }
             }
-        } catch (IllegalRawDataException | NotOpenException | PcapNativeException | SerializationException e) {
+        } catch (NotOpenException | PcapNativeException e) {
             logger.error("Got an exception while processing raw socket data", e);
-            future.completeExceptionally(new PlcException("Got an internal error while performing discovery"));
-            for (PcapHandle openHandle : openHandles) {
-                openHandle.close();
+            for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+                PcapHandle openHandle = entry.getValue();
+                try {
+                    openHandle.breakLoop();
+                    openHandle.close();
+                } catch (NotOpenException error) {
+                    logger.info("Handle already closed.");
+                }
+            }
+            for (Timer timer : periodicTimers) {
+                timer.cancel();
+                timer.purge();
             }
-            return future;
         }
+    }
+
+    public CompletableFuture<PlcDiscoveryResponse> setDiscoveryEndTimer(PlcDiscoveryRequest discoveryRequest, long delay) {
+        CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
 
         // Create a timer that completes the future after a given time with all the responses it found till then.
         Timer timer = new Timer("Discovery Timeout");
@@ -276,229 +129,395 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
             public void run() {
                 PlcDiscoveryResponse response =
                     new DefaultPlcDiscoveryResponse(discoveryRequest, PlcResponseCode.OK, values);
-                future.complete(response);
-                for (PcapHandle openHandle : openHandles) {
-                    openHandle.close();
+                for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+                    PcapHandle openHandle = entry.getValue();
+                    try {
+                        openHandle.breakLoop();
+                        openHandle.close();
+                    } catch (Exception e) {
+                        logger.error("Error occurred while closing handle");
+                    }
+
                 }
+                for (Timer timer : periodicTimers) {
+                    timer.cancel();
+                    timer.purge();
+                }
+                future.complete(response);
             }
-        }, 5000L);
+        }, delay);
 
         return future;
     }
 
-    public void lldpProbe() {
-        Set<PcapHandle> openHandles = new HashSet<>();
-        try {
-            for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
-                // It turned out on some MAC network devices without any ip addresses
-                // the compiling of the filter expression was causing errors. As
-                // currently there was no other way to detect this, this check seems
-                // to be sufficient.
-                if(dev.getAddresses().size() == 0) {
-                    continue;
+    public PacketListener createListener(PcapHandle handle, PlcDiscoveryItemHandler handler) {
+        PacketListener listener =
+            packet -> {
+                // EthernetPacket is the highest level of abstraction we can be expecting.
+                // Everything inside this we will have to decode ourselves.
+                if (packet instanceof EthernetPacket) {
+                    EthernetPacket ethernetPacket = (EthernetPacket) packet;
+                    boolean isPnPacket = false;
+                    // I have observed sometimes the ethernet packets being wrapped inside a VLAN
+                    // Packet, in this case we simply unpack the content.
+                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
+                        Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
+                        if (PN_EtherType.equals(vlanPacket.getHeader().getType()) || LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
+                            isPnPacket = true;
+                        }
+                    } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType()) || LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
+                        isPnPacket = true;
+                    }
+
+                    // It's a PROFINET or LLDP packet.
+                    if (isPnPacket) {
+                        ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
+                        try {
+                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
+
+                            // Access the pdu data (either directly or by
+                            // unpacking the content of the VLAN packet.
+                            if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
+                                Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
+                                if (vlefpl.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
+                                    PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
+                                    processPnDcp(pdu, ethernetPacket, handler);
+                                } else if (vlefpl.getPayload() instanceof Ethernet_FramePayload_LLDP) {
+                                    Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
+                                    processLldp(pdu, ethernetPacket, handler);
+                                }
+                            } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
+                                PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
+                                processPnDcp(pdu, ethernetPacket, handler);
+                            } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_LLDP) {
+                                Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
+                                processLldp(pdu, ethernetPacket, handler);
+                            }
+
+                        } catch (ParseException e) {
+                            logger.error("Got error decoding packet", e);
+                        }
+                    }
                 }
-                if (!dev.isLoopBack()) {
-                    for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
-                        org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
-                        PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
-                        openHandles.add(handle);
+            };
+        return listener;
+    }
 
-                        ExecutorService pool = Executors.newSingleThreadExecutor();
+    public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
+        openDiscoverHandles();
+        startListener(handler);
+        startLldpPoll();
+        startPnDcpPoll();
+        CompletableFuture<PlcDiscoveryResponse> future = setDiscoveryEndTimer(discoveryRequest, 10000L);
+        return future;
+    }
 
-                        // Only react on PROFINET DCP packets targeted at our current MAC address.
-                        handle.setFilter(
-                            "(ether proto 0x88cc)",
-                            BpfProgram.BpfCompileMode.OPTIMIZE);
+    private void processPnDcp(PnDcp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
+        // Inspect the PDU itself
+        // (in this case we only process identify response packets)
+        if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
+            PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
 
-                        PacketListener listener =
-                            packet -> {
-                                // EthernetPacket is the highest level of abstraction we can be expecting.
-                                // Everything inside this we will have to decode ourselves.
-                                if (packet instanceof EthernetPacket) {
-                                    EthernetPacket ethernetPacket = (EthernetPacket) packet;
-                                    boolean isLldpPacket = false;
-                                    // I have observed sometimes the ethernet packets being wrapped inside a VLAN
-                                    // Packet, in this case we simply unpack the content.
-                                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
-                                        Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
-                                        if (LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
-                                            isLldpPacket = true;
-                                        }
-                                    } else if (LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
-                                        isLldpPacket = true;
-                                    }
-
-                                    // It's a LLDP packet.
-                                    if (isLldpPacket) {
-                                        ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
-                                        try {
-                                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
-                                            Lldp_Pdu pdu;
-                                            // Access the pdu data (either directly or by
-                                            // unpacking the content of the VLAN packet.
-                                            if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
-                                                Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
-                                                pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
-                                            } else {
-                                                pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
-                                            }
-                                            // Inspect the PDU itself
-                                            // (in this case we only process identify response packets)
-
-
-                                            logger.debug("Found new device: '{}' using LLDP '{}'",
-                                                "Not Sure", "Not Sure");
-                                        } catch (ParseException ex) {
-                                            throw new RuntimeException(ex);
-                                        }
-                                    }
-                                }
-                            };
-                        Task t = new Task(handle, listener);
-                        pool.execute(t);
-
-                        Function<Object, Boolean> lldpTimer =
-                            message -> {
-                                // Construct and send the LLDP Probe
-                                TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
-                                    new TlvProfibusSubTypePortStatus(0x00)
-                                );
-
-                                TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
-                                    new TlvProfibusSubTypeChassisMac(new MacAddress(linkLayerAddress.getAddress()))
-                                );
-
-                                TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
-                                    (short) 0x01,
-                                    (short) 0x03,
-                                    0x0020,
-                                    0x0010
-                                );
-
-                                Ethernet_Frame identificationRequest = null;
-                                try {
-                                    identificationRequest = new Ethernet_Frame(
-                                        // Pre-Defined LLDP discovery MAC address
-                                        new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
-                                        toPlc4xMacAddress(macAddress),
-                                        new Ethernet_FramePayload_LLDP(
-                                            new Lldp_Pdu(
-                                                Arrays.asList(
-                                                    new TlvChassisId(
-                                                        PLC4X_LLDP_IDENTIFIER.length() + 1,
-                                                        (short) 7,
-                                                        PLC4X_LLDP_IDENTIFIER
-                                                    ),
-                                                    new TlvPortId(
-                                                        PLC4X_LLDP_PORT.length() + 1,
-                                                        (short) 7,
-                                                        PLC4X_LLDP_PORT
-                                                    ),
-                                                    new TlvTimeToLive(2, 20),
-                                                    new TlvOrganizationSpecific(
-                                                        portStatus.getLengthInBytes(),
-                                                        portStatus
-                                                    ),
-                                                    new TlvOrganizationSpecific(
-                                                        chassisMac.getLengthInBytes(),
-                                                        chassisMac
-                                                    ),
-                                                    new TlvOrganizationSpecific(
-                                                        ieee.getLengthInBytes(),
-                                                        ieee
-                                                    ),
-                                                    new TlvManagementAddress(
-                                                        12,
-                                                        ManagementAddressSubType.IPV4,
-                                                        new IpAddress(Hex.decodeHex("c0a85a6e")),
-                                                        (short) 0x03,
-                                                        0x01L,
-                                                        (short) 0x00
-                                                    ),
-                                                    new EndOfLldp(0)
-                                                )
-                                            )));
-                                } catch (DecoderException e) {
-                                    throw new RuntimeException(e);
-                                }
-                                WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
-                                try {
-                                    identificationRequest.serialize(buffer);
-                                } catch (SerializationException e) {
-                                    throw new RuntimeException(e);
-                                }
-                                Packet packet = null;
-                                try {
-                                    packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
-                                } catch (IllegalRawDataException e) {
-                                    throw new RuntimeException(e);
-                                }
-                                try {
-                                    handle.sendPacket(packet);
-                                } catch (PcapNativeException e) {
-                                    throw new RuntimeException(e);
-                                } catch (NotOpenException e) {
-                                    throw new RuntimeException(e);
-                                }
-                                return null;
-                            };
-                        Timer timer = new Timer();
-
-                        // Schedule to run after every 3 second(3000 millisecond)
-                        timer.scheduleAtFixedRate(
-                            new LLDPTask(handle, lldpTimer),
-                            3000,
-                            3000);
-                    }
+            Map<String, PnDcp_Block> blocks = new HashMap<>();
+            for (PnDcp_Block block : identifyResPDU.getBlocks()) {
+                String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
+                blocks.put(blockName, block);
+            }
+
+            // The mac address of the device we found
+            org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
+            // The mac address of the local network device
+            org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
+
+            String deviceTypeName = "unknown";
+            if (blocks.containsKey(DEVICE_TYPE_NAME)) {
+                PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
+                deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
+            }
+
+            String deviceName = "unknown";
+            if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
+                PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
+                deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
+            }
+
+            String role = "unknown";
+            if (blocks.containsKey(DEVICE_ROLE)) {
+                role = "";
+                PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
+                if (block.getPnioSupervisor()) {
+                    role += ",SUPERVISOR";
+                }
+                if (block.getPnioMultidevive()) {
+                    role += ",MULTIDEVICE";
+                }
+                if (block.getPnioController()) {
+                    role += ",CONTROLLER";
+                }
+                if (block.getPnioDevice()) {
+                    role += ",DEVICE";
+                }
+                // Cut off the first comma
+                if (role.length() > 0) {
+                    role = role.substring(1);
+                } else {
+                    role = "unknown";
                 }
             }
-        } catch (NotOpenException | PcapNativeException e) {
-            logger.error("Got an exception while processing raw socket data", e);
 
-            for (PcapHandle openHandle : openHandles) {
-                openHandle.close();
+            String remoteIpAddress = "unknown";
+            String remoteSubnetMask = "unknown";
+            if (blocks.containsKey(IP_OPTION_IP)) {
+                PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
+                try {
+                    InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
+                    remoteIpAddress = addr.getHostAddress();
+                    InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
+                    remoteSubnetMask = netMask.getHostAddress();
+                } catch (UnknownHostException e) {
+                    remoteIpAddress = "invalid";
+                }
             }
+
+            // Get the Vendor Id and the Device Id
+            String vendorId = "unknown";
+            String deviceId = "unknown";
+            if (blocks.containsKey(DEVICE_ID)) {
+                PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
+                vendorId = String.format("%04X", block.getVendorId());
+                deviceId = String.format("%04X", block.getDeviceId());
+            }
+
+            Map<String, String> options = new HashMap<>();
+            options.put("remoteIpAddress", remoteIpAddress);
+            options.put("remoteSubnetMask", remoteSubnetMask);
+            options.put("remoteMacAddress", srcAddr.toString());
+            options.put("localMacAddress", dstAddr.toString());
+            options.put("deviceTypeName", deviceTypeName);
+            options.put("deviceName", deviceName);
+            options.put("vendorId", vendorId);
+            options.put("deviceId", deviceId);
+            options.put("role", role);
+            String name = deviceTypeName + " - " + deviceName;
+            PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
+                ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
+                remoteIpAddress, options, name, Collections.emptyMap());
+            values.add(value);
+
+            // If we have a discovery handler, pass it to the handler callback
+            if (handler != null) {
+                handler.handle(value);
+            }
+
+            logger.debug("Found new device: '{}' with connection-url '{}'",
+                value.getName(), value.getConnectionUrl());
         }
     }
 
-    private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
-        byte[] address = pcap4jMacAddress.getAddress();
-        return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
+    private void processLldp(Lldp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
+        logger.debug("Found new lldp device: '' with connection-url ''");
     }
 
-    private static class Task implements Runnable {
+    public void startPnDcpPoll() {
+        for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+            PcapHandle handle = entry.getValue();
+            MacAddress macAddress = entry.getKey();
+            // Construct and send the search request.
+
+            Function<Object, Boolean> pnDcpTimer =
+                message -> {
+                    Ethernet_Frame identificationRequest = new Ethernet_Frame(
+                        // Pre-Defined PROFINET discovery MAC address
+                        new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
+                        macAddress,
+                        new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
+                            new Ethernet_FramePayload_PnDcp(
+                                new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
+                                    1,
+                                    256,
+                                    Collections.singletonList(
+                                        new PnDcp_Block_ALLSelector()
+                                    )))));
+                    WriteBufferByteBased buffer = new WriteBufferByteBased(34);
+                    try {
+                        identificationRequest.serialize(buffer);
+                    } catch (SerializationException e) {
+                        throw new RuntimeException(e);
+                    }
+                    Packet packet = null;
+                    try {
+                        packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
+                    } catch (IllegalRawDataException e) {
+                        throw new RuntimeException(e);
+                    }
+                    try {
+                        handle.sendPacket(packet);
+                    } catch (PcapNativeException e) {
+                        throw new RuntimeException(e);
+                    } catch (NotOpenException e) {
+                        throw new RuntimeException(e);
+                    }
+                    return null;
+            };
+
+            Timer timer = new Timer();
+            periodicTimers.add(timer);
 
-        private final Logger logger = LoggerFactory.getLogger(Task.class);
+            // Schedule to run after every 3 second(3000 millisecond)
+            timer.scheduleAtFixedRate(
+                new PeriodicTask(handle, pnDcpTimer),
+                5000,
+                5000);
+        }
+    }
 
-        private final PcapHandle handle;
-        private final PacketListener listener;
+    public void startListener(PlcDiscoveryItemHandler handler) {
+        for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+            PcapHandle handle = entry.getValue();
+            MacAddress macAddress = entry.getKey();
+            // Construct and send the search request.
+
+            Function<Object, Boolean> pnDcpTimer =
+                message -> {
+                    PacketListener listener = createListener(handle, handler);
+                    try {
+                        handle.loop(-1, listener);
+                    } catch (InterruptedException e) {
+                        logger.error("Got error handling raw socket", e);
+                        Thread.currentThread().interrupt();
+                    } catch (PcapNativeException | NotOpenException e) {
+                        logger.error("Got error handling raw socket", e);
+                    }
+                    return null;
+                };
 
-        public Task(PcapHandle handle, PacketListener listener) {
-            this.handle = handle;
-            this.listener = listener;
+            Timer timer = new Timer();
+            periodicTimers.add(timer);
+
+            // Schedule to run after every 3 second(3000 millisecond)
+            timer.schedule(
+                new PeriodicTask(handle, pnDcpTimer),
+                5000,
+                15000);
         }
+    }
 
-        @Override
-        public void run() {
-            try {
-                handle.loop(10, listener);
-            } catch (InterruptedException e) {
-                logger.error("Got error handling raw socket", e);
-                Thread.currentThread().interrupt();
-            } catch (PcapNativeException | NotOpenException e) {
-                logger.error("Got error handling raw socket", e);
-            }
+
+
+    public void startLldpPoll() {
+        for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+            PcapHandle handle = entry.getValue();
+            MacAddress macAddress = entry.getKey();
+
+            Function<Object, Boolean> lldpTimer =
+                message -> {
+                    // Construct and send the LLDP Probe
+                    TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
+                        new TlvProfibusSubTypePortStatus(0x00)
+                    );
+
+                    TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
+                        new TlvProfibusSubTypeChassisMac(macAddress)
+                    );
+
+                    TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
+                        (short) 0x01,
+                        (short) 0x03,
+                        0x0020,
+                        0x0010
+                    );
+
+                    Ethernet_Frame identificationRequest = null;
+                    try {
+                        identificationRequest = new Ethernet_Frame(
+                            // Pre-Defined LLDP discovery MAC address
+                            new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
+                            macAddress,
+                            new Ethernet_FramePayload_LLDP(
+                                new Lldp_Pdu(
+                                    Arrays.asList(
+                                        new TlvChassisId(
+                                            PLC4X_LLDP_IDENTIFIER.length() + 1,
+                                            (short) 7,
+                                            PLC4X_LLDP_IDENTIFIER
+                                        ),
+                                        new TlvPortId(
+                                            PLC4X_LLDP_PORT.length() + 1,
+                                            (short) 7,
+                                            PLC4X_LLDP_PORT
+                                        ),
+                                        new TlvTimeToLive(2, 20),
+                                        new TlvOrganizationSpecific(
+                                            portStatus.getLengthInBytes(),
+                                            portStatus
+                                        ),
+                                        new TlvOrganizationSpecific(
+                                            chassisMac.getLengthInBytes(),
+                                            chassisMac
+                                        ),
+                                        new TlvOrganizationSpecific(
+                                            ieee.getLengthInBytes(),
+                                            ieee
+                                        ),
+                                        new TlvManagementAddress(
+                                            12,
+                                            ManagementAddressSubType.IPV4,
+                                            new IpAddress(Hex.decodeHex("c0a85a6e")),
+                                            (short) 0x03,
+                                            0x01L,
+                                            (short) 0x00
+                                        ),
+                                        new EndOfLldp(0)
+                                    )
+                                )));
+                    } catch (DecoderException e) {
+                        throw new RuntimeException(e);
+                    }
+                    WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
+                    try {
+                        identificationRequest.serialize(buffer);
+                    } catch (SerializationException e) {
+                        throw new RuntimeException(e);
+                    }
+                    Packet packet = null;
+                    try {
+                        packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
+                    } catch (IllegalRawDataException e) {
+                        throw new RuntimeException(e);
+                    }
+                    try {
+                        handle.sendPacket(packet);
+                    } catch (PcapNativeException e) {
+                        throw new RuntimeException(e);
+                    } catch (NotOpenException e) {
+                        throw new RuntimeException(e);
+                    }
+                    return null;
+                };
+            Timer timer = new Timer();
+            periodicTimers.add(timer);
+
+            // Schedule to run after every 3 second(3000 millisecond)
+            timer.scheduleAtFixedRate(
+                new PeriodicTask(handle, lldpTimer),
+                5000,
+                5000);
         }
     }
 
-    private static class LLDPTask extends TimerTask {
+    private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
+        byte[] address = pcap4jMacAddress.getAddress();
+        return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
+    }
+
+    private static class PeriodicTask extends TimerTask {
 
-        private final Logger logger = LoggerFactory.getLogger(Task.class);
+        private final Logger logger = LoggerFactory.getLogger(PeriodicTask.class);
 
         private final PcapHandle handle;
         private final Function<Object, Boolean> operator;
 
-        public LLDPTask(PcapHandle handle, Function<Object, Boolean> operator) {
+        public PeriodicTask(PcapHandle handle, Function<Object, Boolean> operator) {
             this.handle = handle;
             this.operator = operator;
         }
@@ -507,12 +526,12 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         public void run() {
             operator.apply(null);
         }
+
     }
 
     public static void main(String[] args) throws Exception {
         ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer();
-        //discoverer.discover(null);
-        discoverer.lldpProbe();
+        discoverer.discover(null);
         Thread.sleep(10000);
     }
 


[plc4x] 10/13: fix(plc4j/profinet): Identified that the Application Ready request comes from the device.

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 40d4cc6263a9b8fb8b362bc5bfe32302500b9419
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 18 08:34:19 2022 -0600

    fix(plc4j/profinet): Identified that the Application Ready request comes from the device.
---
 .../apache/plc4x/java/profinet/ProfinetDriver.java |   2 +-
 .../profinet/discovery/ProfinetPlcDiscoverer.java  |  23 ++--
 .../profinet/protocol/ProfinetProtocolLogic.java   | 128 ++++++++++++++++-----
 .../resources/protocols/profinet/profinet.mspec    |  12 +-
 4 files changed, 123 insertions(+), 42 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
index 05d96d8b4..0a311206d 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
@@ -85,7 +85,7 @@ public class ProfinetDriver extends GeneratedDriverBase<Ethernet_Frame> {
      */
     @Override
     protected boolean awaitSetupComplete() {
-        return false;
+        return true;
     }
 
     /**
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index bc7ef865d..f0a1a3a43 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -206,12 +206,19 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
     public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
         openDiscoverHandles();
         startListener(handler);
-        startLldpPoll();
-        startPnDcpPoll();
+        startLldpPoll(5000L);
+        startPnDcpPoll(30000L);
         CompletableFuture<PlcDiscoveryResponse> future = setDiscoveryEndTimer(discoveryRequest, 10000L);
         return future;
     }
 
+    public void ongoingDiscoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler, long lldpPeriod, long dcpPeriod) {
+        openDiscoverHandles();
+        startListener(handler);
+        startLldpPoll(lldpPeriod);
+        startPnDcpPoll(dcpPeriod);
+    }
+
     private void processPnDcp(PnDcp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
         // Inspect the PDU itself
         // (in this case we only process identify response packets)
@@ -318,7 +325,7 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         logger.debug("Found new lldp device: '' with connection-url ''");
     }
 
-    public void startPnDcpPoll() {
+    public void startPnDcpPoll(long period) {
         for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
             PcapHandle handle = entry.getValue();
             MacAddress macAddress = entry.getKey();
@@ -366,8 +373,8 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
             // Schedule to run after every 3 second(3000 millisecond)
             timer.scheduleAtFixedRate(
                 new PeriodicTask(handle, pnDcpTimer),
-                5000,
-                5000);
+                0,
+                period);
         }
     }
 
@@ -404,7 +411,7 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
 
 
 
-    public void startLldpPoll() {
+    public void startLldpPoll(long period) {
         for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
             PcapHandle handle = entry.getValue();
             MacAddress macAddress = entry.getKey();
@@ -500,8 +507,8 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
             // Schedule to run after every 3 second(3000 millisecond)
             timer.scheduleAtFixedRate(
                 new PeriodicTask(handle, lldpTimer),
-                5000,
-                5000);
+                0,
+                period);
         }
     }
 
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
index 3801c1dfa..efbac95c4 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
@@ -25,10 +25,12 @@ import org.apache.commons.lang3.NotImplementedException;
 import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.profinet.context.ProfinetDriverContext;
+import org.apache.plc4x.java.profinet.discovery.ProfinetPlcDiscoverer;
 import org.apache.plc4x.java.profinet.readwrite.*;
 import org.apache.plc4x.java.spi.ConversationContext;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
 import org.apache.plc4x.java.spi.generation.*;
+import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryRequest;
 import org.apache.plc4x.java.utils.rawsockets.netty.RawSocketChannel;
 import org.pcap4j.core.PcapAddress;
 import org.pcap4j.core.PcapNativeException;
@@ -53,6 +55,11 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
     private final Logger logger = LoggerFactory.getLogger(ProfinetProtocolLogic.class);
 
     private ProfinetDriverContext profinetDriverContext;
+    private boolean connected = false;
+
+    private DatagramSocket udpSocket;
+    private RawSocketChannel rawSocketChannel;
+    private Channel channel;
 
     private static final Uuid ARUUID;
 
@@ -72,17 +79,17 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
 
     @Override
     public void onConnect(ConversationContext<Ethernet_Frame> context) {
-        final Channel channel = context.getChannel();
+        channel = context.getChannel();
+        connected = false;
         if (!(channel instanceof RawSocketChannel)) {
             logger.warn("Expected a 'raw' transport, closing channel...");
             context.getChannel().close();
             return;
         }
 
-        RawSocketChannel rawSocketChannel = (RawSocketChannel) channel;
+        rawSocketChannel = (RawSocketChannel) channel;
 
         // Create an udp socket
-        DatagramSocket udpSocket;
         try {
             udpSocket = new DatagramSocket();
         } catch (SocketException e) {
@@ -91,6 +98,19 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
             return;
         }
 
+        ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer();
+        DefaultPlcDiscoveryRequest request = new DefaultPlcDiscoveryRequest(
+            discoverer,
+            new LinkedHashMap<>()
+        );
+
+        discoverer.ongoingDiscoverWithHandler(
+            request,
+            null,
+            5000L,
+            30000L
+        );
+
         ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
         // Initialize some important datastructures, that will be used a lot.
 
@@ -225,6 +245,8 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
             resultBuffer = new byte[profinetAdvancedConnectionApplicationReady.getLengthInBytes()];
             connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
             udpSocket.receive(connectResponsePacket);
+            context.fireConnected();
+            connected = true;
 
         } catch (SerializationException | IOException | PlcException | ParseException e) {
             logger.error("Error", e);
@@ -250,14 +272,85 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
         return future;
     }
 
+    private Ethernet_FramePayload_PnDcp createProfinetCyclicDataRequest() {
+        return new Ethernet_FramePayload_PnDcp(
+            new PnDcp_Pdu_RealTimeCyclic(
+                0x8000,
+                new PnIo_CyclicServiceDataUnit((short) 0,(short) 0, (short) 0),
+                16696,
+                false,
+                false,
+                false,
+                false,
+                false,
+                false));
+    }
+
+
     @Override
     public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest subscriptionRequest) {
         CompletableFuture<PlcSubscriptionResponse> future = new CompletableFuture<>();
-        future.completeExceptionally(new NotImplementedException());
+        if (!connected) {
+            throw new RuntimeException("Not Connected");
+        }
+
+        final InetSocketAddress remoteAddress = (InetSocketAddress) rawSocketChannel.getRemoteAddress();
+
+        try {
+            // Create the packet
+            final Ethernet_FramePayload_PnDcp profinetConnectionRequest = createProfinetCyclicDataRequest();
+            // Serialize it to a byte-payload
+            WriteBufferByteBased writeBuffer = new WriteBufferByteBased(profinetConnectionRequest.getLengthInBytes());
+            profinetConnectionRequest.serialize(writeBuffer);
+            // Create a udp packet.
+            DatagramPacket connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
+            connectRequestPacket.setAddress(remoteAddress.getAddress());
+            connectRequestPacket.setPort(remoteAddress.getPort());
+            // Send it.
+
+            udpSocket.send(connectRequestPacket);
+
+            // Receive the response.
+            byte[] resultBuffer = new byte[profinetConnectionRequest.getLengthInBytes()];
+            DatagramPacket connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
+            udpSocket.receive(connectResponsePacket);
+            ReadBufferByteBased readBuffer = new ReadBufferByteBased(resultBuffer);
+            final DceRpc_Packet dceRpc_packet = DceRpc_Packet.staticParse(readBuffer);
+            if ((dceRpc_packet.getOperation() == DceRpc_Operation.CONNECT) && (dceRpc_packet.getPacketType() == DceRpc_PacketType.RESPONSE)) {
+                if (dceRpc_packet.getPayload().getPacketType() == DceRpc_PacketType.RESPONSE) {
+                    // Get the remote MAC address and store it in the context.
+                    final PnIoCm_Packet_Res connectResponse = (PnIoCm_Packet_Res) dceRpc_packet.getPayload();
+                    if ((connectResponse.getBlocks().size() > 0) && (connectResponse.getBlocks().get(0) instanceof PnIoCm_Block_ArRes)) {
+                        final PnIoCm_Block_ArRes pnIoCm_block_arRes = (PnIoCm_Block_ArRes) connectResponse.getBlocks().get(0);
+                        profinetDriverContext.setRemoteMacAddress(pnIoCm_block_arRes.getCmResponderMacAddr());
+
+                        // Update the raw-socket transports filter expression.
+                        ((RawSocketChannel) channel).setRemoteMacAddress(org.pcap4j.util.MacAddress.getByAddress(profinetDriverContext.getRemoteMacAddress().getAddress()));
+                    } else {
+                        throw new PlcException("Unexpected type of first block.");
+                    }
+                } else {
+                    throw new PlcException("Unexpected response");
+                }
+            } else if (dceRpc_packet.getPacketType() == DceRpc_PacketType.REJECT) {
+                throw new PlcException("Device rejected connection request");
+            } else {
+                throw new PlcException("Unexpected response");
+            }
+        } catch (SerializationException e) {
+            throw new RuntimeException(e);
+        } catch (ParseException e) {
+            throw new RuntimeException(e);
+        } catch (PlcException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
         return future;
     }
 
-    @Override
+        @Override
     protected void decode(ConversationContext<Ethernet_Frame> context, Ethernet_Frame msg) throws Exception {
         super.decode(context, msg);
     }
@@ -342,7 +435,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                         new PnIoCm_Block_ExpectedSubmoduleReq((short) 1, (short) 0,
                             Collections.singletonList(
                                 new PnIoCm_ExpectedSubmoduleBlockReqApi(0,
-                                    0x00000010, 0x00000000,
+                                    0x00000001, 0x00000000,
                                     Arrays.asList(
                                         new PnIoCm_Submodule_NoInputNoOutputData(0x0001,
                                             0x00000001, false, false,
@@ -411,29 +504,6 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                         (short) 1,
                         (short) 0,
                         MultipleInterfaceModeNameOfDevice.NAME_PROVIDED_BY_LLDP
-                    ),
-                    new IODWriteRequestHeader(
-                        (short) 1,
-                        (short) 0,
-                        2,
-                        ARUUID,
-                        0x00000000,
-                        0x0000,
-                        0x8001,
-                        0x802b,
-                        40
-                    ),
-                    new PDPortDataCheck(
-                        (short) 1,
-                        (short) 0,
-                        0x0000,
-                        0x8001,
-                        new CheckPeers(
-                            (short) 1,
-                            (short) 0,
-                            new PascalString("port-001"),
-                            new PascalString("plc4x")
-                        )
                     )
                 ))
         );
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 2cabb832f..9a964a6e1 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -362,8 +362,8 @@
     [virtual       PnDcp_FrameId     frameId       'STATIC_CALL("getFrameId", frameIdValue)']
     [typeSwitch frameId
         ['RT_CLASS_1' PnDcp_Pdu_RealTimeCyclic
-            // TODO: This type needs to be implemented ...
-//            [simple   PnIo_CyclicServiceDataUnit dataUnit                 ]
+            // TODO: This type needs to be implemented based of the configuration and gsd file ...
+            [simple   PnIo_CyclicServiceDataUnit dataUnit                 ]
             [simple   uint 16                    cycleCounter             ]
             // Data Status Start (4.7.2.1.3)
             [simple   bit                        ignore                   ]
@@ -488,8 +488,12 @@
     ]
 ]
 
-//[discriminatedType PnIo_CyclicServiceDataUnit
-//]
+[type PnIo_CyclicServiceDataUnit
+    [simple uint 8      dummyIOData1]
+    [simple uint 8      dummyIOData2]
+    [simple uint 8      dummyIOData3]
+    [padding  uint 8      pad '0x00'          '37']
+]
 
 [discriminatedType PnDcp_Block
     [discriminator PnDcp_BlockOptions option                   ]


[plc4x] 02/13: fix(plc4j(profinet): Connections are working for my setup

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit b55c5d22550f970cbd34a73fdf0c3292ad674ecd
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Sep 8 07:03:32 2022 -0600

    fix(plc4j(profinet): Connections are working for my setup
---
 .../profinet/protocol/ProfinetProtocolLogic.java   | 46 ++++++----------------
 .../apache/plc4x/java/profinet/ProfinetPoc.java    |  2 +-
 .../resources/protocols/profinet/profinet.mspec    |  2 +-
 3 files changed, 13 insertions(+), 37 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
index 62b209c10..836732954 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
@@ -40,10 +40,7 @@ import org.slf4j.LoggerFactory;
 import java.io.IOException;
 import java.net.*;
 import java.time.Duration;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Optional;
-import java.util.UUID;
+import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -133,6 +130,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
             connectRequestPacket.setAddress(remoteAddress.getAddress());
             connectRequestPacket.setPort(remoteAddress.getPort());
             // Send it.
+
             udpSocket.send(connectRequestPacket);
 
             // Receive the response.
@@ -221,14 +219,14 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                 new DceRpc_InterfaceUuid_DeviceInterface(),
                 profinetDriverContext.getDceRpcActivityUuid(),
                 0, 0, DceRpc_Operation.CONNECT,
-                new PnIoCm_Packet_Req(404, 404, 404, 0, 404,
+                new PnIoCm_Packet_Req(16696, 16696, 0, 0,
                     Arrays.asList(
                         new PnIoCm_Block_ArReq((short) 1, (short) 0, PnIoCm_ArType.IO_CONTROLLER,
                             new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51")),
                             profinetDriverContext.getSessionKey(),
                             profinetDriverContext.getLocalMacAddress(),
                             new Uuid(Hex.decodeHex("dea000006c9711d1827100640008002a")),
-                            false, false, false,
+                            false, true, false,
                             false, PnIoCm_CompanionArType.SINGLE_AR, false,
                             true, false, PnIoCm_State.ACTIVE,
                             600,
@@ -249,31 +247,23 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                                     Arrays.asList(
                                         new PnIoCm_IoDataObject(0, 0x0001, 0),
                                         new PnIoCm_IoDataObject(0, 0x8000, 1),
-                                        new PnIoCm_IoDataObject(0, 0x8001, 2),
-                                        new PnIoCm_IoDataObject(0, 0x8002, 3),
-                                        new PnIoCm_IoDataObject(1, 0x0001, 4)
+                                        new PnIoCm_IoDataObject(0, 0x8001, 2)
                                     ),
-                                    Collections.singletonList(
-                                        new PnIoCm_IoCs(0x0001, 0x0001, 0x0019)
-                                    ))
+                                    new ArrayList<PnIoCm_IoCs>(0))
                             )),
                         new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.OUTPUT_CR,
                             0x0002, 0x8892, false, false,
                             false, false, PnIoCm_RtClass.RT_CLASS_2, 40,
-                            0x8000, 128, 8, 1, 0, 0xffffffff,
+                            0xFFFF, 128, 8, 1, 0, 0xffffffff,
                             3, 3, 0xC000,
                             new MacAddress(Hex.decodeHex("000000000000")),
                             Collections.singletonList(
                                 new PnIoCm_IoCrBlockReqApi(
-                                    Collections.singletonList(
-                                        new PnIoCm_IoDataObject(0x0001, 0x0001, 0x0005)
-                                    ),
+                                    new ArrayList<PnIoCm_IoDataObject>(0),
                                     Arrays.asList(
                                         new PnIoCm_IoCs(0, 0x0001, 0),
                                         new PnIoCm_IoCs(0, 0x8000, 1),
-                                        new PnIoCm_IoCs(0, 0x8001, 2),
-                                        new PnIoCm_IoCs(0, 0x8002, 3),
-                                        new PnIoCm_IoCs(1, 0x0001, 4)
+                                        new PnIoCm_IoCs(0, 0x8001, 2)
                                     )
                                 )
                             )
@@ -287,29 +277,15 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                                             0x00000001, false, false,
                                             false, false),
                                         new PnIoCm_Submodule_NoInputNoOutputData(0x8000,
-                                            0x00000002, false, false,
+                                            0x00008000, false, false,
                                             false, false),
                                         new PnIoCm_Submodule_NoInputNoOutputData(0x8001,
-                                            0x00000003, false, false,
-                                            false, false),
-                                        new PnIoCm_Submodule_NoInputNoOutputData(0x8002,
-                                            0x00000003, false, false,
+                                            0x00008001, false, false,
                                             false, false)
                                     )
                                 )
                             )
                         ),
-                        new PnIoCm_Block_ExpectedSubmoduleReq((short) 1, (short) 0,
-                            Collections.singletonList(
-                                new PnIoCm_ExpectedSubmoduleBlockReqApi(1,
-                                    0x00000022, 0x00000000, Collections.singletonList(
-                                    new PnIoCm_Submodule_InputAndOutputData(0x0001, 0x00000010,
-                                        false, false, false,
-                                        false, 20, (short) 1, (short) 1,
-                                        6, (short) 1, (short) 1))
-                                )
-                            )
-                        ),
                         new PnIoCm_Block_AlarmCrReq((short) 1, (short) 0,
                             PnIoCm_AlarmCrType.ALARM_CR, 0x8892, false, false, 1, 3,
                             0x0000, 200, 0xC000, 0xA000)
diff --git a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetPoc.java b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetPoc.java
index 401258f04..90ccefbc3 100644
--- a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetPoc.java
+++ b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetPoc.java
@@ -40,7 +40,7 @@ public class ProfinetPoc {
             new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
             new DceRpc_InterfaceUuid_DeviceInterface(), generateActivityUuid(),
             0, 0, DceRpc_Operation.CONNECT,
-            new PnIoCm_Packet_Req(404, 404, 404, 0, 404,
+            new PnIoCm_Packet_Req(404, 404, 0, 404,
                 Arrays.asList(
                     new PnIoCm_Block_ArReq((short) 1, (short) 0, PnIoCm_ArType.IO_CONTROLLER,
                         new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51")), 2,
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index b6d05a84a..35bdaf24c 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -651,7 +651,7 @@
     [typeSwitch packetType
         ['REQUEST' PnIoCm_Packet_Req
             [simple uint 32      argsMaximum                          ]
-            [simple uint 32      argsLength                           ]
+            [implicit uint 32    argsLength       'lengthInBytes - 20']
             [simple uint 32      arrayMaximumCount                    ]
             [simple uint 32      arrayOffset                          ]
             [simple uint 32      arrayActualCount                     ]


[plc4x] 13/13: fix(plc4j/profinet): Continued to split device logic out.

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit e99d97c6b474c50ac653ca942a2b7219f0ebfcc2
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Oct 6 07:52:35 2022 -0600

    fix(plc4j/profinet): Continued to split device logic out.
---
 .../java/profinet/device/ProfinetCallable.java     |  25 +-
 .../plc4x/java/profinet/device/ProfinetDevice.java | 136 +++++++++-
 .../profinet/device/ProfinetMessageWrapper.java    |  27 +-
 .../profinet/protocol/ProfinetProtocolLogic.java   | 294 +--------------------
 .../HelloPlc4xDiscoverAndBrowse.java               |  35 +--
 .../src/main/resources/logback.xml                 |  12 +-
 6 files changed, 199 insertions(+), 330 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java
index fd117c9f4..1cde0c154 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java
@@ -1,10 +1,29 @@
+/*
+ * 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.profinet.device;
 
 import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.profinet.readwrite.DceRpc_Packet;
 
-public interface ProfinetCallable {
-    void handle(DceRpc_Packet packet) throws PlcException;
+public interface ProfinetCallable<T> {
+    void handle(T packet) throws PlcException;
 
-    DceRpc_Packet create() throws PlcException;
+    T create() throws PlcException;
 }
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java
index 80f10d961..d2bd0887e 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java
@@ -61,6 +61,15 @@ public class ProfinetDevice {
 
     private AtomicInteger sessionKeyGenerator = new AtomicInteger(1);
 
+    private static final Uuid ARUUID;
+    static {
+        try {
+            ARUUID = new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51"));
+        } catch (DecoderException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
 
     private void closeUDPSocket() {
         // Handle the closing of the connection, might need to send some messages beforehand.
@@ -70,7 +79,7 @@ public class ProfinetDevice {
         }
     }
 
-    private boolean createUDPSocket() {
+    private boolean createUdpSocket() {
         if (state != ProfinetDeviceState.IDLE) {
             closeUDPSocket();
         }
@@ -82,6 +91,7 @@ public class ProfinetDevice {
 
         rawSocketChannel = (RawSocketChannel) channel;
 
+
         // Create an udp socket
         try {
             udpSocket = new DatagramSocket();
@@ -94,16 +104,28 @@ public class ProfinetDevice {
     }
 
     public boolean onConnect() {
-        if (!createUDPSocket()) {
+        if (!createUdpSocket()) {
             // Unable to create UDP connection
             return false;
         }
 
-        ProfinetMessageWrapper.sendMessage(
+        ProfinetMessageWrapper.sendUdpMessage(
             new CreateConnection(),
             this
         );
 
+        ProfinetMessageWrapper.sendUdpMessage(
+            new WriteParameters(),
+            this
+        );
+
+        ProfinetMessageWrapper.sendUdpMessage(
+            new WriteParametersEnd(),
+            this
+        );
+
+
+
         return false;
     }
 
@@ -171,6 +193,10 @@ public class ProfinetDevice {
         return this.udpSocket;
     }
 
+    public RawSocketChannel getRawSocket() {
+        return this.rawSocketChannel;
+    }
+
     public InetAddress getIpAddress() throws UnknownHostException {
         return InetAddress.getByName(this.ipAddress);
     }
@@ -179,7 +205,7 @@ public class ProfinetDevice {
         return DEFAULT_UDP_PORT;
     }
 
-    public class CreateConnection implements ProfinetCallable {
+    public class CreateConnection implements ProfinetCallable<DceRpc_Packet> {
 
         public DceRpc_Packet create() throws PlcException {
             try {
@@ -298,4 +324,106 @@ public class ProfinetDevice {
             }
         }
     }
+
+    public class WriteParameters implements ProfinetCallable<DceRpc_Packet> {
+        public DceRpc_Packet create() {
+            return new DceRpc_Packet(
+                DceRpc_PacketType.REQUEST, true, false, false,
+                IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
+                new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
+                new DceRpc_InterfaceUuid_DeviceInterface(),
+                uuid,
+                0, 1, DceRpc_Operation.WRITE,
+                new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+                    Arrays.asList(
+                        new IODWriteRequestHeader(
+                            (short) 1,
+                            (short) 0,
+                            0,
+                            ARUUID,
+                            0x00000000,
+                            0x0000,
+                            0x0000,
+                            0xe040,
+                            180
+                        ),
+                        new IODWriteRequestHeader(
+                            (short) 1,
+                            (short) 0,
+                            1,
+                            ARUUID,
+                            0x00000000,
+                            0x0000,
+                            0x8000,
+                            0x8071,
+                            12
+                        ),
+                        new PDInterfaceAdjust(
+                            (short) 1,
+                            (short) 0,
+                            MultipleInterfaceModeNameOfDevice.NAME_PROVIDED_BY_LLDP
+                        )
+                    ))
+            );
+        }
+
+        @Override
+        public void handle(DceRpc_Packet packet) throws PlcException {
+            logger.debug("Received a Write Parameter Response");
+        }
+    }
+
+    public class WriteParametersEnd implements ProfinetCallable<DceRpc_Packet> {
+        public DceRpc_Packet create() {
+            return new DceRpc_Packet(
+                DceRpc_PacketType.REQUEST, true, false, false,
+                IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
+                new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
+                new DceRpc_InterfaceUuid_DeviceInterface(),
+                uuid,
+                0, 1, DceRpc_Operation.CONTROL,
+                new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+                    Arrays.asList(
+                        new PnIoCm_Control_Request(
+                            (short) 1,
+                            (short) 0,
+                            ARUUID,
+                            0x0001,
+                            0x0001
+                        )
+                    ))
+            );
+        }
+
+        @Override
+        public void handle(DceRpc_Packet packet) throws PlcException {
+            logger.debug("Received a Write Parameter End Response");
+        }
+    }
+
+    public class CyclicData implements ProfinetCallable<Ethernet_Frame> {
+        public Ethernet_Frame create() {
+            return new Ethernet_Frame(
+                macAddress,
+                macAddress,
+                new Ethernet_FramePayload_PnDcp(
+                new PnDcp_Pdu_RealTimeCyclic(
+                    0x8000,
+                    new PnIo_CyclicServiceDataUnit((short) 0,(short) 0, (short) 0),
+                    16696,
+                    false,
+                    false,
+                    false,
+                    false,
+                    false,
+                    false)));
+        }
+
+        @Override
+        public void handle(Ethernet_Frame packet) throws PlcException {
+            logger.debug("Received a Write Parameter End Response");
+        }
+    }
+
+
 }
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java
index bee0ae8d3..e56b92d61 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java
@@ -1,5 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   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.profinet.device;
 
+import io.netty.channel.Channel;
 import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.profinet.readwrite.*;
 import org.apache.plc4x.java.spi.generation.ParseException;
@@ -13,7 +33,7 @@ import java.net.DatagramPacket;
 
 public class ProfinetMessageWrapper {
 
-    public static void sendMessage(ProfinetCallable callable, ProfinetDevice context) throws RuntimeException {
+    public static void sendUdpMessage(ProfinetCallable<DceRpc_Packet> callable, ProfinetDevice context) throws RuntimeException {
         try {
             DceRpc_Packet packet = callable.create();
             // Serialize it to a byte-payload
@@ -43,6 +63,9 @@ public class ProfinetMessageWrapper {
         } catch (PlcException e) {
             throw new RuntimeException(e);
         }
-
     }
+
+
+
+
 }
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
index fe931765c..8d31d198f 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
@@ -70,15 +70,6 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
 
     private ProfinetConfiguration configuration;
 
-    private static final Uuid ARUUID;
-
-    static {
-        try {
-            ARUUID = new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51"));
-        } catch (DecoderException e) {
-            throw new RuntimeException(e);
-        }
-    }
 
     @Override
     public void setConfiguration(ProfinetConfiguration configuration) {
@@ -153,110 +144,8 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
             device.getValue().onConnect();
         }
 
-        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-        // Create the connection request.
-        try {
-            // Create the packet
-            final DceRpc_Packet profinetConnectionRequest = createProfinetConnectionRequest();
-            // Serialize it to a byte-payload
-            WriteBufferByteBased writeBuffer = new WriteBufferByteBased(profinetConnectionRequest.getLengthInBytes());
-            profinetConnectionRequest.serialize(writeBuffer);
-            // Create a udp packet.
-            DatagramPacket connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
-            connectRequestPacket.setAddress(remoteAddress.getAddress());
-            connectRequestPacket.setPort(remoteAddress.getPort());
-            // Send it.
-
-            udpSocket.send(connectRequestPacket);
-
-            // Receive the response.
-            byte[] resultBuffer = new byte[profinetConnectionRequest.getLengthInBytes()];
-            DatagramPacket connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
-            udpSocket.receive(connectResponsePacket);
-            ReadBufferByteBased readBuffer = new ReadBufferByteBased(resultBuffer);
-            final DceRpc_Packet dceRpc_packet = DceRpc_Packet.staticParse(readBuffer);
-            if ((dceRpc_packet.getOperation() == DceRpc_Operation.CONNECT) && (dceRpc_packet.getPacketType() == DceRpc_PacketType.RESPONSE)) {
-                if (dceRpc_packet.getPayload().getPacketType() == DceRpc_PacketType.RESPONSE) {
-                    // Get the remote MAC address and store it in the context.
-                    final PnIoCm_Packet_Res connectResponse = (PnIoCm_Packet_Res) dceRpc_packet.getPayload();
-                    if ((connectResponse.getBlocks().size() > 0) && (connectResponse.getBlocks().get(0) instanceof PnIoCm_Block_ArRes)) {
-                        final PnIoCm_Block_ArRes pnIoCm_block_arRes = (PnIoCm_Block_ArRes) connectResponse.getBlocks().get(0);
-                        profinetDriverContext.setRemoteMacAddress(pnIoCm_block_arRes.getCmResponderMacAddr());
-
-                        // Update the raw-socket transports filter expression.
-                        ((RawSocketChannel) channel).setRemoteMacAddress(org.pcap4j.util.MacAddress.getByAddress(profinetDriverContext.getRemoteMacAddress().getAddress()));
-                    } else {
-                        throw new PlcException("Unexpected type of first block.");
-                    }
-                } else {
-                    throw new PlcException("Unexpected response");
-                }
-            } else if (dceRpc_packet.getPacketType() == DceRpc_PacketType.REJECT) {
-                throw new PlcException("Device rejected connection request");
-            } else {
-                throw new PlcException("Unexpected response");
-            }
-
-            // Create the packet
-            final DceRpc_Packet profinetAdvancedConnectionWriteRequest = createProfinetAdvancedConnectionWriteRequest();
-            // Serialize it to a byte-payload
-            writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionWriteRequest.getLengthInBytes());
-            profinetAdvancedConnectionWriteRequest.serialize(writeBuffer);
-            // Create a udp packet.
-            connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
-            connectRequestPacket.setAddress(remoteAddress.getAddress());
-            connectRequestPacket.setPort(remoteAddress.getPort());
-            // Send it.
-
-            udpSocket.send(connectRequestPacket);
-
-            // Receive the response.
-            resultBuffer = new byte[profinetAdvancedConnectionWriteRequest.getLengthInBytes()];
-            connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
-            udpSocket.receive(connectResponsePacket);
-
-
-            // Create the packet
-            final DceRpc_Packet profinetAdvancedConnectionParameterEnd = createProfinetAdvancedConnectionParameterEnd();
-            // Serialize it to a byte-payload
-            writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionParameterEnd.getLengthInBytes());
-            profinetAdvancedConnectionParameterEnd.serialize(writeBuffer);
-            // Create a udp packet.
-            connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
-            connectRequestPacket.setAddress(remoteAddress.getAddress());
-            connectRequestPacket.setPort(remoteAddress.getPort());
-            // Send it.
-
-            udpSocket.send(connectRequestPacket);
-
-            // Receive the response.
-            resultBuffer = new byte[profinetAdvancedConnectionParameterEnd.getLengthInBytes()];
-            connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
-            udpSocket.receive(connectResponsePacket);
-
-            // Create the packet
-            final DceRpc_Packet profinetAdvancedConnectionApplicationReady = createProfinetAdvancedConnectionApplicationReady();
-            // Serialize it to a byte-payload
-            writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionApplicationReady.getLengthInBytes());
-            profinetAdvancedConnectionApplicationReady.serialize(writeBuffer);
-            // Create a udp packet.
-            connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
-            connectRequestPacket.setAddress(remoteAddress.getAddress());
-            connectRequestPacket.setPort(remoteAddress.getPort());
-            // Send it.
-
-            udpSocket.send(connectRequestPacket);
-
-            // Receive the response.
-            resultBuffer = new byte[profinetAdvancedConnectionApplicationReady.getLengthInBytes()];
-            connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
-            udpSocket.receive(connectResponsePacket);
-            context.fireConnected();
-            connected = true;
-
-        } catch (SerializationException | IOException | PlcException | ParseException e) {
-            logger.error("Error", e);
-        }
+        context.fireConnected();
+        connected = true;
     }
 
     @Override
@@ -390,188 +279,9 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
         return Optional.empty();
     }
 
-    private DceRpc_Packet createProfinetConnectionRequest() throws PlcException {
-        try {
-            return new DceRpc_Packet(
-                DceRpc_PacketType.REQUEST, true, false, false,
-                IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
-                new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
-                new DceRpc_InterfaceUuid_DeviceInterface(),
-                profinetDriverContext.getDceRpcActivityUuid(),
-                0, 0, DceRpc_Operation.CONNECT,
-                new PnIoCm_Packet_Req(16696, 16696, 0, 0,
-                    Arrays.asList(
-                        new PnIoCm_Block_ArReq((short) 1, (short) 0, PnIoCm_ArType.IO_CONTROLLER,
-                            new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51")),
-                            profinetDriverContext.getSessionKey(),
-                            profinetDriverContext.getLocalMacAddress(),
-                            new Uuid(Hex.decodeHex("dea000006c9711d1827100640008002a")),
-                            false, true, false,
-                            false, PnIoCm_CompanionArType.SINGLE_AR, false,
-                            true, false, PnIoCm_State.ACTIVE,
-                            600,
-                            // This actually needs to be set to this value and not the real port number.
-                            0x8892,
-                            // It seems that it must be set to this value, or it won't work.
-                            "plc4x"),
-                        new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.INPUT_CR,
-                            0x0001,
-                            0x8892,
-                            false, false,
-                            false, false, PnIoCm_RtClass.RT_CLASS_2, 40,
-                            0xBBF0, 128, 8, 1, 0, 0xffffffff,
-                            50, 50, 0xC000,
-                            new org.apache.plc4x.java.profinet.readwrite.MacAddress(Hex.decodeHex("000000000000")),
-                            Collections.singletonList(
-                                new PnIoCm_IoCrBlockReqApi(
-                                    Arrays.asList(
-                                        new PnIoCm_IoDataObject(0, 0x0001, 0),
-                                        new PnIoCm_IoDataObject(0, 0x8000, 1),
-                                        new PnIoCm_IoDataObject(0, 0x8001, 2)
-                                    ),
-                                    new ArrayList<PnIoCm_IoCs>(0))
-                            )),
-                        new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.OUTPUT_CR,
-                            0x0002, 0x8892, false, false,
-                            false, false, PnIoCm_RtClass.RT_CLASS_2, 40,
-                            0xFFFF, 128, 8, 1, 0, 0xffffffff,
-                            50, 50, 0xC000,
-                            new MacAddress(Hex.decodeHex("000000000000")),
-                            Collections.singletonList(
-                                new PnIoCm_IoCrBlockReqApi(
-                                    new ArrayList<PnIoCm_IoDataObject>(0),
-                                    Arrays.asList(
-                                        new PnIoCm_IoCs(0, 0x0001, 0),
-                                        new PnIoCm_IoCs(0, 0x8000, 1),
-                                        new PnIoCm_IoCs(0, 0x8001, 2)
-                                    )
-                                )
-                            )
-                        ),
-                        new PnIoCm_Block_ExpectedSubmoduleReq((short) 1, (short) 0,
-                            Collections.singletonList(
-                                new PnIoCm_ExpectedSubmoduleBlockReqApi(0,
-                                    0x00000001, 0x00000000,
-                                    Arrays.asList(
-                                        new PnIoCm_Submodule_NoInputNoOutputData(0x0001,
-                                            0x00000001, false, false,
-                                            false, false),
-                                        new PnIoCm_Submodule_NoInputNoOutputData(0x8000,
-                                            0x00008000, false, false,
-                                            false, false),
-                                        new PnIoCm_Submodule_NoInputNoOutputData(0x8001,
-                                            0x00008001, false, false,
-                                            false, false)
-                                    )
-                                )
-                            )
-                        ),
-                        new PnIoCm_Block_AlarmCrReq((short) 1, (short) 0,
-                            PnIoCm_AlarmCrType.ALARM_CR, 0x8892, false, false, 1, 3,
-                            0x0000, 200, 0xC000, 0xA000)
-                    ))
-            );
-
-            /*// Build the UDP/IP/EthernetFrame to transport the package.
-            return new Ethernet_Frame(profinetDriverContext.getRemoteMacAddress(), profinetDriverContext.getLocalMacAddress(),
-                new Ethernet_FramePayload_IPv4(ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE), (short) 64,
-                    profinetDriverContext.getLocalIpAddress(), profinetDriverContext.getRemoteIpAddress(),
-                    new Udp_Packet(profinetDriverContext.getLocalUdpPort(), profinetDriverContext.getRemoteUdpPort(),
-                        dceRpcConnectionRequest)));*/
-        } catch (DecoderException e) {
-            throw new PlcException("Error creating connection request", e);
-        }
-    }
-
-    private DceRpc_Packet createProfinetAdvancedConnectionWriteRequest() throws PlcException {
-
-        return new DceRpc_Packet(
-            DceRpc_PacketType.REQUEST, true, false, false,
-            IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
-            new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
-            new DceRpc_InterfaceUuid_DeviceInterface(),
-            profinetDriverContext.getDceRpcActivityUuid(),
-            0, 1, DceRpc_Operation.WRITE,
-            new PnIoCm_Packet_Req(16696, 16696, 0, 244,
-                Arrays.asList(
-                    new IODWriteRequestHeader(
-                        (short) 1,
-                        (short) 0,
-                        0,
-                        ARUUID,
-                        0x00000000,
-                        0x0000,
-                        0x0000,
-                        0xe040,
-                        180
-                        ),
-                    new IODWriteRequestHeader(
-                        (short) 1,
-                        (short) 0,
-                        1,
-                        ARUUID,
-                        0x00000000,
-                        0x0000,
-                        0x8000,
-                        0x8071,
-                        12
-                    ),
-                    new PDInterfaceAdjust(
-                        (short) 1,
-                        (short) 0,
-                        MultipleInterfaceModeNameOfDevice.NAME_PROVIDED_BY_LLDP
-                    )
-                ))
-        );
-    }
-
-    private DceRpc_Packet createProfinetAdvancedConnectionParameterEnd() throws PlcException {
-
-        return new DceRpc_Packet(
-            DceRpc_PacketType.REQUEST, true, false, false,
-            IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
-            new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
-            new DceRpc_InterfaceUuid_DeviceInterface(),
-            profinetDriverContext.getDceRpcActivityUuid(),
-            0, 1, DceRpc_Operation.CONTROL,
-            new PnIoCm_Packet_Req(16696, 16696, 0, 244,
-                Arrays.asList(
-                    new PnIoCm_Control_Request(
-                        (short) 1,
-                        (short) 0,
-                        ARUUID,
-                        0x0001,
-                        0x0001
-                    )
-                ))
-        );
-    }
 
 
 
-    private DceRpc_Packet createProfinetAdvancedConnectionApplicationReady() throws PlcException {
-
-        return new DceRpc_Packet(
-            DceRpc_PacketType.REQUEST, true, false, false,
-            IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
-            new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
-            new DceRpc_InterfaceUuid_DeviceInterface(),
-            profinetDriverContext.getDceRpcActivityUuid(),
-            0, 1, DceRpc_Operation.CONTROL,
-            new PnIoCm_Packet_Req(16696, 16696, 0, 244,
-                Arrays.asList(
-                    new PnIoCM_Block_Request(
-                        (short) 1,
-                        (short) 0,
-                        ARUUID,
-                        0x0001,
-                        0x0002,
-                        0x0000
-                    )
-                ))
-        );
-    }
-
 
 
 }
diff --git a/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4xDiscoverAndBrowse.java b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4xDiscoverAndBrowse.java
index 9b8418dfc..e1b863745 100644
--- a/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4xDiscoverAndBrowse.java
+++ b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4xDiscoverAndBrowse.java
@@ -35,33 +35,22 @@ public class HelloPlc4xDiscoverAndBrowse {
     public static void main(String[] args) throws Exception {
         // Iterate over all installed drivers and execute their browse functionality (If they support it)
         PlcDriverManager driverManager = new PlcDriverManager();
-        for (String protocolCode : driverManager.listDrivers()) {
-            PlcDriver driver = driverManager.getDriver(protocolCode);
-            if (driver.getMetadata().canDiscover()) {
-                logger.info("Performing discovery for {} protocol", driver.getProtocolName());
-
-                PlcDiscoveryRequest discoveryRequest = driver.discoveryRequestBuilder().build();
-
-                discoveryRequest.executeWithHandler(discoveryItem -> {
-                    logger.info(" - Found device with connection-url {}", discoveryItem.getConnectionUrl());
-                    try (PlcConnection connection = driverManager.getConnection(discoveryItem.getConnectionUrl())) {
-                        if (connection.getMetadata().canBrowse()) {
-                            PlcBrowseRequest browseRequest = connection.browseRequestBuilder().build();
-                            browseRequest.execute().whenComplete((browseResponse, throwable) -> {
-                                if (throwable != null) {
-                                    throwable.printStackTrace();
-                                } else {
-                                    for (PlcBrowseItem value : browseResponse.getValues()) {
-                                        outputBrowseItem(value, 0);
-                                    }
-                                }
-                            });
+        //try (PlcConnection connection = driverManager.getConnection("opcua:tcp://missy-nuc:53530/plc4x")) {
+        try (PlcConnection connection = driverManager.getConnection("profinet:raw://192.168.90.128")) {
+            if (connection.getMetadata().canBrowse()) {
+                PlcBrowseRequest browseRequest = connection.browseRequestBuilder().build();
+                browseRequest.execute().whenComplete((browseResponse, throwable) -> {
+                    if (throwable != null) {
+                        throwable.printStackTrace();
+                    } else {
+                        for (PlcBrowseItem value : browseResponse.getValues()) {
+                            outputBrowseItem(value, 0);
                         }
-                    } catch (Exception e) {
-                        throw new RuntimeException(e);
                     }
                 });
             }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
         }
     }
 
diff --git a/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml
index 2641def3a..f64d08114 100644
--- a/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml
+++ b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml
@@ -28,13 +28,13 @@
   </appender>
 
   <!-- Remove the messages we're not interested in -->
-  <logger name="io.netty.channel" level="ERROR"/>
-  <logger name="org.pcap4j" level="WARN"/>
-  <logger name="org.apache.plc4x.java.modbus.tcp.discovery" level="WARN"/>
-  <logger name="org.apache.plc4x.java.transport" level="WARN"/>
-  <logger name="org.apache.plc4x.java.spi" level="WARN"/>
+  <logger name="io.netty.channel" level="TRACE"/>
+  <logger name="org.pcap4j" level="TRACE"/>
+  <logger name="org.apache.plc4x.java.modbus.tcp.discovery" level="TRACE"/>
+  <logger name="org.apache.plc4x.java.transport" level="TRACE"/>
+  <logger name="org.apache.plc4x.java.spi" level="TRACE"/>
 
-  <root level="INFO">
+  <root level="TRACE">
     <appender-ref ref="STDOUT" />
   </root>
 


[plc4x] 01/13: fix(plc4j/profinet): Fix to remove spaces from the generated connection string.

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit afeb1a491b2b11a4326b23cfe48af0ac1be7c042
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 4 09:14:24 2022 -0600

    fix(plc4j/profinet): Fix to remove spaces from the generated connection string.
---
 .../apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index 5701f7c9c..5eb899bcc 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -145,13 +145,13 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
                                                 String deviceTypeName = "unknown";
                                                 if (blocks.containsKey(DEVICE_TYPE_NAME)) {
                                                     PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
-                                                    deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
+                                                    deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "_");
                                                 }
 
                                                 String deviceName = "unknown";
                                                 if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
                                                     PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
-                                                    deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
+                                                    deviceName = new String(block.getNameOfStation()).replace(" ", "_");
                                                 }
 
                                                 String role = "unknown";


[plc4x] 05/13: fix(plc4j(profinet): Continued to add the LLDP broadcast

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit bb01815f87977da5b37885a88dd3e963ae47a195
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Sep 8 16:53:28 2022 -0600

    fix(plc4j(profinet): Continued to add the LLDP broadcast
---
 .../resources/protocols/profinet/profinet.mspec    | 85 +++++++++++++++++++---
 1 file changed, 73 insertions(+), 12 deletions(-)

diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index b3c0d9ca0..19dcdcfee 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -89,22 +89,64 @@
 ]
 
 [type Lldp_Pdu
-    [simple     TlvType_Pdu             tlvChassisId]
-    [simple     TlvType_Pdu             tlvPortId]
-    [simple     TlvTimeToLive           tlvTimeToLive]
+    [array      LldpUnit                lldpParameters]
 ]
 
-[type TlvType_Pdu
-    [simple     uint 7                  tlvId                               ]
-    [implicit   uint 9                  tlvIdLength       'lengthInBytes'   ]
-    [simple     typeSubType             idSubType                           ]
-    [simple     vstring '(tlvIdLength * 8) +  1' Id                         ]
+[discriminatedType LldpUnit
+    [disciminator     uint 7                  tlvId                               ]
+    [implicit   uint 9                  tlvIdLength       'lengthInBytes'         ]                      ]
+    [typeSwitch idSubType
+        ['END_OF_LLDP'  EndOfLldp
+        [
+        ['CHASSIS_ID'   TlvChassisId
+            [simple     uint 8          chassisIdSubType                           ]
+            [simple     vstring '(tlvIdLength * 8) +  1' chassisId                 ]
+        ]
+        ['PORT_ID'   TlvPortId
+            [simple     uint 8          portIdSubType                              ]
+            [simple     vstring '(tlvIdLength * 8) +  1' portId                    ]
+        ]
+        ['PORT_ID'   TlvPortId
+            [simple     uint 16         tlvTimeToLive                              ]
+        ]
+        ['MANAGEMENT_ADDRESS' TlvManagementAddress
+            [implicit   uint 8          addressStringLength                        ]
+            [simple     ManagementAddressSubType  addressSubType                   ]
+            [simple     IpAddress       ipAddress                                  ]
+            [simple     uint 8          interfaceSubType                           ]
+            [simple     uint 32         interfaceNumber                            ]
+            [simple     uint 8          oidStringLength                            ]
+        ]
+        ['ORGANIZATION_SPECIFIC' TlvOrganizationSpecific
+            [simple     TlvOrganizationSpecificUnit     organizationSpecificUnit   ]
+        ]
+    ]
+[
+
+[type TlvOrganizationSpecificUnit(uint 9 unitLength)
+    [discriminator      uint 24         uniqueCode]
+    [typeSwitch uniqueCode
+        ['0x000ECF' TlvOrgSpecificProfibus
+            [simple     TlvOrgSpecificProfibusUnit      specificUnit               ]
+        ]
+        [´0x00120F' TlvOrgSpecificIeee8023
+            [simple     uint 8                          subType                    ]
+            [simple     uint 8                          negotiationSupport         ]
+            [simple     uint 16                         negotiationCapability      ]
+            [simple     uint 16                         operationalMauType         ]
+        ]
+    ]
+]
+
+[discriminatedType TlvOrgSpecificProfibusUnit
+    [discriminator  TlvProfibusSubType  subType]
+    [typeSwitch subType
+    ]
 ]
 
-[type TlvTimeToLive
-    [simple     uint 7                  tlvId                               ]
-    [implicit   uint 9                  tlvIdLength       'lengthInBytes'   ]
-    [simple     uint 16                 tlvTimeToLive]
+[enum   TlvProfibusSubType
+    ['0x02' PORT_STATUS]
+    [´0x05' CHASSIS_MAC]
 ]
 
 // 4.10.3.2
@@ -259,6 +301,25 @@
     ['0x0A' CANCEL_ACKNOWLEDGE   ]
 ]
 
+//LLDP Specific
+[enum uint 7 TlvType
+    ['0x00' END_OF_LLDP          ]
+    ['0x01' CHASSIS_ID           ]
+    ['0x02' PORT_ID              ]
+    ['0x03' TIME_TO_LIVE         ]
+    ['0x04' PORT_DESCRIPTION     ]
+    ['0x05' SYSTEM_NAME          ]
+    ['0x06' SYSTEM_DESCRIPTION   ]
+    ['0x07' SYSTEM_CAPABILITIES  ]
+    ['0x08' MANAGEMENT_ADDRESS    ]
+    ['0xFF' ORGANIZATION_SPECIFIC]
+]
+
+[enum uint 8 ManagementAddressSubType
+    ['0x00' UNKNOWN              ]
+    ['0x01' IPV4                 ]
+]
+
 // 4.10.3.2.14
 [enum uint 16 DceRpc_Operation
     ['0x0000' CONNECT      ]


[plc4x] 12/13: chore(plc4j/profinet): escape the device name and type within the connection string

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 36b3260cc1d99b869b8302bd3094300f28dc40f6
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 11 11:15:59 2022 -0600

    chore(plc4j/profinet): escape the device name and type within the connection string
---
 .../profinet/discovery/ProfinetPlcDiscoverer.java  | 610 +++++++--------------
 1 file changed, 192 insertions(+), 418 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index f0a1a3a43..5701f7c9c 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -18,8 +18,7 @@
  */
 package org.apache.plc4x.java.profinet.discovery;
 
-import org.apache.commons.codec.DecoderException;
-import org.apache.commons.codec.binary.Hex;
+import org.apache.plc4x.java.api.exceptions.PlcException;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryItemHandler;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
@@ -48,30 +47,20 @@ import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
-import java.util.function.Function;
 
 public class ProfinetPlcDiscoverer implements PlcDiscoverer {
 
     private static final EtherType PN_EtherType = EtherType.getInstance((short) 0x8892);
-    private static final EtherType LLDP_EtherType = EtherType.getInstance((short) 0x88cc);
 
     // The constants for the different block names and their actual meaning.
     private static final String DEVICE_TYPE_NAME = "DEVICE_PROPERTIES_OPTION-1";
     private static final String DEVICE_NAME_OF_STATION = "DEVICE_PROPERTIES_OPTION-2";
-    private static final String PLC4X_LLDP_IDENTIFIER = "PLC4X PROFINET Controller Client";
-    private static final String PLC4X_LLDP_PORT = "port001.plc4x";
     private static final String DEVICE_ID = "DEVICE_PROPERTIES_OPTION-3";
     private static final String DEVICE_ROLE = "DEVICE_PROPERTIES_OPTION-4";
     private static final String DEVICE_OPTIONS = "DEVICE_PROPERTIES_OPTION-5";
     private static final String DEVICE_INSTANCE = "DEVICE_PROPERTIES_OPTION-7";
     private static final String IP_OPTION_IP = "IP_OPTION-2";
 
-    ExecutorService pool = Executors.newSingleThreadExecutor();
-    Map<MacAddress, PcapHandle> openHandles = new HashMap<>();
-    List<PlcDiscoveryItem> values = new ArrayList<>();
-
-    Set<Timer> periodicTimers = new HashSet<>();
-
     private final Logger logger = LoggerFactory.getLogger(ProfinetPlcDiscoverer.class);
 
     @Override
@@ -79,49 +68,200 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         return discoverWithHandler(discoveryRequest, null);
     }
 
-    public void openDiscoverHandles() {
+    public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
+        CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
+        Set<PcapHandle> openHandles = new HashSet<>();
+        List<PlcDiscoveryItem> values = new ArrayList<>();
         try {
             for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
                 // It turned out on some MAC network devices without any ip addresses
                 // the compiling of the filter expression was causing errors. As
                 // currently there was no other way to detect this, this check seems
                 // to be sufficient.
-                if (dev.getAddresses().size() == 0) {
+                if(dev.getAddresses().size() == 0) {
                     continue;
                 }
                 if (!dev.isLoopBack()) {
                     for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
                         org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
                         PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
-                        openHandles.put(toPlc4xMacAddress(macAddress), handle);
+                        openHandles.add(handle);
+
+                        ExecutorService pool = Executors.newSingleThreadExecutor();
 
-                        // Only react on PROFINET DCP or LLDP packets targeted at our current MAC address.
+                        // Only react on PROFINET DCP packets targeted at our current MAC address.
                         handle.setFilter(
-                            "(((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")) or (ether proto 0x88cc)",
+                            "((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")",
                             BpfProgram.BpfCompileMode.OPTIMIZE);
+
+                        PacketListener listener =
+                            packet -> {
+                                // EthernetPacket is the highest level of abstraction we can be expecting.
+                                // Everything inside this we will have to decode ourselves.
+                                if (packet instanceof EthernetPacket) {
+                                    EthernetPacket ethernetPacket = (EthernetPacket) packet;
+                                    boolean isPnPacket = false;
+                                    // I have observed some times the ethernet packets being wrapped inside a VLAN
+                                    // Packet, in this case we simply unpack the content.
+                                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
+                                        Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
+                                        if (PN_EtherType.equals(vlanPacket.getHeader().getType())) {
+                                            isPnPacket = true;
+                                        }
+                                    } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType())) {
+                                        isPnPacket = true;
+                                    }
+
+                                    // It's a PROFINET packet.
+                                    if (isPnPacket) {
+                                        ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
+                                        try {
+                                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
+                                            PnDcp_Pdu pdu;
+                                            // Access the pdu data (either directly or by
+                                            // unpacking the content of the VLAN packet.
+                                            if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
+                                                Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
+                                                pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
+                                            } else {
+                                                pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
+                                            }
+                                            // Inspect the PDU itself
+                                            // (in this case we only process identify response packets)
+                                            if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
+                                                PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
+
+                                                Map<String, PnDcp_Block> blocks = new HashMap<>();
+                                                for (PnDcp_Block block : identifyResPDU.getBlocks()) {
+                                                    String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
+                                                    blocks.put(blockName, block);
+                                                }
+
+                                                // The mac address of the device we found
+                                                org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
+                                                // The mac address of the local network device
+                                                org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
+
+                                                String deviceTypeName = "unknown";
+                                                if (blocks.containsKey(DEVICE_TYPE_NAME)) {
+                                                    PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
+                                                    deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
+                                                }
+
+                                                String deviceName = "unknown";
+                                                if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
+                                                    PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
+                                                    deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
+                                                }
+
+                                                String role = "unknown";
+                                                if (blocks.containsKey(DEVICE_ROLE)) {
+                                                    role = "";
+                                                    PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
+                                                    if (block.getPnioSupervisor()) {
+                                                        role += ",SUPERVISOR";
+                                                    }
+                                                    if (block.getPnioMultidevive()) {
+                                                        role += ",MULTIDEVICE";
+                                                    }
+                                                    if (block.getPnioController()) {
+                                                        role += ",CONTROLLER";
+                                                    }
+                                                    if (block.getPnioDevice()) {
+                                                        role += ",DEVICE";
+                                                    }
+                                                    // Cut off the first comma
+                                                    if (role.length() > 0) {
+                                                        role = role.substring(1);
+                                                    } else {
+                                                        role = "unknown";
+                                                    }
+                                                }
+
+                                                String remoteIpAddress = "unknown";
+                                                String remoteSubnetMask = "unknown";
+                                                if (blocks.containsKey(IP_OPTION_IP)) {
+                                                    PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
+                                                    try {
+                                                        InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
+                                                        remoteIpAddress = addr.getHostAddress();
+                                                        InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
+                                                        remoteSubnetMask = netMask.getHostAddress();
+                                                    } catch (UnknownHostException e) {
+                                                        remoteIpAddress = "invalid";
+                                                    }
+                                                }
+
+                                                // Get the Vendor Id and the Device Id
+                                                String vendorId = "unknown";
+                                                String deviceId = "unknown";
+                                                if (blocks.containsKey(DEVICE_ID)) {
+                                                    PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
+                                                    vendorId = String.format("%04X", block.getVendorId());
+                                                    deviceId = String.format("%04X", block.getDeviceId());
+                                                }
+
+                                                Map<String, String> options = new HashMap<>();
+                                                options.put("remoteIpAddress", remoteIpAddress);
+                                                options.put("remoteSubnetMask", remoteSubnetMask);
+                                                options.put("remoteMacAddress", srcAddr.toString());
+                                                options.put("localMacAddress", dstAddr.toString());
+                                                options.put("deviceTypeName", deviceTypeName);
+                                                options.put("deviceName", deviceName);
+                                                options.put("vendorId", vendorId);
+                                                options.put("deviceId", deviceId);
+                                                options.put("role", role);
+                                                String name = deviceTypeName + " - " + deviceName;
+                                                PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
+                                                    ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
+                                                    remoteIpAddress, options, name, Collections.emptyMap());
+                                                values.add(value);
+
+                                                // If we have a discovery handler, pass it to the handler callback
+                                                if (handler != null) {
+                                                    handler.handle(value);
+                                                }
+
+                                                logger.debug("Found new device: '{}' with connection-url '{}'",
+                                                    value.getName(), value.getConnectionUrl());
+                                            }
+                                        } catch (ParseException e) {
+                                            logger.error("Got error decoding packet", e);
+                                        }
+                                    }
+                                }
+                            };
+                        Task t = new Task(handle, listener);
+                        pool.execute(t);
+
+                        // Construct and send the search request.
+                        Ethernet_Frame identificationRequest = new Ethernet_Frame(
+                            // Pre-Defined PROFINET discovery MAC address
+                            new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
+                            toPlc4xMacAddress(macAddress),
+                            new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
+                                new Ethernet_FramePayload_PnDcp(
+                                    new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
+                                        1,
+                                        256,
+                                        Collections.singletonList(
+                                            new PnDcp_Block_ALLSelector()
+                                        )))));
+                        WriteBufferByteBased buffer = new WriteBufferByteBased(34);
+                        identificationRequest.serialize(buffer);
+                        Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
+                        handle.sendPacket(packet);
                     }
                 }
             }
-        } catch (NotOpenException | PcapNativeException e) {
+        } catch (IllegalRawDataException | NotOpenException | PcapNativeException | SerializationException e) {
             logger.error("Got an exception while processing raw socket data", e);
-            for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
-                PcapHandle openHandle = entry.getValue();
-                try {
-                    openHandle.breakLoop();
-                    openHandle.close();
-                } catch (NotOpenException error) {
-                    logger.info("Handle already closed.");
-                }
-            }
-            for (Timer timer : periodicTimers) {
-                timer.cancel();
-                timer.purge();
+            future.completeExceptionally(new PlcException("Got an internal error while performing discovery"));
+            for (PcapHandle openHandle : openHandles) {
+                openHandle.close();
             }
+            return future;
         }
-    }
-
-    public CompletableFuture<PlcDiscoveryResponse> setDiscoveryEndTimer(PlcDiscoveryRequest discoveryRequest, long delay) {
-        CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
 
         // Create a timer that completes the future after a given time with all the responses it found till then.
         Timer timer = new Timer("Discovery Timeout");
@@ -129,387 +269,14 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
             public void run() {
                 PlcDiscoveryResponse response =
                     new DefaultPlcDiscoveryResponse(discoveryRequest, PlcResponseCode.OK, values);
-                for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
-                    PcapHandle openHandle = entry.getValue();
-                    try {
-                        openHandle.breakLoop();
-                        openHandle.close();
-                    } catch (Exception e) {
-                        logger.error("Error occurred while closing handle");
-                    }
-
-                }
-                for (Timer timer : periodicTimers) {
-                    timer.cancel();
-                    timer.purge();
-                }
                 future.complete(response);
-            }
-        }, delay);
-
-        return future;
-    }
-
-    public PacketListener createListener(PcapHandle handle, PlcDiscoveryItemHandler handler) {
-        PacketListener listener =
-            packet -> {
-                // EthernetPacket is the highest level of abstraction we can be expecting.
-                // Everything inside this we will have to decode ourselves.
-                if (packet instanceof EthernetPacket) {
-                    EthernetPacket ethernetPacket = (EthernetPacket) packet;
-                    boolean isPnPacket = false;
-                    // I have observed sometimes the ethernet packets being wrapped inside a VLAN
-                    // Packet, in this case we simply unpack the content.
-                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
-                        Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
-                        if (PN_EtherType.equals(vlanPacket.getHeader().getType()) || LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
-                            isPnPacket = true;
-                        }
-                    } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType()) || LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
-                        isPnPacket = true;
-                    }
-
-                    // It's a PROFINET or LLDP packet.
-                    if (isPnPacket) {
-                        ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
-                        try {
-                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
-
-                            // Access the pdu data (either directly or by
-                            // unpacking the content of the VLAN packet.
-                            if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
-                                Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
-                                if (vlefpl.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
-                                    PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
-                                    processPnDcp(pdu, ethernetPacket, handler);
-                                } else if (vlefpl.getPayload() instanceof Ethernet_FramePayload_LLDP) {
-                                    Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
-                                    processLldp(pdu, ethernetPacket, handler);
-                                }
-                            } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
-                                PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
-                                processPnDcp(pdu, ethernetPacket, handler);
-                            } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_LLDP) {
-                                Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
-                                processLldp(pdu, ethernetPacket, handler);
-                            }
-
-                        } catch (ParseException e) {
-                            logger.error("Got error decoding packet", e);
-                        }
-                    }
-                }
-            };
-        return listener;
-    }
-
-    public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
-        openDiscoverHandles();
-        startListener(handler);
-        startLldpPoll(5000L);
-        startPnDcpPoll(30000L);
-        CompletableFuture<PlcDiscoveryResponse> future = setDiscoveryEndTimer(discoveryRequest, 10000L);
-        return future;
-    }
-
-    public void ongoingDiscoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler, long lldpPeriod, long dcpPeriod) {
-        openDiscoverHandles();
-        startListener(handler);
-        startLldpPoll(lldpPeriod);
-        startPnDcpPoll(dcpPeriod);
-    }
-
-    private void processPnDcp(PnDcp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
-        // Inspect the PDU itself
-        // (in this case we only process identify response packets)
-        if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
-            PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
-
-            Map<String, PnDcp_Block> blocks = new HashMap<>();
-            for (PnDcp_Block block : identifyResPDU.getBlocks()) {
-                String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
-                blocks.put(blockName, block);
-            }
-
-            // The mac address of the device we found
-            org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
-            // The mac address of the local network device
-            org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
-
-            String deviceTypeName = "unknown";
-            if (blocks.containsKey(DEVICE_TYPE_NAME)) {
-                PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
-                deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
-            }
-
-            String deviceName = "unknown";
-            if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
-                PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
-                deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
-            }
-
-            String role = "unknown";
-            if (blocks.containsKey(DEVICE_ROLE)) {
-                role = "";
-                PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
-                if (block.getPnioSupervisor()) {
-                    role += ",SUPERVISOR";
-                }
-                if (block.getPnioMultidevive()) {
-                    role += ",MULTIDEVICE";
-                }
-                if (block.getPnioController()) {
-                    role += ",CONTROLLER";
-                }
-                if (block.getPnioDevice()) {
-                    role += ",DEVICE";
-                }
-                // Cut off the first comma
-                if (role.length() > 0) {
-                    role = role.substring(1);
-                } else {
-                    role = "unknown";
-                }
-            }
-
-            String remoteIpAddress = "unknown";
-            String remoteSubnetMask = "unknown";
-            if (blocks.containsKey(IP_OPTION_IP)) {
-                PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
-                try {
-                    InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
-                    remoteIpAddress = addr.getHostAddress();
-                    InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
-                    remoteSubnetMask = netMask.getHostAddress();
-                } catch (UnknownHostException e) {
-                    remoteIpAddress = "invalid";
+                for (PcapHandle openHandle : openHandles) {
+                    openHandle.close();
                 }
             }
+        }, 5000L);
 
-            // Get the Vendor Id and the Device Id
-            String vendorId = "unknown";
-            String deviceId = "unknown";
-            if (blocks.containsKey(DEVICE_ID)) {
-                PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
-                vendorId = String.format("%04X", block.getVendorId());
-                deviceId = String.format("%04X", block.getDeviceId());
-            }
-
-            Map<String, String> options = new HashMap<>();
-            options.put("remoteIpAddress", remoteIpAddress);
-            options.put("remoteSubnetMask", remoteSubnetMask);
-            options.put("remoteMacAddress", srcAddr.toString());
-            options.put("localMacAddress", dstAddr.toString());
-            options.put("deviceTypeName", deviceTypeName);
-            options.put("deviceName", deviceName);
-            options.put("vendorId", vendorId);
-            options.put("deviceId", deviceId);
-            options.put("role", role);
-            String name = deviceTypeName + " - " + deviceName;
-            PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
-                ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
-                remoteIpAddress, options, name, Collections.emptyMap());
-            values.add(value);
-
-            // If we have a discovery handler, pass it to the handler callback
-            if (handler != null) {
-                handler.handle(value);
-            }
-
-            logger.debug("Found new device: '{}' with connection-url '{}'",
-                value.getName(), value.getConnectionUrl());
-        }
-    }
-
-    private void processLldp(Lldp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
-        logger.debug("Found new lldp device: '' with connection-url ''");
-    }
-
-    public void startPnDcpPoll(long period) {
-        for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
-            PcapHandle handle = entry.getValue();
-            MacAddress macAddress = entry.getKey();
-            // Construct and send the search request.
-
-            Function<Object, Boolean> pnDcpTimer =
-                message -> {
-                    Ethernet_Frame identificationRequest = new Ethernet_Frame(
-                        // Pre-Defined PROFINET discovery MAC address
-                        new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
-                        macAddress,
-                        new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
-                            new Ethernet_FramePayload_PnDcp(
-                                new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
-                                    1,
-                                    256,
-                                    Collections.singletonList(
-                                        new PnDcp_Block_ALLSelector()
-                                    )))));
-                    WriteBufferByteBased buffer = new WriteBufferByteBased(34);
-                    try {
-                        identificationRequest.serialize(buffer);
-                    } catch (SerializationException e) {
-                        throw new RuntimeException(e);
-                    }
-                    Packet packet = null;
-                    try {
-                        packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
-                    } catch (IllegalRawDataException e) {
-                        throw new RuntimeException(e);
-                    }
-                    try {
-                        handle.sendPacket(packet);
-                    } catch (PcapNativeException e) {
-                        throw new RuntimeException(e);
-                    } catch (NotOpenException e) {
-                        throw new RuntimeException(e);
-                    }
-                    return null;
-            };
-
-            Timer timer = new Timer();
-            periodicTimers.add(timer);
-
-            // Schedule to run after every 3 second(3000 millisecond)
-            timer.scheduleAtFixedRate(
-                new PeriodicTask(handle, pnDcpTimer),
-                0,
-                period);
-        }
-    }
-
-    public void startListener(PlcDiscoveryItemHandler handler) {
-        for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
-            PcapHandle handle = entry.getValue();
-            MacAddress macAddress = entry.getKey();
-            // Construct and send the search request.
-
-            Function<Object, Boolean> pnDcpTimer =
-                message -> {
-                    PacketListener listener = createListener(handle, handler);
-                    try {
-                        handle.loop(-1, listener);
-                    } catch (InterruptedException e) {
-                        logger.error("Got error handling raw socket", e);
-                        Thread.currentThread().interrupt();
-                    } catch (PcapNativeException | NotOpenException e) {
-                        logger.error("Got error handling raw socket", e);
-                    }
-                    return null;
-                };
-
-            Timer timer = new Timer();
-            periodicTimers.add(timer);
-
-            // Schedule to run after every 3 second(3000 millisecond)
-            timer.schedule(
-                new PeriodicTask(handle, pnDcpTimer),
-                5000,
-                15000);
-        }
-    }
-
-
-
-    public void startLldpPoll(long period) {
-        for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
-            PcapHandle handle = entry.getValue();
-            MacAddress macAddress = entry.getKey();
-
-            Function<Object, Boolean> lldpTimer =
-                message -> {
-                    // Construct and send the LLDP Probe
-                    TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
-                        new TlvProfibusSubTypePortStatus(0x00)
-                    );
-
-                    TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
-                        new TlvProfibusSubTypeChassisMac(macAddress)
-                    );
-
-                    TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
-                        (short) 0x01,
-                        (short) 0x03,
-                        0x0020,
-                        0x0010
-                    );
-
-                    Ethernet_Frame identificationRequest = null;
-                    try {
-                        identificationRequest = new Ethernet_Frame(
-                            // Pre-Defined LLDP discovery MAC address
-                            new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
-                            macAddress,
-                            new Ethernet_FramePayload_LLDP(
-                                new Lldp_Pdu(
-                                    Arrays.asList(
-                                        new TlvChassisId(
-                                            PLC4X_LLDP_IDENTIFIER.length() + 1,
-                                            (short) 7,
-                                            PLC4X_LLDP_IDENTIFIER
-                                        ),
-                                        new TlvPortId(
-                                            PLC4X_LLDP_PORT.length() + 1,
-                                            (short) 7,
-                                            PLC4X_LLDP_PORT
-                                        ),
-                                        new TlvTimeToLive(2, 20),
-                                        new TlvOrganizationSpecific(
-                                            portStatus.getLengthInBytes(),
-                                            portStatus
-                                        ),
-                                        new TlvOrganizationSpecific(
-                                            chassisMac.getLengthInBytes(),
-                                            chassisMac
-                                        ),
-                                        new TlvOrganizationSpecific(
-                                            ieee.getLengthInBytes(),
-                                            ieee
-                                        ),
-                                        new TlvManagementAddress(
-                                            12,
-                                            ManagementAddressSubType.IPV4,
-                                            new IpAddress(Hex.decodeHex("c0a85a6e")),
-                                            (short) 0x03,
-                                            0x01L,
-                                            (short) 0x00
-                                        ),
-                                        new EndOfLldp(0)
-                                    )
-                                )));
-                    } catch (DecoderException e) {
-                        throw new RuntimeException(e);
-                    }
-                    WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
-                    try {
-                        identificationRequest.serialize(buffer);
-                    } catch (SerializationException e) {
-                        throw new RuntimeException(e);
-                    }
-                    Packet packet = null;
-                    try {
-                        packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
-                    } catch (IllegalRawDataException e) {
-                        throw new RuntimeException(e);
-                    }
-                    try {
-                        handle.sendPacket(packet);
-                    } catch (PcapNativeException e) {
-                        throw new RuntimeException(e);
-                    } catch (NotOpenException e) {
-                        throw new RuntimeException(e);
-                    }
-                    return null;
-                };
-            Timer timer = new Timer();
-            periodicTimers.add(timer);
-
-            // Schedule to run after every 3 second(3000 millisecond)
-            timer.scheduleAtFixedRate(
-                new PeriodicTask(handle, lldpTimer),
-                0,
-                period);
-        }
+        return future;
     }
 
     private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
@@ -517,28 +284,35 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
     }
 
-    private static class PeriodicTask extends TimerTask {
+    private static class Task implements Runnable {
 
-        private final Logger logger = LoggerFactory.getLogger(PeriodicTask.class);
+        private final Logger logger = LoggerFactory.getLogger(Task.class);
 
         private final PcapHandle handle;
-        private final Function<Object, Boolean> operator;
+        private final PacketListener listener;
 
-        public PeriodicTask(PcapHandle handle, Function<Object, Boolean> operator) {
+        public Task(PcapHandle handle, PacketListener listener) {
             this.handle = handle;
-            this.operator = operator;
+            this.listener = listener;
         }
 
         @Override
         public void run() {
-            operator.apply(null);
+            try {
+                handle.loop(10, listener);
+            } catch (InterruptedException e) {
+                logger.error("Got error handling raw socket", e);
+                Thread.currentThread().interrupt();
+            } catch (PcapNativeException | NotOpenException e) {
+                logger.error("Got error handling raw socket", e);
+            }
         }
-
     }
 
     public static void main(String[] args) throws Exception {
         ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer();
         discoverer.discover(null);
+
         Thread.sleep(10000);
     }
 


[plc4x] 03/13: fix(plc4j(profinet): Add Dummy Advanced Connection Write Request

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit bf77f0d958314681986ed25ff5e134b8ae086f75
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Sep 8 10:43:35 2022 -0600

    fix(plc4j(profinet): Add Dummy Advanced Connection Write Request
---
 .../profinet/protocol/ProfinetProtocolLogic.java   | 97 ++++++++++++++++++++--
 .../resources/protocols/profinet/profinet.mspec    | 56 +++++++++++--
 2 files changed, 142 insertions(+), 11 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
index 836732954..8e4e666ab 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
@@ -54,6 +54,16 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
 
     private ProfinetDriverContext profinetDriverContext;
 
+    private static final Uuid ARUUID;
+
+    static {
+        try {
+            ARUUID = new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51"));
+        } catch (DecoderException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     @Override
     public void setContext(ConversationContext<Ethernet_Frame> context) {
         super.setContext(context);
@@ -160,11 +170,23 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
             } else {
                 throw new PlcException("Unexpected response");
             }
+
+            // Create the packet
+            final DceRpc_Packet profinetAdvancedConnectionWriteRequest = createProfinetAdvancedConnectionWriteRequest();
+            // Serialize it to a byte-payload
+            writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionWriteRequest.getLengthInBytes());
+            profinetAdvancedConnectionWriteRequest.serialize(writeBuffer);
+            // Create a udp packet.
+            connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
+            connectRequestPacket.setAddress(remoteAddress.getAddress());
+            connectRequestPacket.setPort(remoteAddress.getPort());
+            // Send it.
+
+            udpSocket.send(connectRequestPacket);
+
         } catch (SerializationException | IOException | PlcException | ParseException e) {
             logger.error("Error", e);
         }
-
-        //System.out.println(rawSocketChannel);
     }
 
     @Override
@@ -233,14 +255,14 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                             // This actually needs to be set to this value and not the real port number.
                             0x8892,
                             // It seems that it must be set to this value, or it won't work.
-                            "profinetxadriver4933"),
+                            "controller"),
                         new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.INPUT_CR,
                             0x0001,
                             0x8892,
                             false, false,
                             false, false, PnIoCm_RtClass.RT_CLASS_2, 40,
                             0xBBF0, 128, 8, 1, 0, 0xffffffff,
-                            3, 3, 0xC000,
+                            50, 50, 0xC000,
                             new org.apache.plc4x.java.profinet.readwrite.MacAddress(Hex.decodeHex("000000000000")),
                             Collections.singletonList(
                                 new PnIoCm_IoCrBlockReqApi(
@@ -255,7 +277,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                             0x0002, 0x8892, false, false,
                             false, false, PnIoCm_RtClass.RT_CLASS_2, 40,
                             0xFFFF, 128, 8, 1, 0, 0xffffffff,
-                            3, 3, 0xC000,
+                            50, 50, 0xC000,
                             new MacAddress(Hex.decodeHex("000000000000")),
                             Collections.singletonList(
                                 new PnIoCm_IoCrBlockReqApi(
@@ -303,6 +325,71 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
         }
     }
 
+    private DceRpc_Packet createProfinetAdvancedConnectionWriteRequest() throws PlcException {
+
+        return new DceRpc_Packet(
+            DceRpc_PacketType.REQUEST, true, false, false,
+            IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
+            new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
+            new DceRpc_InterfaceUuid_DeviceInterface(),
+            profinetDriverContext.getDceRpcActivityUuid(),
+            0, 1, DceRpc_Operation.WRITE,
+            new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+                Arrays.asList(
+                    new IODWriteRequestHeader(
+                        (short) 1,
+                        (short) 0,
+                        0,
+                        ARUUID,
+                        0x00000000,
+                        0x0000,
+                        0x0000,
+                        0xe040,
+                        180
+                        ),
+                    new IODWriteRequestHeader(
+                        (short) 1,
+                        (short) 0,
+                        1,
+                        ARUUID,
+                        0x00000000,
+                        0x0000,
+                        0x8000,
+                        0x8071,
+                        12
+                    ),
+                    new PDInterfaceAdjust(
+                        (short) 1,
+                        (short) 0,
+                        MultipleInterfaceModeNameOfDevice.PORT_PROVIDED_BY_LLDP
+                    ),
+                    new IODWriteRequestHeader(
+                        (short) 1,
+                        (short) 0,
+                        2,
+                        ARUUID,
+                        0x00000000,
+                        0x0000,
+                        0x8001,
+                        0x802b,
+                        40
+                    ),
+                    new PDPortDataCheck(
+                        (short) 1,
+                        (short) 0,
+                        0x0000,
+                        0x8001,
+                        new CheckPeers(
+                            (short) 1,
+                            (short) 0,
+                            new PascalString("port-001"),
+                            new PascalString("controller")
+                        )
+                    )
+                ))
+        );
+    }
+
     protected static DceRpc_ActivityUuid generateActivityUuid() {
         UUID number = UUID.randomUUID();
         try {
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 35bdaf24c..09bfea4a5 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -681,6 +681,34 @@
     [simple        uint 8           blockVersionHigh                    ]
     [simple        uint 8           blockVersionLow                     ]
     [typeSwitch blockType
+        ['IOD_WRITE_REQUEST_HEADER' IODWriteRequestHeader
+            [simple   uint 16                         sequenceNumber                                         ]
+            [simple   Uuid                            arUuid                                                 ]
+            [simple   uint 32                         api                                                    ]
+            [simple   uint 16                         slotNumber                                             ]
+            [simple   uint 16                         subSlotNumber                                          ]
+            [const    uint 16                         padField                  0x0000                       ]
+            [simple   uint 16                         index                                                  ]
+            [simple   uint 32                         recordDataLength                                       ]
+            [padding  uint 8      pad '0x00'          '64 - 6 - 2 - 16 - 4 - 2 - 2 - 2 - 2 - 4']
+        ]
+        ['PD_INTERFACE_ADJUST' PDInterfaceAdjust
+            [const    uint 16                         padField                  0x0000                       ]
+            [const    uint 16                         multipleInterfaceModeReserved2                  0x0000 ]
+            [const    uint 15                         multipleInterfaceModeReserved1                  0x0000 ]
+            [simple   MultipleInterfaceModeNameOfDevice multipleInterfaceModeNameOfDevice                    ]
+        ]
+        ['PD_PORT_DATA_CHECK' PDPortDataCheck
+            [const    uint 16                         padField                  0x0000                       ]
+            [simple   uint 16                         slotNumber                                             ]
+            [simple   uint 16                         subSlotNumber                                          ]
+            [simple   PnIoCm_Block                    checkPeers                                             ]
+        ]
+        ['CHECK_PEERS'  CheckPeers
+            [const    uint 8                          noOfPeers                 0x01                         ]
+            [simple   PascalString                    peerPortId                                             ]
+            [simple   PascalString                    peerChassisId                                          ]
+        ]
         ['AR_BLOCK_REQ' PnIoCm_Block_ArReq
             [simple   PnIoCm_ArType                   arType                                                 ]
             [simple   Uuid                            arUuid                                                 ]
@@ -773,13 +801,18 @@
             [array    PnIoCm_ModuleDiffBlockApi apis              count         'numberOfApis'      ]
         ]
         ['AR_SERVER_BLOCK' PnIoCm_Block_ArServer
-            //[implicit uint 16                         stationNameLength      'STR_LEN(cmInitiatorStationName)']
-            //[simple   vstring 'stationNameLength * 8' cmInitiatorStationName                                  ]
-            //[padding  byte 0x00                                                                               ]
+            [simple   PascalString                    stationName                                   ]
+            [padding  uint 8      pad '0x00'          '20 - 6 - (stationName.stringLength)'              ]
         ]
     ]
 ]
 
+[type PascalString
+    [implicit int 16 sLength          'stringValue.length == 0 ? -1 : stringValue.length']
+    [simple vstring 'sLength == -1 ? 0 : sLength * 8' stringValue]
+    [virtual  int 16 stringLength     'stringValue.length == -1 ? 0 : stringValue.length']
+]
+
 [type PnIoCm_IoCrBlockReqApi
     [const    uint 32             api              0x00000000             ]
     [implicit uint 16             numIoDataObjects 'COUNT(ioDataObjects)'   ]
@@ -872,15 +905,21 @@
 ]
 
 [enum uint 16 PnIoCm_BlockType
+    ['0x0008' IOD_WRITE_REQUEST_HEADER    ]
     ['0x0101' AR_BLOCK_REQ                ]
-    ['0x8101' AR_BLOCK_RES                ]
     ['0x0102' IO_CR_BLOCK_REQ             ]
-    ['0x8102' IO_CR_BLOCK_RES             ]
     ['0x0103' ALARM_CR_BLOCK_REQ          ]
-    ['0x8103' ALARM_CR_BLOCK_RES          ]
     ['0x0104' EXPECTED_SUBMODULE_BLOCK_REQ]
+    ['0x0110' IOD_CONTROL_REQ             ]
+    ['0x0200' PD_PORT_DATA_CHECK          ]
+    ['0x020a' CHECK_PEERS                 ]
+    ['0x0250' PD_INTERFACE_ADJUST         ]
+    ['0x8101' AR_BLOCK_RES                ]
+    ['0x8102' IO_CR_BLOCK_RES             ]
+    ['0x8103' ALARM_CR_BLOCK_RES          ]
     ['0x8104' MODULE_DIFF_BLOCK           ]
     ['0x8106' AR_SERVER_BLOCK             ]
+    ['0x8110' IOD_CONTROL_RES             ]
 ]
 
 [enum uint 16 PnIoCm_ArType
@@ -917,6 +956,11 @@
     ['0x3' INPUT_AND_OUTPUT_DATA]
 ]
 
+[enum bit MultipleInterfaceModeNameOfDevice
+    ['false' PORT_PROVIDED_BY_LLDP]
+    ['true'  NAME_PROVIDED_BY_LLDP]
+]
+
 [enum uint 16 PnIoCm_DescriptionType
     ['0x0001' INPUT]
 ]


[plc4x] 08/13: feat(plc4j/profinet): Escape spaces using %20

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 6f5b27b2b878bea0a716950d1d6c17745defd685
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 11 13:39:31 2022 -0600

    feat(plc4j/profinet): Escape spaces using %20
---
 .../apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index f4066d098..4486e1abc 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -152,13 +152,13 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
                                                 String deviceTypeName = "unknown";
                                                 if (blocks.containsKey(DEVICE_TYPE_NAME)) {
                                                     PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
-                                                    deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "_");
+                                                    deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
                                                 }
 
                                                 String deviceName = "unknown";
                                                 if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
                                                     PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
-                                                    deviceName = new String(block.getNameOfStation()).replace(" ", "_");
+                                                    deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
                                                 }
 
                                                 String role = "unknown";


[plc4x] 07/13: feat(plc4j/profinet): Fixed a few minor issues and added the Application Ready packet

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit dcdad3137d4a5b2a42e15af6fd45dfe6c491dcc8
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 11 13:38:47 2022 -0600

    feat(plc4j/profinet): Fixed a few minor issues and added the Application Ready packet
---
 .../apache/plc4x/java/profinet/ProfinetDriver.java |  7 ++-
 .../profinet/discovery/ProfinetPlcDiscoverer.java  |  2 +-
 .../profinet/protocol/ProfinetProtocolLogic.java   | 51 +++++++++++++++++++++-
 .../resources/protocols/profinet/profinet.mspec    | 44 +++++++++++++------
 4 files changed, 88 insertions(+), 16 deletions(-)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
index 1fec4f741..05d96d8b4 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/ProfinetDriver.java
@@ -99,11 +99,16 @@ public class ProfinetDriver extends GeneratedDriverBase<Ethernet_Frame> {
 
     @Override
     protected boolean canRead() {
-        return true;
+        return false;
     }
 
     @Override
     protected boolean canWrite() {
+        return false;
+    }
+
+    @Override
+    protected boolean canSubscribe() {
         return true;
     }
 
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index 35681f795..f4066d098 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -410,7 +410,7 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
                                                     new TlvManagementAddress(
                                                         12,
                                                         ManagementAddressSubType.IPV4,
-                                                        new IpAddress(Hex.decodeHex("c0a8006e")),
+                                                        new IpAddress(Hex.decodeHex("c0a85a6e")),
                                                         (short) 0x03,
                                                         0x01L,
                                                         (short) 0x00
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
index d3c1d1024..3801c1dfa 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/protocol/ProfinetProtocolLogic.java
@@ -208,6 +208,23 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
             connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
             udpSocket.receive(connectResponsePacket);
 
+            // Create the packet
+            final DceRpc_Packet profinetAdvancedConnectionApplicationReady = createProfinetAdvancedConnectionApplicationReady();
+            // Serialize it to a byte-payload
+            writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionApplicationReady.getLengthInBytes());
+            profinetAdvancedConnectionApplicationReady.serialize(writeBuffer);
+            // Create a udp packet.
+            connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
+            connectRequestPacket.setAddress(remoteAddress.getAddress());
+            connectRequestPacket.setPort(remoteAddress.getPort());
+            // Send it.
+
+            udpSocket.send(connectRequestPacket);
+
+            // Receive the response.
+            resultBuffer = new byte[profinetAdvancedConnectionApplicationReady.getLengthInBytes()];
+            connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
+            udpSocket.receive(connectResponsePacket);
 
         } catch (SerializationException | IOException | PlcException | ParseException e) {
             logger.error("Error", e);
@@ -233,6 +250,13 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
         return future;
     }
 
+    @Override
+    public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest subscriptionRequest) {
+        CompletableFuture<PlcSubscriptionResponse> future = new CompletableFuture<>();
+        future.completeExceptionally(new NotImplementedException());
+        return future;
+    }
+
     @Override
     protected void decode(ConversationContext<Ethernet_Frame> context, Ethernet_Frame msg) throws Exception {
         super.decode(context, msg);
@@ -386,7 +410,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
                     new PDInterfaceAdjust(
                         (short) 1,
                         (short) 0,
-                        MultipleInterfaceModeNameOfDevice.PORT_PROVIDED_BY_LLDP
+                        MultipleInterfaceModeNameOfDevice.NAME_PROVIDED_BY_LLDP
                     ),
                     new IODWriteRequestHeader(
                         (short) 1,
@@ -437,6 +461,31 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
         );
     }
 
+
+
+    private DceRpc_Packet createProfinetAdvancedConnectionApplicationReady() throws PlcException {
+
+        return new DceRpc_Packet(
+            DceRpc_PacketType.REQUEST, true, false, false,
+            IntegerEncoding.BIG_ENDIAN, CharacterEncoding.ASCII, FloatingPointEncoding.IEEE,
+            new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
+            new DceRpc_InterfaceUuid_DeviceInterface(),
+            profinetDriverContext.getDceRpcActivityUuid(),
+            0, 1, DceRpc_Operation.CONTROL,
+            new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+                Arrays.asList(
+                    new PnIoCM_Block_Request(
+                        (short) 1,
+                        (short) 0,
+                        ARUUID,
+                        0x0001,
+                        0x0002,
+                        0x0000
+                    )
+                ))
+        );
+    }
+
     protected static DceRpc_ActivityUuid generateActivityUuid() {
         UUID number = UUID.randomUUID();
         try {
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 57f3ced3a..2cabb832f 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -823,27 +823,43 @@
             [simple   vstring 'stationNameLength * 8' cmInitiatorStationName                                 ]
         ]
         ['AR_BLOCK_RES' PnIoCm_Block_ArRes
-            [simple   PnIoCm_ArType          arType                                                 ]
-            [simple   Uuid                   arUuid                                                 ]
-            [simple   uint 16                sessionKey                                             ]
-            [simple   MacAddress             cmResponderMacAddr                                     ]
-            [simple   uint 16                responderUDPRTPort                                     ]
+            [simple   PnIoCm_ArType          arType                                                          ]
+            [simple   Uuid                   arUuid                                                          ]
+            [simple   uint 16                sessionKey                                                      ]
+            [simple   MacAddress             cmResponderMacAddr                                              ]
+            [simple   uint 16                responderUDPRTPort                                              ]
         ]
         ['IOD_CONTROL_REQ' PnIoCm_Control_Request
-            [reserved uint 16                         '0x0000'                                         ]
+            [reserved uint 16                         '0x0000'                                               ]
             [simple   Uuid                            arUuid                                                 ]
             [simple   uint 16                         sessionKey                                             ]
-            [reserved uint 16                         '0x0000'                                         ]
+            [reserved uint 16                         '0x0000'                                               ]
             [simple   uint 16                         controlCommand                                         ]
-            [reserved uint 16                         '0x0000'                                         ]
+            [reserved uint 16                         '0x0000'                                               ]
+        ]
+        ['IOX_BLOCK_REQ'    PnIoCM_Block_Request
+            [reserved uint 16                         '0x0000'                                               ]
+            [simple   Uuid                            arUuid                                                 ]
+            [simple   uint 16                         sessionKey                                             ]
+            [reserved uint 16                         '0x0000'                                               ]
+            [simple   uint 16                         controlCommand                                         ]
+            [simple   uint 16                         controlBlockProperties                                 ]
+        ]
+        ['IOX_BLOCK_RES'    PnIoCM_Block_Response
+            [reserved uint 16                         '0x0000'                                               ]
+            [simple   Uuid                            arUuid                                                 ]
+            [simple   uint 16                         sessionKey                                             ]
+            [reserved uint 16                         '0x0000'                                               ]
+            [simple   uint 16                         controlCommand                                         ]
+            [simple   uint 16                         controlBlockProperties                                 ]
         ]
         ['IOD_CONTROL_RES' PnIoCm_Control_Response
-            [reserved uint 16                         '0x0000'                                         ]
+            [reserved uint 16                         '0x0000'                                               ]
             [simple   Uuid                            arUuid                                                 ]
             [simple   uint 16                         sessionKey                                             ]
-            [reserved uint 16                         '0x0000'                                         ]
+            [reserved uint 16                         '0x0000'                                               ]
             [simple   uint 16                         controlCommand                                         ]
-            [reserved uint 16                         '0x0000'                                         ]
+            [reserved uint 16                         '0x0000'                                               ]
         ]
         ['IO_CR_BLOCK_REQ' PnIoCm_Block_IoCrReq
             [simple PnIoCm_IoCrType          ioCrType                                               ]
@@ -913,9 +929,9 @@
 ]
 
 [type PascalString
-    [implicit int 16 sLength          'stringValue.length == 0 ? -1 : stringValue.length']
+    [implicit int 8 sLength          'stringValue.length == 0 ? -1 : stringValue.length']
     [simple vstring 'sLength == -1 ? 0 : sLength * 8' stringValue]
-    [virtual  int 16 stringLength     'stringValue.length == -1 ? 0 : stringValue.length']
+    [virtual  int 8 stringLength     'stringValue.length == -1 ? 0 : stringValue.length']
 ]
 
 [type PnIoCm_IoCrBlockReqApi
@@ -1016,6 +1032,7 @@
     ['0x0103' ALARM_CR_BLOCK_REQ          ]
     ['0x0104' EXPECTED_SUBMODULE_BLOCK_REQ]
     ['0x0110' IOD_CONTROL_REQ             ]
+    ['0x0112' IOX_BLOCK_REQ               ]
     ['0x0200' PD_PORT_DATA_CHECK          ]
     ['0x020a' CHECK_PEERS                 ]
     ['0x0250' PD_INTERFACE_ADJUST         ]
@@ -1025,6 +1042,7 @@
     ['0x8104' MODULE_DIFF_BLOCK           ]
     ['0x8106' AR_SERVER_BLOCK             ]
     ['0x8110' IOD_CONTROL_RES             ]
+    ['0x8112' IOX_BLOCK_RES               ]
 ]
 
 [enum uint 16 PnIoCm_ArType


[plc4x] 04/13: fix(plc4j(profinet): Started to add the LLDP broadcast

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

hutcheb pushed a commit to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 83becf0591431f76d0b34d3ede14fd4f64db83b2
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Sep 8 12:49:59 2022 -0600

    fix(plc4j(profinet): Started to add the LLDP broadcast
---
 .../profinet/discovery/ProfinetPlcDiscoverer.java  | 102 +++++++++++++++++++++
 .../resources/protocols/profinet/profinet.mspec    |  22 +++++
 2 files changed, 124 insertions(+)

diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index 5eb899bcc..96b2dba67 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -51,6 +51,7 @@ import java.util.concurrent.Executors;
 public class ProfinetPlcDiscoverer implements PlcDiscoverer {
 
     private static final EtherType PN_EtherType = EtherType.getInstance((short) 0x8892);
+    private static final EtherType LLDP_EtherType = EtherType.getInstance((short) 0x88cc);
 
     // The constants for the different block names and their actual meaning.
     private static final String DEVICE_TYPE_NAME = "DEVICE_PROPERTIES_OPTION-1";
@@ -279,6 +280,107 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         return future;
     }
 
+    public void lldpProbe() {
+        Set<PcapHandle> openHandles = new HashSet<>();
+        try {
+            for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
+                // It turned out on some MAC network devices without any ip addresses
+                // the compiling of the filter expression was causing errors. As
+                // currently there was no other way to detect this, this check seems
+                // to be sufficient.
+                if(dev.getAddresses().size() == 0) {
+                    continue;
+                }
+                if (!dev.isLoopBack()) {
+                    for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
+                        org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
+                        PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
+                        openHandles.add(handle);
+
+                        ExecutorService pool = Executors.newSingleThreadExecutor();
+
+                        // Only react on PROFINET DCP packets targeted at our current MAC address.
+                        handle.setFilter(
+                            "(ether proto 0x88cc)",
+                            BpfProgram.BpfCompileMode.OPTIMIZE);
+
+                        PacketListener listener =
+                            packet -> {
+                                // EthernetPacket is the highest level of abstraction we can be expecting.
+                                // Everything inside this we will have to decode ourselves.
+                                if (packet instanceof EthernetPacket) {
+                                    EthernetPacket ethernetPacket = (EthernetPacket) packet;
+                                    boolean isLldpPacket = false;
+                                    // I have observed sometimes the ethernet packets being wrapped inside a VLAN
+                                    // Packet, in this case we simply unpack the content.
+                                    if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
+                                        Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
+                                        if (LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
+                                            isLldpPacket = true;
+                                        }
+                                    } else if (LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
+                                        isLldpPacket = true;
+                                    }
+
+                                    // It's a LLDP packet.
+                                    if (isLldpPacket) {
+                                        ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
+                                        try {
+                                            Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
+                                            PnDcp_Pdu pdu;
+                                            // Access the pdu data (either directly or by
+                                            // unpacking the content of the VLAN packet.
+                                            if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
+                                                Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
+                                                pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
+                                            } else {
+                                                pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
+                                            }
+                                            // Inspect the PDU itself
+                                            // (in this case we only process identify response packets)
+
+
+                                            logger.debug("Found new device: '{}' using LLDP '{}'",
+                                                "Not Sure", "Not Sure");
+                                        } catch (ParseException ex) {
+                                            throw new RuntimeException(ex);
+                                        }
+                                    }
+                                }
+                            };
+                        Task t = new Task(handle, listener);
+                        pool.execute(t);
+
+                        // Construct and send the LLDP Probe
+
+                        Ethernet_Frame identificationRequest = new Ethernet_Frame(
+                            // Pre-Defined PROFINET discovery MAC address
+                            new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
+                            toPlc4xMacAddress(macAddress),
+                            new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
+                                new Ethernet_FramePayload_PnDcp(
+                                    new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
+                                        1,
+                                        256,
+                                        Collections.singletonList(
+                                            new PnDcp_Block_ALLSelector()
+                                        )))));
+                        WriteBufferByteBased buffer = new WriteBufferByteBased(34);
+                        identificationRequest.serialize(buffer);
+                        Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
+                        handle.sendPacket(packet);
+                    }
+                }
+            }
+        } catch (IllegalRawDataException | NotOpenException | PcapNativeException | SerializationException e) {
+            logger.error("Got an exception while processing raw socket data", e);
+
+            for (PcapHandle openHandle : openHandles) {
+                openHandle.close();
+            }
+        }
+    }
+
     private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
         byte[] address = pcap4jMacAddress.getAddress();
         return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 09bfea4a5..b3c0d9ca0 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -82,9 +82,31 @@
         ['0x8892' Ethernet_FramePayload_PnDcp
             [simple PnDcp_Pdu             pdu                                                        ]
         ]
+        ['0x88cc' Ethernet_FramePayload_LLDP
+            [simple Lldp_Pdu             pdu                                                        ]
+        ]
     ]
 ]
 
+[type Lldp_Pdu
+    [simple     TlvType_Pdu             tlvChassisId]
+    [simple     TlvType_Pdu             tlvPortId]
+    [simple     TlvTimeToLive           tlvTimeToLive]
+]
+
+[type TlvType_Pdu
+    [simple     uint 7                  tlvId                               ]
+    [implicit   uint 9                  tlvIdLength       'lengthInBytes'   ]
+    [simple     typeSubType             idSubType                           ]
+    [simple     vstring '(tlvIdLength * 8) +  1' Id                         ]
+]
+
+[type TlvTimeToLive
+    [simple     uint 7                  tlvId                               ]
+    [implicit   uint 9                  tlvIdLength       'lengthInBytes'   ]
+    [simple     uint 16                 tlvTimeToLive]
+]
+
 // 4.10.3.2
 // A lot of the fields are set to constant values, which would
 // usually be dynamic. However are we trying to limit the number of