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

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

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                   ]