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 f90225bd43321ff7c9f014e6fd73344148b54fbe Author: Ben Hutcheson <[email protected]> AuthorDate: Sun Sep 18 13:30:24 2022 -0600 fix(plc4j/profinet): Cleaned up the message send and receive interface --- .../java/api/messages/PlcDiscoveryItemHandler.java | 2 + .../profinet/config/ProfinetConfiguration.java | 23 ++ .../java/profinet/device/ProfinetCallable.java | 10 + .../plc4x/java/profinet/device/ProfinetDevice.java | 301 +++++++++++++++++++++ .../device/ProfinetDeviceMessageHandler.java | 51 ++++ .../profinet/device/ProfinetMessageWrapper.java | 48 ++++ .../profinet/protocol/ProfinetProtocolLogic.java | 133 ++++----- .../resources/protocols/profinet/profinet.mspec | 8 + 8 files changed, 511 insertions(+), 65 deletions(-) diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java index bb2bb0196..9aa33d4ee 100644 --- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java +++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItemHandler.java @@ -18,6 +18,8 @@ */ package org.apache.plc4x.java.api.messages; +import java.util.List; + public interface PlcDiscoveryItemHandler { void handle(PlcDiscoveryItem discoveryItem); diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/config/ProfinetConfiguration.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/config/ProfinetConfiguration.java index ee3c0c1c3..e262b7ce6 100644 --- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/config/ProfinetConfiguration.java +++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/config/ProfinetConfiguration.java @@ -18,10 +18,21 @@ */ package org.apache.plc4x.java.profinet.config; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.apache.plc4x.java.profinet.device.ProfinetDevice; +import org.apache.plc4x.java.profinet.readwrite.MacAddress; import org.apache.plc4x.java.spi.configuration.Configuration; +import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter; +import org.apache.plc4x.java.spi.configuration.annotations.defaults.BooleanDefaultValue; +import org.apache.plc4x.java.spi.configuration.annotations.defaults.StringDefaultValue; import org.apache.plc4x.java.transport.rawsocket.RawSocketTransportConfiguration; import org.apache.plc4x.java.utils.pcap.netty.handlers.PacketHandler; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + public class ProfinetConfiguration implements Configuration, RawSocketTransportConfiguration { @Override @@ -44,6 +55,18 @@ public class ProfinetConfiguration implements Configuration, RawSocketTransportC return null; } + @ConfigurationParameter("devices") + @StringDefaultValue("") + private String devices; + + public HashMap<MacAddress, ProfinetDevice> configuredDevices = new HashMap<>(); + + public void setDevices(String sDevices) throws DecoderException { + // TODO:- Add support for passing in configured devices. + MacAddress macAddress = new MacAddress(Hex.decodeHex("005056c00001")); + configuredDevices.put(macAddress, new ProfinetDevice(macAddress)); + } + @Override public String toString() { return "Configuration{" + diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java new file mode 100644 index 000000000..fd117c9f4 --- /dev/null +++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetCallable.java @@ -0,0 +1,10 @@ +package org.apache.plc4x.java.profinet.device; + +import org.apache.plc4x.java.api.exceptions.PlcException; +import org.apache.plc4x.java.profinet.readwrite.DceRpc_Packet; + +public interface ProfinetCallable { + void handle(DceRpc_Packet packet) throws PlcException; + + DceRpc_Packet create() throws PlcException; +} diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java new file mode 100644 index 000000000..80f10d961 --- /dev/null +++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDevice.java @@ -0,0 +1,301 @@ +/* + * 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 + * + * https://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.device; + +import io.netty.channel.Channel; +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.profinet.protocol.ProfinetProtocolLogic; +import org.apache.plc4x.java.profinet.readwrite.*; +import org.apache.plc4x.java.spi.ConversationContext; +import org.apache.plc4x.java.spi.generation.*; +import org.apache.plc4x.java.utils.rawsockets.netty.RawSocketChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +public class ProfinetDevice { + + private static final int DEFAULT_UDP_PORT = 34964; + private final Logger logger = LoggerFactory.getLogger(ProfinetDevice.class); + private final DceRpc_ActivityUuid uuid; + + private DatagramSocket udpSocket; + private RawSocketChannel rawSocketChannel; + private Channel channel; + private final MacAddress macAddress; + private ConversationContext<Ethernet_Frame> context; + private ProfinetDeviceState state = ProfinetDeviceState.IDLE; + private Lldp_Pdu lldpPdu = null; + private PnDcp_Pdu dcpPdu = null; + private String ipAddress; + private String portId; + + private AtomicInteger sessionKeyGenerator = new AtomicInteger(1); + + + private void closeUDPSocket() { + // Handle the closing of the connection, might need to send some messages beforehand. + if (udpSocket != null && !udpSocket.isConnected()) { + udpSocket.close(); + context.getChannel().close(); + } + } + + private boolean createUDPSocket() { + if (state != ProfinetDeviceState.IDLE) { + closeUDPSocket(); + } + if (!(channel instanceof RawSocketChannel)) { + logger.warn("Expected a 'raw' transport, closing channel..."); + closeUDPSocket(); + return false; + } + + rawSocketChannel = (RawSocketChannel) channel; + + // Create an udp socket + try { + udpSocket = new DatagramSocket(); + } catch (SocketException e) { + logger.warn("Unable to create udp socket " + e.getMessage()); + closeUDPSocket(); + return false; + } + return true; + } + + public boolean onConnect() { + if (!createUDPSocket()) { + // Unable to create UDP connection + return false; + } + + ProfinetMessageWrapper.sendMessage( + new CreateConnection(), + this + ); + + return false; + } + + private int generateSessionKey() { + // Generate a new session key. + Integer sessionKey = sessionKeyGenerator.getAndIncrement(); + // Reset the session key as soon as it reaches the max for a 16 bit uint + if (sessionKeyGenerator.get() == 0xFFFF) { + sessionKeyGenerator.set(1); + } + return sessionKey; + } + + public boolean hasLldpPdu() { + if (lldpPdu != null) { + return true; + } + return false; + } + + public boolean hasDcpPdu() { + if (dcpPdu != null) { + return true; + } + return false; + } + + public void handle(PlcDiscoveryItem item) { + logger.debug("Received Discovered item at device"); + if (item.getOptions().containsKey("IpAddress")) { + this.ipAddress = item.getOptions().get("IpAddress"); + } + if (item.getOptions().containsKey("PortId")) { + this.portId = item.getOptions().get("PortId"); + } + } + + public void setContext(ConversationContext<Ethernet_Frame> context) { + this.context = context; + channel = context.getChannel(); + } + + public ProfinetDevice(MacAddress macAddress) { + this.macAddress = macAddress; + // Generate a new Activity Id, which will be used throughout the connection. + this.uuid = generateActivityUuid(); + } + + protected static DceRpc_ActivityUuid generateActivityUuid() { + UUID number = UUID.randomUUID(); + try { + WriteBufferByteBased wb = new WriteBufferByteBased(128); + wb.writeLong(64, number.getMostSignificantBits()); + wb.writeLong(64, number.getLeastSignificantBits()); + + ReadBuffer rb = new ReadBufferByteBased(wb.getData()); + return new DceRpc_ActivityUuid(rb.readLong(32), rb.readInt(16), rb.readInt(16), rb.readByteArray(8)); + } catch (SerializationException | ParseException e) { + // Ignore ... this should actually never happen. + } + return null; + } + + public DatagramSocket getUdpSocket() { + return this.udpSocket; + } + + public InetAddress getIpAddress() throws UnknownHostException { + return InetAddress.getByName(this.ipAddress); + } + + public int getPort() { + return DEFAULT_UDP_PORT; + } + + public class CreateConnection implements ProfinetCallable { + + public DceRpc_Packet create() throws PlcException { + try { + 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(), + ProfinetDevice.this.uuid, + 0, 0, DceRpc_Operation.CONNECT, + new PnIoCm_Packet_Req(16696, 16696, 0, 0, + Arrays.asList( + new PnIoCm_Block_ArReq((short) 1, (short) 0, PnIoCm_ArType.IO_CONTROLLER, + new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51")), + ProfinetDevice.this.generateSessionKey(), + ProfinetDevice.this.macAddress, + new Uuid(Hex.decodeHex("dea000006c9711d1827100640008002a")), + false, true, false, + false, PnIoCm_CompanionArType.SINGLE_AR, false, + true, false, PnIoCm_State.ACTIVE, + 600, + // 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. + "plc4x"), + new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.INPUT_CR, + 0x0001, + 0x8892, + false, false, + false, false, PnIoCm_RtClass.RT_CLASS_2, 40, + 0xBBF0, 128, 8, 1, 0, 0xffffffff, + 50, 50, 0xC000, + new org.apache.plc4x.java.profinet.readwrite.MacAddress(Hex.decodeHex("000000000000")), + Collections.singletonList( + new PnIoCm_IoCrBlockReqApi( + Arrays.asList( + new PnIoCm_IoDataObject(0, 0x0001, 0), + new PnIoCm_IoDataObject(0, 0x8000, 1), + new PnIoCm_IoDataObject(0, 0x8001, 2) + ), + new ArrayList<PnIoCm_IoCs>(0)) + )), + new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.OUTPUT_CR, + 0x0002, 0x8892, false, false, + false, false, PnIoCm_RtClass.RT_CLASS_2, 40, + 0xFFFF, 128, 8, 1, 0, 0xffffffff, + 50, 50, 0xC000, + new MacAddress(Hex.decodeHex("000000000000")), + Collections.singletonList( + new PnIoCm_IoCrBlockReqApi( + new ArrayList<PnIoCm_IoDataObject>(0), + Arrays.asList( + new PnIoCm_IoCs(0, 0x0001, 0), + new PnIoCm_IoCs(0, 0x8000, 1), + new PnIoCm_IoCs(0, 0x8001, 2) + ) + ) + ) + ), + new PnIoCm_Block_ExpectedSubmoduleReq((short) 1, (short) 0, + Collections.singletonList( + new PnIoCm_ExpectedSubmoduleBlockReqApi(0, + 0x00000001, 0x00000000, + Arrays.asList( + new PnIoCm_Submodule_NoInputNoOutputData(0x0001, + 0x00000001, false, false, + false, false), + new PnIoCm_Submodule_NoInputNoOutputData(0x8000, + 0x00008000, false, false, + false, false), + new PnIoCm_Submodule_NoInputNoOutputData(0x8001, + 0x00008001, false, false, + false, false) + ) + ) + ) + ), + new PnIoCm_Block_AlarmCrReq((short) 1, (short) 0, + PnIoCm_AlarmCrType.ALARM_CR, 0x8892, false, false, 1, 3, + 0x0000, 200, 0xC000, 0xA000) + )) + ); + + /*// Build the UDP/IP/EthernetFrame to transport the package. + return new Ethernet_Frame(profinetDriverContext.getRemoteMacAddress(), profinetDriverContext.getLocalMacAddress(), + new Ethernet_FramePayload_IPv4(ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE), (short) 64, + profinetDriverContext.getLocalIpAddress(), profinetDriverContext.getRemoteIpAddress(), + new Udp_Packet(profinetDriverContext.getLocalUdpPort(), profinetDriverContext.getRemoteUdpPort(), + dceRpcConnectionRequest)));*/ + } catch (DecoderException e) { + throw new PlcException("Error creating connection request", e); + } + } + + public void handle(DceRpc_Packet dceRpc_packet) throws PlcException { + 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); + + // Update the raw-socket transports filter expression. + ((RawSocketChannel) channel).setRemoteMacAddress(org.pcap4j.util.MacAddress.getByAddress(macAddress.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"); + } + } + } +} diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDeviceMessageHandler.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDeviceMessageHandler.java new file mode 100644 index 000000000..b737801fa --- /dev/null +++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetDeviceMessageHandler.java @@ -0,0 +1,51 @@ +/* + * 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 + * + * https://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.device; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.apache.plc4x.java.api.messages.PlcDiscoveryItem; +import org.apache.plc4x.java.api.messages.PlcDiscoveryItemHandler; +import org.apache.plc4x.java.profinet.readwrite.MacAddress; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ProfinetDeviceMessageHandler implements PlcDiscoveryItemHandler { + + private HashMap<MacAddress, ProfinetDevice> configuredDevices; + + @Override + public void handle(PlcDiscoveryItem discoveryItem) { + try { + MacAddress macAddress = new MacAddress(Hex.decodeHex(discoveryItem.getOptions().get("MacAddress"))); + if (configuredDevices.containsKey(macAddress)) { + configuredDevices.get(macAddress).handle(discoveryItem); + } + } catch (DecoderException e) { + throw new RuntimeException(e); + } + } + + public void setConfiguredDevices(HashMap<MacAddress, ProfinetDevice> configuredDevices) { + this.configuredDevices = configuredDevices; + } +} diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java new file mode 100644 index 000000000..bee0ae8d3 --- /dev/null +++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/device/ProfinetMessageWrapper.java @@ -0,0 +1,48 @@ +package org.apache.plc4x.java.profinet.device; + +import org.apache.plc4x.java.api.exceptions.PlcException; +import org.apache.plc4x.java.profinet.readwrite.*; +import org.apache.plc4x.java.spi.generation.ParseException; +import org.apache.plc4x.java.spi.generation.ReadBufferByteBased; +import org.apache.plc4x.java.spi.generation.SerializationException; +import org.apache.plc4x.java.spi.generation.WriteBufferByteBased; +import org.apache.plc4x.java.utils.rawsockets.netty.RawSocketChannel; + +import java.io.IOException; +import java.net.DatagramPacket; + +public class ProfinetMessageWrapper { + + public static void sendMessage(ProfinetCallable callable, ProfinetDevice context) throws RuntimeException { + try { + DceRpc_Packet packet = callable.create(); + // Serialize it to a byte-payload + WriteBufferByteBased writeBuffer = new WriteBufferByteBased(packet.getLengthInBytes()); + packet.serialize(writeBuffer); + // Create a udp packet. + DatagramPacket connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length); + connectRequestPacket.setAddress(context.getIpAddress()); + connectRequestPacket.setPort(context.getPort()); + + // Send it. + context.getUdpSocket().send(connectRequestPacket); + + // Receive the response. + byte[] resultBuffer = new byte[packet.getLengthInBytes()]; + DatagramPacket connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length); + context.getUdpSocket().receive(connectResponsePacket); + ReadBufferByteBased readBuffer = new ReadBufferByteBased(resultBuffer); + final DceRpc_Packet dceRpc_packet = DceRpc_Packet.staticParse(readBuffer); + callable.handle(dceRpc_packet); + } catch (SerializationException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (ParseException e) { + throw new RuntimeException(e); + } catch (PlcException e) { + throw new RuntimeException(e); + } + + } +} 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 efbac95c4..fe931765c 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 @@ -24,13 +24,20 @@ import org.apache.commons.codec.binary.Hex; 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.api.model.PlcConsumerRegistration; +import org.apache.plc4x.java.api.model.PlcSubscriptionHandle; +import org.apache.plc4x.java.profinet.config.ProfinetConfiguration; import org.apache.plc4x.java.profinet.context.ProfinetDriverContext; +import org.apache.plc4x.java.profinet.device.ProfinetDevice; +import org.apache.plc4x.java.profinet.device.ProfinetDeviceMessageHandler; 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.configuration.HasConfiguration; import org.apache.plc4x.java.spi.generation.*; import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryRequest; +import org.apache.plc4x.java.spi.messages.PlcSubscriber; import org.apache.plc4x.java.utils.rawsockets.netty.RawSocketChannel; import org.pcap4j.core.PcapAddress; import org.pcap4j.core.PcapNativeException; @@ -45,13 +52,11 @@ import java.time.Duration; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; -public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> { +public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> implements HasConfiguration<ProfinetConfiguration>, PlcSubscriber { public static final Duration REQUEST_TIMEOUT = Duration.ofMillis(10000); - - private static AtomicInteger sessionKeyGenerator = new AtomicInteger(1); - private final Logger logger = LoggerFactory.getLogger(ProfinetProtocolLogic.class); private ProfinetDriverContext profinetDriverContext; @@ -61,6 +66,10 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> { private RawSocketChannel rawSocketChannel; private Channel channel; + private ProfinetDeviceMessageHandler handler = new ProfinetDeviceMessageHandler(); + + private ProfinetConfiguration configuration; + private static final Uuid ARUUID; static { @@ -72,53 +81,66 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> { } @Override - public void setContext(ConversationContext<Ethernet_Frame> context) { - super.setContext(context); - this.profinetDriverContext = (ProfinetDriverContext) driverContext; + public void setConfiguration(ProfinetConfiguration configuration) { + this.configuration = configuration; + this.handler.setConfiguredDevices(configuration.configuredDevices); } @Override - public void onConnect(ConversationContext<Ethernet_Frame> context) { - channel = context.getChannel(); - connected = false; - if (!(channel instanceof RawSocketChannel)) { - logger.warn("Expected a 'raw' transport, closing channel..."); - context.getChannel().close(); - return; + public void setContext(ConversationContext<Ethernet_Frame> context) { + super.setContext(context); + this.profinetDriverContext = (ProfinetDriverContext) driverContext; + for (Map.Entry<MacAddress, ProfinetDevice> device : configuration.configuredDevices.entrySet()) { + device.getValue().setContext(context); } - - rawSocketChannel = (RawSocketChannel) channel; - - // Create an udp socket try { - udpSocket = new DatagramSocket(); - } catch (SocketException e) { - logger.warn("Unable to create udp socket " + e.getMessage()); - context.getChannel().close(); - return; + onDeviceDiscovery(); + } catch (InterruptedException e) { } + } + private void onDeviceDiscovery() throws InterruptedException { ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer(); DefaultPlcDiscoveryRequest request = new DefaultPlcDiscoveryRequest( discoverer, new LinkedHashMap<>() ); - discoverer.ongoingDiscoverWithHandler( + // TODO:- Add handler for un-requested messages + discoverer.discoverWithHandler( request, - null, - 5000L, - 30000L + handler ); + waitForDeviceDiscovery(); + } - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Initialize some important datastructures, that will be used a lot. + private void waitForDeviceDiscovery() throws InterruptedException { + // Once we receive an LLDP and PN-DCP message for each device move on. + boolean discovered = false; + int count = 0; + while (!discovered) { + discovered = true; + for (Map.Entry<MacAddress, ProfinetDevice> device : configuration.configuredDevices.entrySet()) { + if (!device.getValue().hasLldpPdu() || !device.getValue().hasDcpPdu()) { + discovered = false; + } + } + if (!discovered) { + Thread.sleep(3000L); + count += 1; + } + if (count > 5) { + break; + } + } + } - // Generate a new Activity Id, which will be used throughout the connection. - profinetDriverContext.setDceRpcActivityUuid(generateActivityUuid()); + @Override + public void onConnect(ConversationContext<Ethernet_Frame> context) { + //////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Initialize some important datastructures, that will be used a lot. // TODO: Possibly we can remove the ARP lookup and simply use the mac address in the connection-response. - // Local connectivity attributes profinetDriverContext.setLocalMacAddress(new MacAddress(rawSocketChannel.getLocalMacAddress().getAddress())); final InetSocketAddress localAddress = (InetSocketAddress) rawSocketChannel.getLocalAddress(); @@ -127,24 +149,8 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> { // Use the port of the udp socket profinetDriverContext.setLocalUdpPort(udpSocket.getPort()); - // Remote connectivity attributes - byte[] macAddress = null; - try { - macAddress = Hex.decodeHex("000000000000"); - } catch (DecoderException e) { - // Ignore this. - } - profinetDriverContext.setRemoteMacAddress(new MacAddress(macAddress)); - final InetSocketAddress remoteAddress = (InetSocketAddress) rawSocketChannel.getRemoteAddress(); - Inet4Address remoteIpAddress = (Inet4Address) remoteAddress.getAddress(); - profinetDriverContext.setRemoteIpAddress(new IpAddress(remoteIpAddress.getAddress())); - profinetDriverContext.setRemoteUdpPort(remoteAddress.getPort()); - - // Generate a new session key. - profinetDriverContext.setSessionKey(sessionKeyGenerator.getAndIncrement()); - // Reset the session key as soon as it reaches the max for a 16 bit uint - if (sessionKeyGenerator.get() == 0xFFFF) { - sessionKeyGenerator.set(1); + for (Map.Entry<MacAddress, ProfinetDevice> device : configuration.configuredDevices.entrySet()) { + device.getValue().onConnect(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -350,7 +356,17 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> { return future; } - @Override + @Override + public PlcConsumerRegistration register(Consumer<PlcSubscriptionEvent> consumer, Collection<PlcSubscriptionHandle> handles) { + return null; + } + + @Override + public void unregister(PlcConsumerRegistration registration) { + + } + + @Override protected void decode(ConversationContext<Ethernet_Frame> context, Ethernet_Frame msg) throws Exception { super.decode(context, msg); } @@ -556,19 +572,6 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> { ); } - protected static DceRpc_ActivityUuid generateActivityUuid() { - UUID number = UUID.randomUUID(); - try { - WriteBufferByteBased wb = new WriteBufferByteBased(128); - wb.writeLong(64, number.getMostSignificantBits()); - wb.writeLong(64, number.getLeastSignificantBits()); - - ReadBuffer rb = new ReadBufferByteBased(wb.getData()); - return new DceRpc_ActivityUuid(rb.readLong(32), rb.readInt(16), rb.readInt(16), rb.readByteArray(8)); - } catch (SerializationException | ParseException e) { - // Ignore ... this should actually never happen. - } - return null; - } + } diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec index 9a964a6e1..a0d8f1ecd 100644 --- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec +++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec @@ -1049,6 +1049,14 @@ ['0x8112' IOX_BLOCK_RES ] ] +[enum uint 16 ProfinetDeviceState + ['0x00' IDLE] + ['0x01' STARTUP] + ['0x02' PRMEND] + ['0x03' APPLRDY] + ['0x04' ABORT] +] + [enum uint 16 PnIoCm_ArType ['0x0001' IO_CONTROLLER] ]
