This is an automated email from the ASF dual-hosted git repository. ldywicki pushed a commit to branch feature/profinet2 in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit 7d0fe2b6f60ff8bc8c6b8e64e1ae552bb2a07c3f Author: Ćukasz Dywicki <[email protected]> AuthorDate: Mon Mar 29 13:55:52 2021 +0200 Support for tagged ethernet frames. Some sketch work around subscriptions for ident answers, without actual field definitions and concrete implementations. --- .../protocols/profinet/profinet.dcp.mspec | 33 ++++-- .../java/profinet/dcp/ProfinetDCPPlcDriver.java | 8 +- .../java/profinet/dcp/field/ProfinetDcpField.java | 24 ++++ .../dcp/protocol/ProfinetDCPProtocolLogic.java | 126 ++++++++++++++++++--- .../protocol/ProfinetDCPSubscriptionHandle.java | 54 +++++++++ 5 files changed, 217 insertions(+), 28 deletions(-) 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 d202968..fb9bf40 100644 --- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.dcp.mspec +++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.dcp.mspec @@ -17,11 +17,22 @@ // under the License. // -[type 'EthernetFrame' - [simple MacAddress 'destination' ] - [simple MacAddress 'source' ] - [simple uint 16 'ethernetType'] - [simple ProfinetFrame 'payload' ] +[discriminatedType 'BaseEthernetFrame' + [simple MacAddress 'destination' ] + [simple MacAddress 'source' ] + [simple uint 16 'etherType'] + [typeSwitch 'etherType' + ['0x8100' TaggedFrame + [simple uint 3 'priority'] + [simple bit 'droppable'] + [simple uint 12 'vlan'] + [simple uint 16 'ethernetType'] + ] + [EthernetFrame + [implicit uint 16 'ethernetType' 'etherType'] + ] + ] + [simple ProfinetFrame 'payload' ] ] [type 'ProfinetFrame' @@ -40,11 +51,19 @@ [simple uint 16 'dcpDataLength' ] [array DCPBlock 'blocks' length 'dcpDataLength'] ] + ['FrameType.GET_SET' DcpGetSetPDU + [enum DCPServiceID 'serviceId' ] + [enum DCPServiceType 'serviceType' ] + [simple uint 32 'xid' ] + [reserved uint 16 '0x00' ] + [simple uint 16 'dcpDataLength' ] + [array DCPBlock 'blocks' length 'dcpDataLength'] + ] ['FrameType.IDENTIFY_RESPONSE' DcpIdentResponsePDU [enum DCPServiceID 'serviceId' ] [enum DCPServiceType 'serviceType' ] [simple uint 32 'xid' ] - [simple uint 16 'responseDelay' ] + [reserved uint 16 '0x00' ] [simple uint 16 'dcpDataLength' ] [array DCPBlock 'blocks' length 'dcpDataLength'] ] @@ -167,7 +186,6 @@ ['0x04' SET ] ['0x05' IDENTIFY ] ['0x06' HELLO ] - ['0xFEFE' IDENTIFY_RESPONSE ] ] [enum uint 8 'DCPServiceType' @@ -177,6 +195,7 @@ [enum uint 16 'FrameType' ['0xFEFE' IDENTIFY_MULTICAST_REQUEST] + ['0xFEFD' GET_SET ] ['0xFEFF' IDENTIFY_RESPONSE ] ] 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 index 5baa18e..eba4937 100644 --- 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 @@ -24,7 +24,9 @@ import org.apache.plc4x.java.api.value.PlcValueHandler; 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.BaseEthernetFrame; import org.apache.plc4x.java.profinet.dcp.readwrite.EthernetFrame; +import org.apache.plc4x.java.profinet.dcp.readwrite.io.BaseEthernetFrameIO; 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; @@ -37,7 +39,7 @@ import org.apache.plc4x.java.spi.values.IEC61131ValueHandler; /** * Proof of concept implementation of Profinet DCP driver. */ -public class ProfinetDCPPlcDriver extends GeneratedDriverBase<EthernetFrame> { +public class ProfinetDCPPlcDriver extends GeneratedDriverBase<BaseEthernetFrame> { @Override public String getProtocolCode() { @@ -70,8 +72,8 @@ public class ProfinetDCPPlcDriver extends GeneratedDriverBase<EthernetFrame> { } @Override - protected ProtocolStackConfigurer<EthernetFrame> getStackConfigurer() { - return SingleProtocolStackConfigurer.builder(EthernetFrame.class, EthernetFrameIO.class) + protected ProtocolStackConfigurer<BaseEthernetFrame> getStackConfigurer() { + return SingleProtocolStackConfigurer.builder(BaseEthernetFrame.class, BaseEthernetFrameIO.class) .withProtocol(ProfinetDCPProtocolLogic.class) .withPacketSizeEstimator(ProfinetPacketEstimator.class) .withCorruptPacketRemover(CorruptEthernetFrameRemover.class) diff --git a/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/field/ProfinetDcpField.java b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/field/ProfinetDcpField.java new file mode 100644 index 0000000..98d92d5 --- /dev/null +++ b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/field/ProfinetDcpField.java @@ -0,0 +1,24 @@ +/* + * 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.model.PlcField; + +public class ProfinetDcpField implements PlcField { +} 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 index 9348d2c..c8ba4a0 100644 --- 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 @@ -19,18 +19,28 @@ package org.apache.plc4x.java.profinet.dcp.protocol; import java.time.Duration; +import java.time.Instant; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import org.apache.plc4x.java.api.messages.PlcSubscriptionEvent; +import org.apache.plc4x.java.api.messages.PlcSubscriptionRequest; +import org.apache.plc4x.java.api.messages.PlcSubscriptionResponse; +import org.apache.plc4x.java.api.model.PlcConsumerRegistration; +import org.apache.plc4x.java.api.model.PlcSubscriptionHandle; +import org.apache.plc4x.java.api.types.PlcResponseCode; +import org.apache.plc4x.java.api.types.PlcSubscriptionType; +import org.apache.plc4x.java.api.value.PlcValue; import org.apache.plc4x.java.profinet.dcp.configuration.ProfinetConfiguration; -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.field.ProfinetDcpField; +import org.apache.plc4x.java.profinet.dcp.readwrite.*; 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; @@ -38,20 +48,33 @@ 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.apache.plc4x.java.spi.configuration.HasConfiguration; +import org.apache.plc4x.java.spi.generation.ParseException; +import org.apache.plc4x.java.spi.generation.ReadBuffer; +import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionEvent; +import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionRequest; +import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionResponse; +import org.apache.plc4x.java.spi.messages.PlcSubscriber; +import org.apache.plc4x.java.spi.messages.utils.ResponseItem; +import org.apache.plc4x.java.spi.model.DefaultPlcConsumerRegistration; +import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionField; +import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle; +import org.apache.plc4x.java.spi.values.PlcNull; +import org.apache.plc4x.java.spi.values.PlcValues; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Driver logic for handling Profinet-DCP packets. */ -public class ProfinetDCPProtocolLogic extends Plc4xProtocolBase<EthernetFrame> implements - HasConfiguration<ProfinetConfiguration> { +public class ProfinetDCPProtocolLogic extends Plc4xProtocolBase<BaseEthernetFrame> implements + HasConfiguration<ProfinetConfiguration>, PlcSubscriber { public static MacAddress PROFINET_BROADCAST = createAddress(0x01, 0x0E, 0xCF, 0x00, 0x00, 0x00); public static int PN_DCP = 0x8892; public static final Duration REQUEST_TIMEOUT = Duration.ofMillis(10000); private final Logger logger = LoggerFactory.getLogger(ProfinetDCPProtocolLogic.class); + private Map<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> consumers = new ConcurrentHashMap<>(); private AtomicInteger invokeId = new AtomicInteger(0); private ProfinetConfiguration configuration; @@ -62,7 +85,7 @@ public class ProfinetDCPProtocolLogic extends Plc4xProtocolBase<EthernetFrame> i } @Override - public void onConnect(ConversationContext<EthernetFrame> context) { + public void onConnect(ConversationContext<BaseEthernetFrame> context) { DCPServiceID serviceId = DCPServiceID.IDENTIFY; DCPServiceType serviceType = DCPServiceType.REQUEST; long xid = invokeId.incrementAndGet(); @@ -80,17 +103,48 @@ public class ProfinetDCPProtocolLogic extends Plc4xProtocolBase<EthernetFrame> i } @Override - public void onDisconnect(ConversationContext<EthernetFrame> context) { + public void onDisconnect(ConversationContext<BaseEthernetFrame> 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()); + protected void decode(ConversationContext<BaseEthernetFrame> context, BaseEthernetFrame msg) throws Exception { + if (msg instanceof TaggedFrame) { + TaggedFrame frame = (TaggedFrame) msg; + if (frame.getEthernetType() != PN_DCP) { + logger.trace("Discarding unwanted frame type {}", frame.getEthernetType()); + } + } else if (msg.getEtherType() != PN_DCP) { + logger.trace("Discarding unwanted frame type {}", msg.getEtherType()); } ProfinetFrame profinetFrame = msg.getPayload(); + + for (Map.Entry<DefaultPlcConsumerRegistration, Consumer<PlcSubscriptionEvent>> entry : consumers.entrySet()) { + DefaultPlcConsumerRegistration registration = entry.getKey(); + Consumer<PlcSubscriptionEvent> consumer = entry.getValue(); + + for (PlcSubscriptionHandle handler : registration.getSubscriptionHandles()) { + ProfinetDCPSubscriptionHandle handle = (ProfinetDCPSubscriptionHandle) handler; + + if (handle.matches(profinetFrame)) { + logger.trace("Dispatching frame {} to {}", profinetFrame, handle); + + ProfinetDcpField field = handle.getField(); + // todo map actual DCP fields to PlcValues ? + PlcValue value = PlcValues.of(profinetFrame); + DefaultPlcSubscriptionEvent event = new DefaultPlcSubscriptionEvent( + Instant.now(), + Collections.singletonMap( + handle.getName(), + new ResponseItem<>(PlcResponseCode.OK, value) + ) + ); + consumer.accept(event); + } + } + } + if (profinetFrame.getFrameType() == FrameType.IDENTIFY_RESPONSE) { logger.info("Ident response from Profinet device:"); if (profinetFrame.getFrame() instanceof DcpIdentResponsePDU) { @@ -113,12 +167,48 @@ public class ProfinetDCPProtocolLogic extends Plc4xProtocolBase<EthernetFrame> i } } + @Override + public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest request) { + DefaultPlcSubscriptionRequest rq = (DefaultPlcSubscriptionRequest) request; + + Map<String, ResponseItem<PlcSubscriptionHandle>> answers = new LinkedHashMap<>(); + DefaultPlcSubscriptionResponse response = new DefaultPlcSubscriptionResponse(rq, answers); + + for (String key : rq.getFieldNames()) { + DefaultPlcSubscriptionField subscription = (DefaultPlcSubscriptionField) rq.getField(key); + if (subscription.getPlcSubscriptionType() != PlcSubscriptionType.EVENT) { + answers.put(key, new ResponseItem<>(PlcResponseCode.UNSUPPORTED, null)); + } else if ((subscription.getPlcField() instanceof ProfinetDcpField)) { + answers.put(key, new ResponseItem<>(PlcResponseCode.OK, + new ProfinetDCPSubscriptionHandle(this, key, (ProfinetDcpField) subscription.getPlcField()) + )); + } else { + answers.put(key, new ResponseItem<>(PlcResponseCode.INVALID_ADDRESS, null)); + } + } + + return CompletableFuture.completedFuture(response); + } + + @Override + public PlcConsumerRegistration register(Consumer<PlcSubscriptionEvent> consumer, Collection<PlcSubscriptionHandle> handles) { + final DefaultPlcConsumerRegistration consumerRegistration = new DefaultPlcConsumerRegistration(this, consumer, handles.toArray(new DefaultPlcSubscriptionHandle[0])); + consumers.put(consumerRegistration, consumer); + return consumerRegistration; + } + + @Override + public void unregister(PlcConsumerRegistration registration) { + consumers.remove(registration); + } + + private String addressString(IPv4Address address) { return address.getOctet1() + "." + address.getOctet2() + "." + address.getOctet3() + "." + address.getOctet4(); } @Override - public void close(ConversationContext<EthernetFrame> context) { + public void close(ConversationContext<BaseEthernetFrame> context) { } diff --git a/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/protocol/ProfinetDCPSubscriptionHandle.java b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/protocol/ProfinetDCPSubscriptionHandle.java new file mode 100644 index 0000000..2c3b06e --- /dev/null +++ b/sandbox/test-java-profinet-driver/src/main/java/org/apache/plc4x/java/profinet/dcp/protocol/ProfinetDCPSubscriptionHandle.java @@ -0,0 +1,54 @@ +/* + * 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 org.apache.plc4x.java.profinet.dcp.field.ProfinetDcpField; +import org.apache.plc4x.java.profinet.dcp.readwrite.ProfinetFrame; +import org.apache.plc4x.java.spi.messages.PlcSubscriber; +import org.apache.plc4x.java.spi.model.DefaultPlcSubscriptionHandle; + +public class ProfinetDCPSubscriptionHandle extends DefaultPlcSubscriptionHandle { + + private final String name; + private final ProfinetDcpField field; + + public ProfinetDCPSubscriptionHandle(PlcSubscriber subscriber, String name, ProfinetDcpField field) { + super(subscriber); + this.name = name; + this.field = field; + } + + public String getName() { + return name; + } + + boolean matches(ProfinetFrame pdu) { + // TODO implement matching logic + return false; + } + + public ProfinetDcpField getField() { + return field; + } + + public String toString() { + return "ProfinetDCPSubscriptionHandle [service=" + field + "]"; + } + +}
