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:51 UTC

[plc4x] 06/13: fix(plc4j(profinet): Finished the connection setup.

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                                          ]