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 <[email protected]> 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 ]
