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

[plc4x] 01/02: First Profinet Discovery and Configuration Protocol implementation.

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

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

commit 30f52a149203bfb1aeb389550ffa4ad243c8051c
Author: Ɓukasz Dywicki <lu...@code-house.org>
AuthorDate: Mon May 11 12:46:49 2020 +0200

    First Profinet Discovery and Configuration Protocol implementation.
    
    It is quite basic in ints form, allows to send broadcast over ethernet interface. Responses are not processed, they are just printed to logs.
---
 .../protocols/profinet/profinet.dcp.mspec          |   7 ++
 .../java/profinet/dcp/ProfinetDCPPlcDriver.java    | 103 ++++++++++++++++++
 .../dcp/configuration/ProfinetConfiguration.java   |  53 +++++++++
 .../profinet/dcp/field/ProfinetFieldHandler.java   |  36 +++++++
 .../dcp/protocol/ProfinetDCPProtocolLogic.java     | 120 +++++++++++++++++++++
 .../services/org.apache.plc4x.java.api.PlcDriver   |  19 ++++
 6 files changed, 338 insertions(+)

diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.dcp.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.dcp.mspec
index 298243a..fd0be03 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.dcp.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.dcp.mspec
@@ -17,6 +17,13 @@
 // under the License.
 //
 
+[type 'EthernetFrame'
+    [simple MacAddress 'destination' ]
+    [simple MacAddress 'source'      ]
+    [simple uint 16    'ethernetType']
+    [simple ProfinetFrame 'payload'  ]
+]
+
 [type 'ProfinetFrame'
     [enum FrameType 'frameType'                ]
     [simple ProfinetData 'frame'  ['frameType']]
diff --git a/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/ProfinetDCPPlcDriver.java b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/ProfinetDCPPlcDriver.java
new file mode 100644
index 0000000..1c611ed
--- /dev/null
+++ b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/ProfinetDCPPlcDriver.java
@@ -0,0 +1,103 @@
+/*
+ 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
+
+     http://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.dcp;
+
+import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
+import org.apache.plc4x.java.PlcDriverManager;
+import org.apache.plc4x.java.api.PlcConnection;
+import org.apache.plc4x.java.profinet.dcp.configuration.ProfinetConfiguration;
+import org.apache.plc4x.java.profinet.dcp.field.ProfinetFieldHandler;
+import org.apache.plc4x.java.profinet.dcp.protocol.ProfinetDCPProtocolLogic;
+import org.apache.plc4x.java.profinet.dcp.readwrite.EthernetFrame;
+import org.apache.plc4x.java.profinet.dcp.readwrite.io.EthernetFrameIO;
+import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
+import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
+import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
+
+import io.netty.buffer.ByteBuf;
+
+/**
+ * Proof of concept implementation of Profinet DCP driver.
+ */
+public class ProfinetDCPPlcDriver extends GeneratedDriverBase<EthernetFrame> {
+
+    @Override
+    public String getProtocolCode() {
+        return "profinet-dcp";
+    }
+
+    @Override
+    public String getProtocolName() {
+        return "Profinet Discovery and Configuration Protocol";
+    }
+
+    @Override
+    protected Class<? extends Configuration> getConfigurationType() {
+        return ProfinetConfiguration.class;
+    }
+
+    @Override
+    protected String getDefaultTransport() {
+        return "raw";
+    }
+
+    @Override
+    protected ProfinetFieldHandler getFieldHandler() {
+        return new ProfinetFieldHandler();
+    }
+
+    @Override
+    protected ProtocolStackConfigurer<EthernetFrame> getStackConfigurer() {
+        return SingleProtocolStackConfigurer.builder(EthernetFrame.class, EthernetFrameIO.class)
+            .withProtocol(ProfinetDCPProtocolLogic.class)
+            .withPacketSizeEstimator(ProfinetPacketEstimator.class)
+            .withCorruptPacketRemover(CorruptEthernetFrameRemover.class)
+            .build();
+    }
+
+    public static class ProfinetPacketEstimator implements ToIntFunction<ByteBuf> {
+        @Override
+        public int applyAsInt(ByteBuf value) {
+            if (value.readableBytes() >= 24) {
+                int unsignedShort = value.getUnsignedShort(24);
+                return 26 + unsignedShort;
+            }
+            return -1;
+        }
+    }
+
+    public static class CorruptEthernetFrameRemover implements Consumer<ByteBuf> {
+
+        @Override
+        public void accept(ByteBuf byteBuf) {
+            if (byteBuf.getShort(12) != ProfinetDCPProtocolLogic.PN_DCP) {
+                byteBuf.readBytes(12);
+            }
+        }
+    }
+
+
+    public static void main(String[] args) throws Exception {
+        PlcConnection connection = new PlcDriverManager().getConnection("profinet-dcp:raw://ens2f1");
+        //connection.connect();
+    }
+
+}
diff --git a/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/configuration/ProfinetConfiguration.java b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/configuration/ProfinetConfiguration.java
new file mode 100644
index 0000000..c9c87c7
--- /dev/null
+++ b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/configuration/ProfinetConfiguration.java
@@ -0,0 +1,53 @@
+/*
+ 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
+
+     http://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.dcp.configuration;
+
+import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.transport.rawsocket.RawSocketTransportConfiguration;
+import org.apache.plc4x.java.utils.pcap.netty.handlers.PacketHandler;
+import org.pcap4j.packet.Packet;
+
+public class ProfinetConfiguration implements Configuration, RawSocketTransportConfiguration {
+
+    @Override
+    public boolean getSupportVlans() {
+        return false;
+    }
+
+    @Override
+    public int getDefaultPort() {
+        return -1;
+    }
+
+    @Override
+    public Integer getProtocolId() {
+        return 0x8892;
+    }
+
+    @Override
+    public PacketHandler getPcapPacketHandler() {
+        return new PacketHandler() {
+            @Override
+            public byte[] getData(Packet packet) {
+                // We rely directly on the ethernet frame, so we just need everything.
+                return packet.getRawData();
+            }
+        };
+    }
+}
diff --git a/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/field/ProfinetFieldHandler.java b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/field/ProfinetFieldHandler.java
new file mode 100644
index 0000000..48ff3a5
--- /dev/null
+++ b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/field/ProfinetFieldHandler.java
@@ -0,0 +1,36 @@
+/*
+ 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
+
+     http://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.dcp.field;
+
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.spi.connection.DefaultPlcFieldHandler;
+import org.apache.plc4x.java.spi.connection.PlcFieldHandler;
+
+/**
+ * TODO implement this.
+ */
+public class ProfinetFieldHandler extends DefaultPlcFieldHandler implements PlcFieldHandler {
+
+    @Override
+    public PlcField createField(String fieldQuery) throws PlcInvalidFieldException {
+        return null;
+    }
+
+}
diff --git a/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/protocol/ProfinetDCPProtocolLogic.java b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/protocol/ProfinetDCPProtocolLogic.java
new file mode 100644
index 0000000..dd170ce
--- /dev/null
+++ b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/protocol/ProfinetDCPProtocolLogic.java
@@ -0,0 +1,120 @@
+/*
+ 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
+
+     http://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.dcp.protocol;
+
+import java.time.Duration;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.plc4x.java.profinet.dcp.readwrite.AllSelector;
+import org.apache.plc4x.java.profinet.dcp.readwrite.DCPBlock;
+import org.apache.plc4x.java.profinet.dcp.readwrite.DcpIdentRequestPDU;
+import org.apache.plc4x.java.profinet.dcp.readwrite.DcpIdentResponsePDU;
+import org.apache.plc4x.java.profinet.dcp.readwrite.DeviceProperties;
+import org.apache.plc4x.java.profinet.dcp.readwrite.EthernetFrame;
+import org.apache.plc4x.java.profinet.dcp.readwrite.IP;
+import org.apache.plc4x.java.profinet.dcp.readwrite.IPv4Address;
+import org.apache.plc4x.java.profinet.dcp.readwrite.MacAddress;
+import org.apache.plc4x.java.profinet.dcp.readwrite.ProfinetFrame;
+import org.apache.plc4x.java.profinet.dcp.readwrite.types.DCPBlockOption;
+import org.apache.plc4x.java.profinet.dcp.readwrite.types.DCPServiceID;
+import org.apache.plc4x.java.profinet.dcp.readwrite.types.DCPServiceType;
+import org.apache.plc4x.java.profinet.dcp.readwrite.types.FrameType;
+import org.apache.plc4x.java.spi.ConversationContext;
+import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Driver logic for handling Profinet-DCP packets.
+ */
+public class ProfinetDCPProtocolLogic extends Plc4xProtocolBase<EthernetFrame> {
+
+    public static MacAddress PROFINET_BROADCAST = createAddress(0x01, 0x0E, 0xCF, 0x00, 0x00, 0x00);
+    public static MacAddress TEST_ADDRESS = createAddress(0x08, 0x00, 0x27, 0x10, 0xFF, 0x10);
+    public static int PN_DCP = 0x8892;
+
+    public static final Duration REQUEST_TIMEOUT = Duration.ofMillis(10000);
+    private final Logger logger = LoggerFactory.getLogger(ProfinetDCPProtocolLogic.class);
+
+    private AtomicInteger invokeId = new AtomicInteger(0);
+
+    @Override
+    public void onConnect(ConversationContext<EthernetFrame> context) {
+        DCPServiceID serviceId = DCPServiceID.IDENTIFY;
+        DCPServiceType serviceType = DCPServiceType.REQUEST;
+        long xid = invokeId.incrementAndGet();
+        int responseDelay = 1000;
+        DCPBlock[] blocks = new DCPBlock[] { new AllSelector(DCPBlockOption.ALL_SELECTOR)};
+        int dcpDataLength = blocks[0].getLengthInBytes();
+
+        DcpIdentRequestPDU requestPDU = new DcpIdentRequestPDU(serviceId, serviceType, xid, responseDelay, dcpDataLength, blocks);
+        EthernetFrame ethernetFrame = new EthernetFrame(PROFINET_BROADCAST, TEST_ADDRESS, PN_DCP,
+            new ProfinetFrame(FrameType.IDENTIFY_MULTICAST_REQUEST, requestPDU));
+
+        // this is broadcast thus reply might come from multiple participants
+        context.sendToWire(ethernetFrame);
+        context.fireConnected();
+    }
+
+    @Override
+    public void onDisconnect(ConversationContext<EthernetFrame> context) {
+        context.fireDisconnected();
+    }
+
+    @Override
+    protected void decode(ConversationContext<EthernetFrame> context, EthernetFrame msg) throws Exception {
+        if (msg.getEthernetType() != PN_DCP) {
+            logger.trace("Discarding unwanted frame type {}", msg.getEthernetType());
+        }
+
+        ProfinetFrame profinetFrame = msg.getPayload();
+        if (profinetFrame.getFrameType() == FrameType.IDENTIFY_RESPONSE) {
+            logger.info("Ident response from Profinet device:");
+            if (profinetFrame.getFrame() instanceof DcpIdentResponsePDU) {
+                DcpIdentResponsePDU response = (DcpIdentResponsePDU) profinetFrame.getFrame();
+                DCPBlock[] blocks = response.getBlocks();
+                for (int index = 0; index < blocks.length; index++) {
+                    DCPBlock block = blocks[index];
+                    if (block instanceof IP) {
+                        IP ip = (IP) block;
+                        logger.info("Device IP: {}, mask: {}, gateway: {}", addressString(ip.getIpAddress()), addressString(ip.getSubnetMask()), addressString(ip.getStandardGateway()));
+                    }
+                    if (block instanceof DeviceProperties) {
+                        DeviceProperties properties = (DeviceProperties) block;
+                        logger.info("Device option: {}, value: {}", properties.getSubOption().name(), properties.getProperties().toPlcValue());
+                    }
+                }
+            } else {
+                logger.error("Unexpected ident response {}", profinetFrame.getFrame().getClass());
+            }
+        }
+    }
+
+    private String addressString(IPv4Address address) {
+        return address.getOctet1() + "." + address.getOctet2() + "." + address.getOctet3() + "." + address.getOctet4();
+    }
+
+    @Override
+    public void close(ConversationContext<EthernetFrame> context) {
+
+    }
+
+    public final static MacAddress createAddress(int octet1, int octet2, int octet3, int octet4, int octet5, int octet6) {
+        return new MacAddress((short) octet1, (short) octet2, (short) octet3, (short) octet4, (short) octet5, (short) octet6);
+    }
+}
diff --git a/sandbox/test-java-profinet-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver b/sandbox/test-java-profinet-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
new file mode 100644
index 0000000..f0fa90d
--- /dev/null
+++ b/sandbox/test-java-profinet-driver/src/main/resources/META-INF/services/org.apache.plc4x.java.api.PlcDriver
@@ -0,0 +1,19 @@
+#
+# 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
+#
+#   http://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.
+#
+org.apache.plc4x.java.profinet.dcp.ProfinetDCPPlcDriver