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:45 UTC
[plc4x] branch plc4j/profinet created (now e99d97c6b)
This is an automated email from the ASF dual-hosted git repository.
hutcheb pushed a change to branch plc4j/profinet
in repository https://gitbox.apache.org/repos/asf/plc4x.git
at e99d97c6b fix(plc4j/profinet): Continued to split device logic out.
This branch includes the following new commits:
new afeb1a491 fix(plc4j/profinet): Fix to remove spaces from the generated connection string.
new b55c5d225 fix(plc4j(profinet): Connections are working for my setup
new bf77f0d95 fix(plc4j(profinet): Add Dummy Advanced Connection Write Request
new 83becf059 fix(plc4j(profinet): Started to add the LLDP broadcast
new bb01815f8 fix(plc4j(profinet): Continued to add the LLDP broadcast
new d133842ce fix(plc4j(profinet): Finished the connection setup.
new dcdad3137 feat(plc4j/profinet): Fixed a few minor issues and added the Application Ready packet
new 6f5b27b2b feat(plc4j/profinet): Escape spaces using %20
new d1c18db45 feat(plc4j/): split out dcp and lldp tasks, so they can be processed separately.
new 40d4cc626 fix(plc4j/profinet): Identified that the Application Ready request comes from the device.
new f90225bd4 fix(plc4j/profinet): Cleaned up the message send and receive interface
new 36b3260cc chore(plc4j/profinet): escape the device name and type within the connection string
new e99d97c6b fix(plc4j/profinet): Continued to split device logic out.
The 13 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[plc4x] 06/13: fix(plc4j(profinet): Finished the connection setup.
Posted by hu...@apache.org.
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 d133842cef1e39ece146eab300435b6edfe94987
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Fri Sep 9 11:20:59 2022 -0600
fix(plc4j(profinet): Finished the connection setup.
---
.../profinet/discovery/ProfinetPlcDiscoverer.java | 144 +++++++++++++++++----
.../profinet/protocol/ProfinetProtocolLogic.java | 51 +++++++-
.../profinet/readwrite/utils/StaticHelper.java | 33 +++++
.../resources/protocols/profinet/profinet.mspec | 58 ++++++---
4 files changed, 243 insertions(+), 43 deletions(-)
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 96b2dba67..35681f795 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
@@ -18,6 +18,8 @@
*/
package org.apache.plc4x.java.profinet.discovery;
+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.api.messages.PlcDiscoveryItemHandler;
@@ -47,6 +49,8 @@ import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.function.Function;
+import java.util.function.IntBinaryOperator;
public class ProfinetPlcDiscoverer implements PlcDiscoverer {
@@ -56,6 +60,8 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
// The constants for the different block names and their actual meaning.
private static final String DEVICE_TYPE_NAME = "DEVICE_PROPERTIES_OPTION-1";
private static final String DEVICE_NAME_OF_STATION = "DEVICE_PROPERTIES_OPTION-2";
+ private static final String PLC4X_LLDP_IDENTIFIER = "PLC4X PROFINET Controller Client";
+ private static final String PLC4X_LLDP_PORT = "port001.plc4x";
private static final String DEVICE_ID = "DEVICE_PROPERTIES_OPTION-3";
private static final String DEVICE_ROLE = "DEVICE_PROPERTIES_OPTION-4";
private static final String DEVICE_OPTIONS = "DEVICE_PROPERTIES_OPTION-5";
@@ -327,14 +333,14 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
try {
Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
- PnDcp_Pdu pdu;
+ Lldp_Pdu pdu;
// Access the pdu data (either directly or by
// unpacking the content of the VLAN packet.
if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
- pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
+ pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
} else {
- pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
+ pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
}
// Inspect the PDU itself
// (in this case we only process identify response packets)
@@ -351,28 +357,102 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
Task t = new Task(handle, listener);
pool.execute(t);
- // Construct and send the LLDP Probe
+ Function<Object, Boolean> lldpTimer =
+ message -> {
+ // Construct and send the LLDP Probe
+ TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
+ new TlvProfibusSubTypePortStatus(0x00)
+ );
+
+ TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
+ new TlvProfibusSubTypeChassisMac(new MacAddress(linkLayerAddress.getAddress()))
+ );
+
+ TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
+ (short) 0x01,
+ (short) 0x03,
+ 0x0020,
+ 0x0010
+ );
+
+ Ethernet_Frame identificationRequest = null;
+ try {
+ identificationRequest = new Ethernet_Frame(
+ // Pre-Defined LLDP discovery MAC address
+ new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
+ toPlc4xMacAddress(macAddress),
+ new Ethernet_FramePayload_LLDP(
+ new Lldp_Pdu(
+ Arrays.asList(
+ new TlvChassisId(
+ PLC4X_LLDP_IDENTIFIER.length() + 1,
+ (short) 7,
+ PLC4X_LLDP_IDENTIFIER
+ ),
+ new TlvPortId(
+ PLC4X_LLDP_PORT.length() + 1,
+ (short) 7,
+ PLC4X_LLDP_PORT
+ ),
+ new TlvTimeToLive(2, 20),
+ new TlvOrganizationSpecific(
+ portStatus.getLengthInBytes(),
+ portStatus
+ ),
+ new TlvOrganizationSpecific(
+ chassisMac.getLengthInBytes(),
+ chassisMac
+ ),
+ new TlvOrganizationSpecific(
+ ieee.getLengthInBytes(),
+ ieee
+ ),
+ new TlvManagementAddress(
+ 12,
+ ManagementAddressSubType.IPV4,
+ new IpAddress(Hex.decodeHex("c0a8006e")),
+ (short) 0x03,
+ 0x01L,
+ (short) 0x00
+ ),
+ new EndOfLldp(0)
+ )
+ )));
+ } catch (DecoderException e) {
+ throw new RuntimeException(e);
+ }
+ WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
+ try {
+ identificationRequest.serialize(buffer);
+ } catch (SerializationException e) {
+ throw new RuntimeException(e);
+ }
+ Packet packet = null;
+ try {
+ packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
+ } catch (IllegalRawDataException e) {
+ throw new RuntimeException(e);
+ }
+ try {
+ handle.sendPacket(packet);
+ } catch (PcapNativeException e) {
+ throw new RuntimeException(e);
+ } catch (NotOpenException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ };
+ Timer timer = new Timer();
- Ethernet_Frame identificationRequest = new Ethernet_Frame(
- // Pre-Defined PROFINET discovery MAC address
- new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
- toPlc4xMacAddress(macAddress),
- new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
- new Ethernet_FramePayload_PnDcp(
- new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
- 1,
- 256,
- Collections.singletonList(
- new PnDcp_Block_ALLSelector()
- )))));
- WriteBufferByteBased buffer = new WriteBufferByteBased(34);
- identificationRequest.serialize(buffer);
- Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
- handle.sendPacket(packet);
+ // Schedule to run after every 3 second(3000 millisecond)
+ timer.scheduleAtFixedRate(
+ new LLDPTask(handle, lldpTimer),
+ 3000,
+ 3000);
}
}
}
- } catch (IllegalRawDataException | NotOpenException | PcapNativeException | SerializationException e) {
+ } catch (NotOpenException | PcapNativeException e) {
logger.error("Got an exception while processing raw socket data", e);
for (PcapHandle openHandle : openHandles) {
@@ -411,10 +491,28 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
}
}
+ private static class LLDPTask extends TimerTask {
+
+ private final Logger logger = LoggerFactory.getLogger(Task.class);
+
+ private final PcapHandle handle;
+ private final Function<Object, Boolean> operator;
+
+ public LLDPTask(PcapHandle handle, Function<Object, Boolean> operator) {
+ this.handle = handle;
+ this.operator = operator;
+ }
+
+ @Override
+ public void run() {
+ operator.apply(null);
+ }
+ }
+
public static void main(String[] args) throws Exception {
ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer();
- discoverer.discover(null);
-
+ //discoverer.discover(null);
+ discoverer.lldpProbe();
Thread.sleep(10000);
}
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 8e4e666ab..d3c1d1024 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
@@ -184,6 +184,31 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
udpSocket.send(connectRequestPacket);
+ // Receive the response.
+ resultBuffer = new byte[profinetAdvancedConnectionWriteRequest.getLengthInBytes()];
+ connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
+ udpSocket.receive(connectResponsePacket);
+
+
+ // Create the packet
+ final DceRpc_Packet profinetAdvancedConnectionParameterEnd = createProfinetAdvancedConnectionParameterEnd();
+ // Serialize it to a byte-payload
+ writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionParameterEnd.getLengthInBytes());
+ profinetAdvancedConnectionParameterEnd.serialize(writeBuffer);
+ // Create a udp packet.
+ connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
+ connectRequestPacket.setAddress(remoteAddress.getAddress());
+ connectRequestPacket.setPort(remoteAddress.getPort());
+ // Send it.
+
+ udpSocket.send(connectRequestPacket);
+
+ // Receive the response.
+ resultBuffer = new byte[profinetAdvancedConnectionParameterEnd.getLengthInBytes()];
+ connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
+ udpSocket.receive(connectResponsePacket);
+
+
} catch (SerializationException | IOException | PlcException | ParseException e) {
logger.error("Error", e);
}
@@ -255,7 +280,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
// 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.
- "controller"),
+ "plc4x"),
new PnIoCm_Block_IoCrReq((short) 1, (short) 0, PnIoCm_IoCrType.INPUT_CR,
0x0001,
0x8892,
@@ -383,13 +408,35 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
(short) 1,
(short) 0,
new PascalString("port-001"),
- new PascalString("controller")
+ new PascalString("plc4x")
)
)
))
);
}
+ private DceRpc_Packet createProfinetAdvancedConnectionParameterEnd() throws PlcException {
+
+ 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(),
+ profinetDriverContext.getDceRpcActivityUuid(),
+ 0, 1, DceRpc_Operation.CONTROL,
+ new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+ Arrays.asList(
+ new PnIoCm_Control_Request(
+ (short) 1,
+ (short) 0,
+ ARUUID,
+ 0x0001,
+ 0x0001
+ )
+ ))
+ );
+ }
+
protected static DceRpc_ActivityUuid generateActivityUuid() {
UUID number = UUID.randomUUID();
try {
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/readwrite/utils/StaticHelper.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/readwrite/utils/StaticHelper.java
index 97678c957..7331df69b 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/readwrite/utils/StaticHelper.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/readwrite/utils/StaticHelper.java
@@ -19,7 +19,11 @@
package org.apache.plc4x.java.profinet.readwrite.utils;
import org.apache.plc4x.java.profinet.readwrite.IpAddress;
+import org.apache.plc4x.java.profinet.readwrite.LldpUnit;
import org.apache.plc4x.java.profinet.readwrite.PnDcp_FrameId;
+import org.apache.plc4x.java.spi.generation.*;
+
+import java.util.List;
public class StaticHelper {
@@ -153,4 +157,33 @@ public class StaticHelper {
return PnDcp_FrameId.RESERVED;
}
+ public static boolean isSysexEnd(ReadBuffer io) {
+ byte[] test = ((ReadBufferByteBased) io).getBytes(io.getPos(), io.getPos() + 2);
+ return ((ReadBufferByteBased) io).getBytes(io.getPos(), io.getPos() + 2)[0] == (byte) 0x00;
+ }
+
+ public static LldpUnit parseSysexString(ReadBuffer io) {
+ try {
+ LldpUnit unit = LldpUnit.staticParse(io);
+ return unit;
+ } catch (ParseException e) {
+ return null;
+ }
+ }
+
+ public static void serializeSysexString(WriteBuffer io, LldpUnit unit) {
+ try {
+ unit.serialize(io);
+ } catch (SerializationException e) {
+ }
+ }
+
+ public static int lengthSysexString(List<LldpUnit> data) {
+ int lengthInBytes = 0;
+ for (LldpUnit unit : data) {
+ lengthInBytes += unit.getLengthInBytes();
+ }
+ return lengthInBytes;
+ }
+
}
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 19dcdcfee..57f3ced3a 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -83,34 +83,34 @@
[simple PnDcp_Pdu pdu ]
]
['0x88cc' Ethernet_FramePayload_LLDP
- [simple Lldp_Pdu pdu ]
+ [simple Lldp_Pdu pdu ]
]
]
]
[type Lldp_Pdu
- [array LldpUnit lldpParameters]
+ [manualArray LldpUnit lldpParameters terminated 'STATIC_CALL("isSysexEnd", readBuffer)' 'STATIC_CALL("parseSysexString", readBuffer)' 'STATIC_CALL("serializeSysexString", writeBuffer, _value)' 'STATIC_CALL("lengthSysexString", lldpParameters)']
]
[discriminatedType LldpUnit
- [disciminator uint 7 tlvId ]
- [implicit uint 9 tlvIdLength 'lengthInBytes' ] ]
- [typeSwitch idSubType
+ [discriminator TlvType tlvId ]
+ [simple uint 9 tlvIdLength ]
+ [typeSwitch tlvId
['END_OF_LLDP' EndOfLldp
- [
- ['CHASSIS_ID' TlvChassisId
- [simple uint 8 chassisIdSubType ]
- [simple vstring '(tlvIdLength * 8) + 1' chassisId ]
]
- ['PORT_ID' TlvPortId
+ ['CHASSIS_ID' TlvChassisId(uint 9 tlvIdLength)
+ [simple uint 8 chassisIdSubType ]
+ [simple vstring '(tlvIdLength - 1) * 8' chassisId ]
+ ]
+ ['PORT_ID' TlvPortId(uint 9 tlvIdLength)
[simple uint 8 portIdSubType ]
- [simple vstring '(tlvIdLength * 8) + 1' portId ]
+ [simple vstring '(tlvIdLength - 1) * 8' portId ]
]
- ['PORT_ID' TlvPortId
- [simple uint 16 tlvTimeToLive ]
+ ['TIME_TO_LIVE' TlvTimeToLive
+ [simple uint 16 tlvTimeToLiveUnit ]
]
['MANAGEMENT_ADDRESS' TlvManagementAddress
- [implicit uint 8 addressStringLength ]
+ [implicit uint 8 addressStringLength '5' ]
[simple ManagementAddressSubType addressSubType ]
[simple IpAddress ipAddress ]
[simple uint 8 interfaceSubType ]
@@ -121,15 +121,15 @@
[simple TlvOrganizationSpecificUnit organizationSpecificUnit ]
]
]
-[
+]
-[type TlvOrganizationSpecificUnit(uint 9 unitLength)
+[discriminatedType TlvOrganizationSpecificUnit
[discriminator uint 24 uniqueCode]
[typeSwitch uniqueCode
['0x000ECF' TlvOrgSpecificProfibus
[simple TlvOrgSpecificProfibusUnit specificUnit ]
]
- [´0x00120F' TlvOrgSpecificIeee8023
+ ['0x00120F' TlvOrgSpecificIeee8023
[simple uint 8 subType ]
[simple uint 8 negotiationSupport ]
[simple uint 16 negotiationCapability ]
@@ -141,12 +141,18 @@
[discriminatedType TlvOrgSpecificProfibusUnit
[discriminator TlvProfibusSubType subType]
[typeSwitch subType
+ ['PORT_STATUS' TlvProfibusSubTypePortStatus
+ [simple uint 16 rtClassPortStatus]
+ ]
+ ['CHASSIS_MAC' TlvProfibusSubTypeChassisMac
+ [simple MacAddress macAddress]
+ ]
]
]
[enum TlvProfibusSubType
['0x02' PORT_STATUS]
- [´0x05' CHASSIS_MAC]
+ ['0x05' CHASSIS_MAC]
]
// 4.10.3.2
@@ -823,6 +829,22 @@
[simple MacAddress cmResponderMacAddr ]
[simple uint 16 responderUDPRTPort ]
]
+ ['IOD_CONTROL_REQ' PnIoCm_Control_Request
+ [reserved uint 16 '0x0000' ]
+ [simple Uuid arUuid ]
+ [simple uint 16 sessionKey ]
+ [reserved uint 16 '0x0000' ]
+ [simple uint 16 controlCommand ]
+ [reserved uint 16 '0x0000' ]
+ ]
+ ['IOD_CONTROL_RES' PnIoCm_Control_Response
+ [reserved uint 16 '0x0000' ]
+ [simple Uuid arUuid ]
+ [simple uint 16 sessionKey ]
+ [reserved uint 16 '0x0000' ]
+ [simple uint 16 controlCommand ]
+ [reserved uint 16 '0x0000' ]
+ ]
['IO_CR_BLOCK_REQ' PnIoCm_Block_IoCrReq
[simple PnIoCm_IoCrType ioCrType ]
[simple uint 16 ioCrReference ]
[plc4x] 11/13: fix(plc4j/profinet): Cleaned up the message send and receive interface
Posted by hu...@apache.org.
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 <be...@gmail.com>
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]
]
[plc4x] 09/13: feat(plc4j/): split out dcp and lldp tasks, so they can be processed separately.
Posted by hu...@apache.org.
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 d1c18db45f5334e1ddc6d529008fb531fc217722
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Fri Sep 16 15:53:03 2022 -0600
feat(plc4j/): split out dcp and lldp tasks, so they can be processed separately.
---
.../profinet/discovery/ProfinetPlcDiscoverer.java | 755 +++++++++++----------
1 file changed, 387 insertions(+), 368 deletions(-)
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 4486e1abc..bc7ef865d 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
@@ -20,7 +20,6 @@ package org.apache.plc4x.java.profinet.discovery;
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.api.messages.PlcDiscoveryItemHandler;
import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
@@ -50,7 +49,6 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
-import java.util.function.IntBinaryOperator;
public class ProfinetPlcDiscoverer implements PlcDiscoverer {
@@ -68,6 +66,12 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
private static final String DEVICE_INSTANCE = "DEVICE_PROPERTIES_OPTION-7";
private static final String IP_OPTION_IP = "IP_OPTION-2";
+ ExecutorService pool = Executors.newSingleThreadExecutor();
+ Map<MacAddress, PcapHandle> openHandles = new HashMap<>();
+ List<PlcDiscoveryItem> values = new ArrayList<>();
+
+ Set<Timer> periodicTimers = new HashSet<>();
+
private final Logger logger = LoggerFactory.getLogger(ProfinetPlcDiscoverer.class);
@Override
@@ -75,200 +79,49 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
return discoverWithHandler(discoveryRequest, null);
}
- public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
- CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
- Set<PcapHandle> openHandles = new HashSet<>();
- List<PlcDiscoveryItem> values = new ArrayList<>();
+ public void openDiscoverHandles() {
try {
for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
// It turned out on some MAC network devices without any ip addresses
// the compiling of the filter expression was causing errors. As
// currently there was no other way to detect this, this check seems
// to be sufficient.
- if(dev.getAddresses().size() == 0) {
+ if (dev.getAddresses().size() == 0) {
continue;
}
if (!dev.isLoopBack()) {
for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
- openHandles.add(handle);
+ openHandles.put(toPlc4xMacAddress(macAddress), handle);
- ExecutorService pool = Executors.newSingleThreadExecutor();
-
- // Only react on PROFINET DCP packets targeted at our current MAC address.
+ // Only react on PROFINET DCP or LLDP packets targeted at our current MAC address.
handle.setFilter(
- "((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")",
+ "(((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")) or (ether proto 0x88cc)",
BpfProgram.BpfCompileMode.OPTIMIZE);
-
- PacketListener listener =
- packet -> {
- // EthernetPacket is the highest level of abstraction we can be expecting.
- // Everything inside this we will have to decode ourselves.
- if (packet instanceof EthernetPacket) {
- EthernetPacket ethernetPacket = (EthernetPacket) packet;
- boolean isPnPacket = false;
- // I have observed some times the ethernet packets being wrapped inside a VLAN
- // Packet, in this case we simply unpack the content.
- if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
- Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
- if (PN_EtherType.equals(vlanPacket.getHeader().getType())) {
- isPnPacket = true;
- }
- } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType())) {
- isPnPacket = true;
- }
-
- // It's a PROFINET packet.
- if (isPnPacket) {
- ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
- try {
- Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
- PnDcp_Pdu pdu;
- // Access the pdu data (either directly or by
- // unpacking the content of the VLAN packet.
- if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
- Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
- pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
- } else {
- pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
- }
- // Inspect the PDU itself
- // (in this case we only process identify response packets)
- if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
- PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
-
- Map<String, PnDcp_Block> blocks = new HashMap<>();
- for (PnDcp_Block block : identifyResPDU.getBlocks()) {
- String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
- blocks.put(blockName, block);
- }
-
- // The mac address of the device we found
- org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
- // The mac address of the local network device
- org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
-
- String deviceTypeName = "unknown";
- if (blocks.containsKey(DEVICE_TYPE_NAME)) {
- PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
- deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
- }
-
- String deviceName = "unknown";
- if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
- PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
- deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
- }
-
- String role = "unknown";
- if (blocks.containsKey(DEVICE_ROLE)) {
- role = "";
- PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
- if (block.getPnioSupervisor()) {
- role += ",SUPERVISOR";
- }
- if (block.getPnioMultidevive()) {
- role += ",MULTIDEVICE";
- }
- if (block.getPnioController()) {
- role += ",CONTROLLER";
- }
- if (block.getPnioDevice()) {
- role += ",DEVICE";
- }
- // Cut off the first comma
- if (role.length() > 0) {
- role = role.substring(1);
- } else {
- role = "unknown";
- }
- }
-
- String remoteIpAddress = "unknown";
- String remoteSubnetMask = "unknown";
- if (blocks.containsKey(IP_OPTION_IP)) {
- PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
- try {
- InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
- remoteIpAddress = addr.getHostAddress();
- InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
- remoteSubnetMask = netMask.getHostAddress();
- } catch (UnknownHostException e) {
- remoteIpAddress = "invalid";
- }
- }
-
- // Get the Vendor Id and the Device Id
- String vendorId = "unknown";
- String deviceId = "unknown";
- if (blocks.containsKey(DEVICE_ID)) {
- PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
- vendorId = String.format("%04X", block.getVendorId());
- deviceId = String.format("%04X", block.getDeviceId());
- }
-
- Map<String, String> options = new HashMap<>();
- options.put("remoteIpAddress", remoteIpAddress);
- options.put("remoteSubnetMask", remoteSubnetMask);
- options.put("remoteMacAddress", srcAddr.toString());
- options.put("localMacAddress", dstAddr.toString());
- options.put("deviceTypeName", deviceTypeName);
- options.put("deviceName", deviceName);
- options.put("vendorId", vendorId);
- options.put("deviceId", deviceId);
- options.put("role", role);
- String name = deviceTypeName + " - " + deviceName;
- PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
- ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
- remoteIpAddress, options, name, Collections.emptyMap());
- values.add(value);
-
- // If we have a discovery handler, pass it to the handler callback
- if (handler != null) {
- handler.handle(value);
- }
-
- logger.debug("Found new device: '{}' with connection-url '{}'",
- value.getName(), value.getConnectionUrl());
- }
- } catch (ParseException e) {
- logger.error("Got error decoding packet", e);
- }
- }
- }
- };
- Task t = new Task(handle, listener);
- pool.execute(t);
-
- // Construct and send the search request.
- Ethernet_Frame identificationRequest = new Ethernet_Frame(
- // Pre-Defined PROFINET discovery MAC address
- new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
- toPlc4xMacAddress(macAddress),
- new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
- new Ethernet_FramePayload_PnDcp(
- new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
- 1,
- 256,
- Collections.singletonList(
- new PnDcp_Block_ALLSelector()
- )))));
- WriteBufferByteBased buffer = new WriteBufferByteBased(34);
- identificationRequest.serialize(buffer);
- Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
- handle.sendPacket(packet);
}
}
}
- } catch (IllegalRawDataException | NotOpenException | PcapNativeException | SerializationException e) {
+ } catch (NotOpenException | PcapNativeException e) {
logger.error("Got an exception while processing raw socket data", e);
- future.completeExceptionally(new PlcException("Got an internal error while performing discovery"));
- for (PcapHandle openHandle : openHandles) {
- openHandle.close();
+ for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+ PcapHandle openHandle = entry.getValue();
+ try {
+ openHandle.breakLoop();
+ openHandle.close();
+ } catch (NotOpenException error) {
+ logger.info("Handle already closed.");
+ }
+ }
+ for (Timer timer : periodicTimers) {
+ timer.cancel();
+ timer.purge();
}
- return future;
}
+ }
+
+ public CompletableFuture<PlcDiscoveryResponse> setDiscoveryEndTimer(PlcDiscoveryRequest discoveryRequest, long delay) {
+ CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
// Create a timer that completes the future after a given time with all the responses it found till then.
Timer timer = new Timer("Discovery Timeout");
@@ -276,229 +129,395 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
public void run() {
PlcDiscoveryResponse response =
new DefaultPlcDiscoveryResponse(discoveryRequest, PlcResponseCode.OK, values);
- future.complete(response);
- for (PcapHandle openHandle : openHandles) {
- openHandle.close();
+ for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+ PcapHandle openHandle = entry.getValue();
+ try {
+ openHandle.breakLoop();
+ openHandle.close();
+ } catch (Exception e) {
+ logger.error("Error occurred while closing handle");
+ }
+
}
+ for (Timer timer : periodicTimers) {
+ timer.cancel();
+ timer.purge();
+ }
+ future.complete(response);
}
- }, 5000L);
+ }, delay);
return future;
}
- public void lldpProbe() {
- Set<PcapHandle> openHandles = new HashSet<>();
- try {
- for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
- // It turned out on some MAC network devices without any ip addresses
- // the compiling of the filter expression was causing errors. As
- // currently there was no other way to detect this, this check seems
- // to be sufficient.
- if(dev.getAddresses().size() == 0) {
- continue;
+ public PacketListener createListener(PcapHandle handle, PlcDiscoveryItemHandler handler) {
+ PacketListener listener =
+ packet -> {
+ // EthernetPacket is the highest level of abstraction we can be expecting.
+ // Everything inside this we will have to decode ourselves.
+ if (packet instanceof EthernetPacket) {
+ EthernetPacket ethernetPacket = (EthernetPacket) packet;
+ boolean isPnPacket = false;
+ // I have observed sometimes the ethernet packets being wrapped inside a VLAN
+ // Packet, in this case we simply unpack the content.
+ if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
+ Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
+ if (PN_EtherType.equals(vlanPacket.getHeader().getType()) || LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
+ isPnPacket = true;
+ }
+ } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType()) || LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
+ isPnPacket = true;
+ }
+
+ // It's a PROFINET or LLDP packet.
+ if (isPnPacket) {
+ ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
+ try {
+ Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
+
+ // Access the pdu data (either directly or by
+ // unpacking the content of the VLAN packet.
+ if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
+ Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
+ if (vlefpl.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
+ PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
+ processPnDcp(pdu, ethernetPacket, handler);
+ } else if (vlefpl.getPayload() instanceof Ethernet_FramePayload_LLDP) {
+ Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
+ processLldp(pdu, ethernetPacket, handler);
+ }
+ } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
+ PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
+ processPnDcp(pdu, ethernetPacket, handler);
+ } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_LLDP) {
+ Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
+ processLldp(pdu, ethernetPacket, handler);
+ }
+
+ } catch (ParseException e) {
+ logger.error("Got error decoding packet", e);
+ }
+ }
}
- if (!dev.isLoopBack()) {
- for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
- org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
- PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
- openHandles.add(handle);
+ };
+ return listener;
+ }
- ExecutorService pool = Executors.newSingleThreadExecutor();
+ public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
+ openDiscoverHandles();
+ startListener(handler);
+ startLldpPoll();
+ startPnDcpPoll();
+ CompletableFuture<PlcDiscoveryResponse> future = setDiscoveryEndTimer(discoveryRequest, 10000L);
+ return future;
+ }
- // Only react on PROFINET DCP packets targeted at our current MAC address.
- handle.setFilter(
- "(ether proto 0x88cc)",
- BpfProgram.BpfCompileMode.OPTIMIZE);
+ private void processPnDcp(PnDcp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
+ // Inspect the PDU itself
+ // (in this case we only process identify response packets)
+ if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
+ PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
- PacketListener listener =
- packet -> {
- // EthernetPacket is the highest level of abstraction we can be expecting.
- // Everything inside this we will have to decode ourselves.
- if (packet instanceof EthernetPacket) {
- EthernetPacket ethernetPacket = (EthernetPacket) packet;
- boolean isLldpPacket = false;
- // I have observed sometimes the ethernet packets being wrapped inside a VLAN
- // Packet, in this case we simply unpack the content.
- if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
- Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
- if (LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
- isLldpPacket = true;
- }
- } else if (LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
- isLldpPacket = true;
- }
-
- // It's a LLDP packet.
- if (isLldpPacket) {
- ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
- try {
- Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
- Lldp_Pdu pdu;
- // Access the pdu data (either directly or by
- // unpacking the content of the VLAN packet.
- if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
- Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
- pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
- } else {
- pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
- }
- // Inspect the PDU itself
- // (in this case we only process identify response packets)
-
-
- logger.debug("Found new device: '{}' using LLDP '{}'",
- "Not Sure", "Not Sure");
- } catch (ParseException ex) {
- throw new RuntimeException(ex);
- }
- }
- }
- };
- Task t = new Task(handle, listener);
- pool.execute(t);
-
- Function<Object, Boolean> lldpTimer =
- message -> {
- // Construct and send the LLDP Probe
- TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
- new TlvProfibusSubTypePortStatus(0x00)
- );
-
- TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
- new TlvProfibusSubTypeChassisMac(new MacAddress(linkLayerAddress.getAddress()))
- );
-
- TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
- (short) 0x01,
- (short) 0x03,
- 0x0020,
- 0x0010
- );
-
- Ethernet_Frame identificationRequest = null;
- try {
- identificationRequest = new Ethernet_Frame(
- // Pre-Defined LLDP discovery MAC address
- new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
- toPlc4xMacAddress(macAddress),
- new Ethernet_FramePayload_LLDP(
- new Lldp_Pdu(
- Arrays.asList(
- new TlvChassisId(
- PLC4X_LLDP_IDENTIFIER.length() + 1,
- (short) 7,
- PLC4X_LLDP_IDENTIFIER
- ),
- new TlvPortId(
- PLC4X_LLDP_PORT.length() + 1,
- (short) 7,
- PLC4X_LLDP_PORT
- ),
- new TlvTimeToLive(2, 20),
- new TlvOrganizationSpecific(
- portStatus.getLengthInBytes(),
- portStatus
- ),
- new TlvOrganizationSpecific(
- chassisMac.getLengthInBytes(),
- chassisMac
- ),
- new TlvOrganizationSpecific(
- ieee.getLengthInBytes(),
- ieee
- ),
- new TlvManagementAddress(
- 12,
- ManagementAddressSubType.IPV4,
- new IpAddress(Hex.decodeHex("c0a85a6e")),
- (short) 0x03,
- 0x01L,
- (short) 0x00
- ),
- new EndOfLldp(0)
- )
- )));
- } catch (DecoderException e) {
- throw new RuntimeException(e);
- }
- WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
- try {
- identificationRequest.serialize(buffer);
- } catch (SerializationException e) {
- throw new RuntimeException(e);
- }
- Packet packet = null;
- try {
- packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
- } catch (IllegalRawDataException e) {
- throw new RuntimeException(e);
- }
- try {
- handle.sendPacket(packet);
- } catch (PcapNativeException e) {
- throw new RuntimeException(e);
- } catch (NotOpenException e) {
- throw new RuntimeException(e);
- }
- return null;
- };
- Timer timer = new Timer();
-
- // Schedule to run after every 3 second(3000 millisecond)
- timer.scheduleAtFixedRate(
- new LLDPTask(handle, lldpTimer),
- 3000,
- 3000);
- }
+ Map<String, PnDcp_Block> blocks = new HashMap<>();
+ for (PnDcp_Block block : identifyResPDU.getBlocks()) {
+ String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
+ blocks.put(blockName, block);
+ }
+
+ // The mac address of the device we found
+ org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
+ // The mac address of the local network device
+ org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
+
+ String deviceTypeName = "unknown";
+ if (blocks.containsKey(DEVICE_TYPE_NAME)) {
+ PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
+ deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
+ }
+
+ String deviceName = "unknown";
+ if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
+ PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
+ deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
+ }
+
+ String role = "unknown";
+ if (blocks.containsKey(DEVICE_ROLE)) {
+ role = "";
+ PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
+ if (block.getPnioSupervisor()) {
+ role += ",SUPERVISOR";
+ }
+ if (block.getPnioMultidevive()) {
+ role += ",MULTIDEVICE";
+ }
+ if (block.getPnioController()) {
+ role += ",CONTROLLER";
+ }
+ if (block.getPnioDevice()) {
+ role += ",DEVICE";
+ }
+ // Cut off the first comma
+ if (role.length() > 0) {
+ role = role.substring(1);
+ } else {
+ role = "unknown";
}
}
- } catch (NotOpenException | PcapNativeException e) {
- logger.error("Got an exception while processing raw socket data", e);
- for (PcapHandle openHandle : openHandles) {
- openHandle.close();
+ String remoteIpAddress = "unknown";
+ String remoteSubnetMask = "unknown";
+ if (blocks.containsKey(IP_OPTION_IP)) {
+ PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
+ try {
+ InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
+ remoteIpAddress = addr.getHostAddress();
+ InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
+ remoteSubnetMask = netMask.getHostAddress();
+ } catch (UnknownHostException e) {
+ remoteIpAddress = "invalid";
+ }
}
+
+ // Get the Vendor Id and the Device Id
+ String vendorId = "unknown";
+ String deviceId = "unknown";
+ if (blocks.containsKey(DEVICE_ID)) {
+ PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
+ vendorId = String.format("%04X", block.getVendorId());
+ deviceId = String.format("%04X", block.getDeviceId());
+ }
+
+ Map<String, String> options = new HashMap<>();
+ options.put("remoteIpAddress", remoteIpAddress);
+ options.put("remoteSubnetMask", remoteSubnetMask);
+ options.put("remoteMacAddress", srcAddr.toString());
+ options.put("localMacAddress", dstAddr.toString());
+ options.put("deviceTypeName", deviceTypeName);
+ options.put("deviceName", deviceName);
+ options.put("vendorId", vendorId);
+ options.put("deviceId", deviceId);
+ options.put("role", role);
+ String name = deviceTypeName + " - " + deviceName;
+ PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
+ ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
+ remoteIpAddress, options, name, Collections.emptyMap());
+ values.add(value);
+
+ // If we have a discovery handler, pass it to the handler callback
+ if (handler != null) {
+ handler.handle(value);
+ }
+
+ logger.debug("Found new device: '{}' with connection-url '{}'",
+ value.getName(), value.getConnectionUrl());
}
}
- private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
- byte[] address = pcap4jMacAddress.getAddress();
- return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
+ private void processLldp(Lldp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
+ logger.debug("Found new lldp device: '' with connection-url ''");
}
- private static class Task implements Runnable {
+ public void startPnDcpPoll() {
+ for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+ PcapHandle handle = entry.getValue();
+ MacAddress macAddress = entry.getKey();
+ // Construct and send the search request.
+
+ Function<Object, Boolean> pnDcpTimer =
+ message -> {
+ Ethernet_Frame identificationRequest = new Ethernet_Frame(
+ // Pre-Defined PROFINET discovery MAC address
+ new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
+ macAddress,
+ new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
+ new Ethernet_FramePayload_PnDcp(
+ new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
+ 1,
+ 256,
+ Collections.singletonList(
+ new PnDcp_Block_ALLSelector()
+ )))));
+ WriteBufferByteBased buffer = new WriteBufferByteBased(34);
+ try {
+ identificationRequest.serialize(buffer);
+ } catch (SerializationException e) {
+ throw new RuntimeException(e);
+ }
+ Packet packet = null;
+ try {
+ packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
+ } catch (IllegalRawDataException e) {
+ throw new RuntimeException(e);
+ }
+ try {
+ handle.sendPacket(packet);
+ } catch (PcapNativeException e) {
+ throw new RuntimeException(e);
+ } catch (NotOpenException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ };
+
+ Timer timer = new Timer();
+ periodicTimers.add(timer);
- private final Logger logger = LoggerFactory.getLogger(Task.class);
+ // Schedule to run after every 3 second(3000 millisecond)
+ timer.scheduleAtFixedRate(
+ new PeriodicTask(handle, pnDcpTimer),
+ 5000,
+ 5000);
+ }
+ }
- private final PcapHandle handle;
- private final PacketListener listener;
+ public void startListener(PlcDiscoveryItemHandler handler) {
+ for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+ PcapHandle handle = entry.getValue();
+ MacAddress macAddress = entry.getKey();
+ // Construct and send the search request.
+
+ Function<Object, Boolean> pnDcpTimer =
+ message -> {
+ PacketListener listener = createListener(handle, handler);
+ try {
+ handle.loop(-1, listener);
+ } catch (InterruptedException e) {
+ logger.error("Got error handling raw socket", e);
+ Thread.currentThread().interrupt();
+ } catch (PcapNativeException | NotOpenException e) {
+ logger.error("Got error handling raw socket", e);
+ }
+ return null;
+ };
- public Task(PcapHandle handle, PacketListener listener) {
- this.handle = handle;
- this.listener = listener;
+ Timer timer = new Timer();
+ periodicTimers.add(timer);
+
+ // Schedule to run after every 3 second(3000 millisecond)
+ timer.schedule(
+ new PeriodicTask(handle, pnDcpTimer),
+ 5000,
+ 15000);
}
+ }
- @Override
- public void run() {
- try {
- handle.loop(10, listener);
- } catch (InterruptedException e) {
- logger.error("Got error handling raw socket", e);
- Thread.currentThread().interrupt();
- } catch (PcapNativeException | NotOpenException e) {
- logger.error("Got error handling raw socket", e);
- }
+
+
+ public void startLldpPoll() {
+ for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
+ PcapHandle handle = entry.getValue();
+ MacAddress macAddress = entry.getKey();
+
+ Function<Object, Boolean> lldpTimer =
+ message -> {
+ // Construct and send the LLDP Probe
+ TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
+ new TlvProfibusSubTypePortStatus(0x00)
+ );
+
+ TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
+ new TlvProfibusSubTypeChassisMac(macAddress)
+ );
+
+ TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
+ (short) 0x01,
+ (short) 0x03,
+ 0x0020,
+ 0x0010
+ );
+
+ Ethernet_Frame identificationRequest = null;
+ try {
+ identificationRequest = new Ethernet_Frame(
+ // Pre-Defined LLDP discovery MAC address
+ new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
+ macAddress,
+ new Ethernet_FramePayload_LLDP(
+ new Lldp_Pdu(
+ Arrays.asList(
+ new TlvChassisId(
+ PLC4X_LLDP_IDENTIFIER.length() + 1,
+ (short) 7,
+ PLC4X_LLDP_IDENTIFIER
+ ),
+ new TlvPortId(
+ PLC4X_LLDP_PORT.length() + 1,
+ (short) 7,
+ PLC4X_LLDP_PORT
+ ),
+ new TlvTimeToLive(2, 20),
+ new TlvOrganizationSpecific(
+ portStatus.getLengthInBytes(),
+ portStatus
+ ),
+ new TlvOrganizationSpecific(
+ chassisMac.getLengthInBytes(),
+ chassisMac
+ ),
+ new TlvOrganizationSpecific(
+ ieee.getLengthInBytes(),
+ ieee
+ ),
+ new TlvManagementAddress(
+ 12,
+ ManagementAddressSubType.IPV4,
+ new IpAddress(Hex.decodeHex("c0a85a6e")),
+ (short) 0x03,
+ 0x01L,
+ (short) 0x00
+ ),
+ new EndOfLldp(0)
+ )
+ )));
+ } catch (DecoderException e) {
+ throw new RuntimeException(e);
+ }
+ WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
+ try {
+ identificationRequest.serialize(buffer);
+ } catch (SerializationException e) {
+ throw new RuntimeException(e);
+ }
+ Packet packet = null;
+ try {
+ packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
+ } catch (IllegalRawDataException e) {
+ throw new RuntimeException(e);
+ }
+ try {
+ handle.sendPacket(packet);
+ } catch (PcapNativeException e) {
+ throw new RuntimeException(e);
+ } catch (NotOpenException e) {
+ throw new RuntimeException(e);
+ }
+ return null;
+ };
+ Timer timer = new Timer();
+ periodicTimers.add(timer);
+
+ // Schedule to run after every 3 second(3000 millisecond)
+ timer.scheduleAtFixedRate(
+ new PeriodicTask(handle, lldpTimer),
+ 5000,
+ 5000);
}
}
- private static class LLDPTask extends TimerTask {
+ private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
+ byte[] address = pcap4jMacAddress.getAddress();
+ return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
+ }
+
+ private static class PeriodicTask extends TimerTask {
- private final Logger logger = LoggerFactory.getLogger(Task.class);
+ private final Logger logger = LoggerFactory.getLogger(PeriodicTask.class);
private final PcapHandle handle;
private final Function<Object, Boolean> operator;
- public LLDPTask(PcapHandle handle, Function<Object, Boolean> operator) {
+ public PeriodicTask(PcapHandle handle, Function<Object, Boolean> operator) {
this.handle = handle;
this.operator = operator;
}
@@ -507,12 +526,12 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
public void run() {
operator.apply(null);
}
+
}
public static void main(String[] args) throws Exception {
ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer();
- //discoverer.discover(null);
- discoverer.lldpProbe();
+ discoverer.discover(null);
Thread.sleep(10000);
}
[plc4x] 10/13: fix(plc4j/profinet): Identified that the Application Ready request comes from the device.
Posted by hu...@apache.org.
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 ]
[plc4x] 02/13: fix(plc4j(profinet): Connections are working for my setup
Posted by hu...@apache.org.
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 b55c5d22550f970cbd34a73fdf0c3292ad674ecd
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Sep 8 07:03:32 2022 -0600
fix(plc4j(profinet): Connections are working for my setup
---
.../profinet/protocol/ProfinetProtocolLogic.java | 46 ++++++----------------
.../apache/plc4x/java/profinet/ProfinetPoc.java | 2 +-
.../resources/protocols/profinet/profinet.mspec | 2 +-
3 files changed, 13 insertions(+), 37 deletions(-)
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 62b209c10..836732954 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
@@ -40,10 +40,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.*;
import java.time.Duration;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Optional;
-import java.util.UUID;
+import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
@@ -133,6 +130,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
connectRequestPacket.setAddress(remoteAddress.getAddress());
connectRequestPacket.setPort(remoteAddress.getPort());
// Send it.
+
udpSocket.send(connectRequestPacket);
// Receive the response.
@@ -221,14 +219,14 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
new DceRpc_InterfaceUuid_DeviceInterface(),
profinetDriverContext.getDceRpcActivityUuid(),
0, 0, DceRpc_Operation.CONNECT,
- new PnIoCm_Packet_Req(404, 404, 404, 0, 404,
+ 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")),
profinetDriverContext.getSessionKey(),
profinetDriverContext.getLocalMacAddress(),
new Uuid(Hex.decodeHex("dea000006c9711d1827100640008002a")),
- false, false, false,
+ false, true, false,
false, PnIoCm_CompanionArType.SINGLE_AR, false,
true, false, PnIoCm_State.ACTIVE,
600,
@@ -249,31 +247,23 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
Arrays.asList(
new PnIoCm_IoDataObject(0, 0x0001, 0),
new PnIoCm_IoDataObject(0, 0x8000, 1),
- new PnIoCm_IoDataObject(0, 0x8001, 2),
- new PnIoCm_IoDataObject(0, 0x8002, 3),
- new PnIoCm_IoDataObject(1, 0x0001, 4)
+ new PnIoCm_IoDataObject(0, 0x8001, 2)
),
- Collections.singletonList(
- new PnIoCm_IoCs(0x0001, 0x0001, 0x0019)
- ))
+ 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,
- 0x8000, 128, 8, 1, 0, 0xffffffff,
+ 0xFFFF, 128, 8, 1, 0, 0xffffffff,
3, 3, 0xC000,
new MacAddress(Hex.decodeHex("000000000000")),
Collections.singletonList(
new PnIoCm_IoCrBlockReqApi(
- Collections.singletonList(
- new PnIoCm_IoDataObject(0x0001, 0x0001, 0x0005)
- ),
+ 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_IoCs(0, 0x8002, 3),
- new PnIoCm_IoCs(1, 0x0001, 4)
+ new PnIoCm_IoCs(0, 0x8001, 2)
)
)
)
@@ -287,29 +277,15 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
0x00000001, false, false,
false, false),
new PnIoCm_Submodule_NoInputNoOutputData(0x8000,
- 0x00000002, false, false,
+ 0x00008000, false, false,
false, false),
new PnIoCm_Submodule_NoInputNoOutputData(0x8001,
- 0x00000003, false, false,
- false, false),
- new PnIoCm_Submodule_NoInputNoOutputData(0x8002,
- 0x00000003, false, false,
+ 0x00008001, false, false,
false, false)
)
)
)
),
- new PnIoCm_Block_ExpectedSubmoduleReq((short) 1, (short) 0,
- Collections.singletonList(
- new PnIoCm_ExpectedSubmoduleBlockReqApi(1,
- 0x00000022, 0x00000000, Collections.singletonList(
- new PnIoCm_Submodule_InputAndOutputData(0x0001, 0x00000010,
- false, false, false,
- false, 20, (short) 1, (short) 1,
- 6, (short) 1, (short) 1))
- )
- )
- ),
new PnIoCm_Block_AlarmCrReq((short) 1, (short) 0,
PnIoCm_AlarmCrType.ALARM_CR, 0x8892, false, false, 1, 3,
0x0000, 200, 0xC000, 0xA000)
diff --git a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetPoc.java b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetPoc.java
index 401258f04..90ccefbc3 100644
--- a/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetPoc.java
+++ b/plc4j/drivers/profinet/src/test/java/org/apache/plc4x/java/profinet/ProfinetPoc.java
@@ -40,7 +40,7 @@ public class ProfinetPoc {
new DceRpc_ObjectUuid((byte) 0x00, 0x0001, 0x0904, 0x002A),
new DceRpc_InterfaceUuid_DeviceInterface(), generateActivityUuid(),
0, 0, DceRpc_Operation.CONNECT,
- new PnIoCm_Packet_Req(404, 404, 404, 0, 404,
+ new PnIoCm_Packet_Req(404, 404, 0, 404,
Arrays.asList(
new PnIoCm_Block_ArReq((short) 1, (short) 0, PnIoCm_ArType.IO_CONTROLLER,
new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51")), 2,
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index b6d05a84a..35bdaf24c 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -651,7 +651,7 @@
[typeSwitch packetType
['REQUEST' PnIoCm_Packet_Req
[simple uint 32 argsMaximum ]
- [simple uint 32 argsLength ]
+ [implicit uint 32 argsLength 'lengthInBytes - 20']
[simple uint 32 arrayMaximumCount ]
[simple uint 32 arrayOffset ]
[simple uint 32 arrayActualCount ]
[plc4x] 13/13: fix(plc4j/profinet): Continued to split device logic out.
Posted by hu...@apache.org.
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 e99d97c6b474c50ac653ca942a2b7219f0ebfcc2
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Oct 6 07:52:35 2022 -0600
fix(plc4j/profinet): Continued to split device logic out.
---
.../java/profinet/device/ProfinetCallable.java | 25 +-
.../plc4x/java/profinet/device/ProfinetDevice.java | 136 +++++++++-
.../profinet/device/ProfinetMessageWrapper.java | 27 +-
.../profinet/protocol/ProfinetProtocolLogic.java | 294 +--------------------
.../HelloPlc4xDiscoverAndBrowse.java | 35 +--
.../src/main/resources/logback.xml | 12 +-
6 files changed, 199 insertions(+), 330 deletions(-)
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
index fd117c9f4..1cde0c154 100644
--- 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
@@ -1,10 +1,29 @@
+/*
+ * 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.plc4x.java.api.exceptions.PlcException;
import org.apache.plc4x.java.profinet.readwrite.DceRpc_Packet;
-public interface ProfinetCallable {
- void handle(DceRpc_Packet packet) throws PlcException;
+public interface ProfinetCallable<T> {
+ void handle(T packet) throws PlcException;
- DceRpc_Packet create() throws PlcException;
+ T 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
index 80f10d961..d2bd0887e 100644
--- 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
@@ -61,6 +61,15 @@ public class ProfinetDevice {
private AtomicInteger sessionKeyGenerator = new AtomicInteger(1);
+ private static final Uuid ARUUID;
+ static {
+ try {
+ ARUUID = new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51"));
+ } catch (DecoderException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private void closeUDPSocket() {
// Handle the closing of the connection, might need to send some messages beforehand.
@@ -70,7 +79,7 @@ public class ProfinetDevice {
}
}
- private boolean createUDPSocket() {
+ private boolean createUdpSocket() {
if (state != ProfinetDeviceState.IDLE) {
closeUDPSocket();
}
@@ -82,6 +91,7 @@ public class ProfinetDevice {
rawSocketChannel = (RawSocketChannel) channel;
+
// Create an udp socket
try {
udpSocket = new DatagramSocket();
@@ -94,16 +104,28 @@ public class ProfinetDevice {
}
public boolean onConnect() {
- if (!createUDPSocket()) {
+ if (!createUdpSocket()) {
// Unable to create UDP connection
return false;
}
- ProfinetMessageWrapper.sendMessage(
+ ProfinetMessageWrapper.sendUdpMessage(
new CreateConnection(),
this
);
+ ProfinetMessageWrapper.sendUdpMessage(
+ new WriteParameters(),
+ this
+ );
+
+ ProfinetMessageWrapper.sendUdpMessage(
+ new WriteParametersEnd(),
+ this
+ );
+
+
+
return false;
}
@@ -171,6 +193,10 @@ public class ProfinetDevice {
return this.udpSocket;
}
+ public RawSocketChannel getRawSocket() {
+ return this.rawSocketChannel;
+ }
+
public InetAddress getIpAddress() throws UnknownHostException {
return InetAddress.getByName(this.ipAddress);
}
@@ -179,7 +205,7 @@ public class ProfinetDevice {
return DEFAULT_UDP_PORT;
}
- public class CreateConnection implements ProfinetCallable {
+ public class CreateConnection implements ProfinetCallable<DceRpc_Packet> {
public DceRpc_Packet create() throws PlcException {
try {
@@ -298,4 +324,106 @@ public class ProfinetDevice {
}
}
}
+
+ public class WriteParameters implements ProfinetCallable<DceRpc_Packet> {
+ public DceRpc_Packet create() {
+ 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(),
+ uuid,
+ 0, 1, DceRpc_Operation.WRITE,
+ new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+ Arrays.asList(
+ new IODWriteRequestHeader(
+ (short) 1,
+ (short) 0,
+ 0,
+ ARUUID,
+ 0x00000000,
+ 0x0000,
+ 0x0000,
+ 0xe040,
+ 180
+ ),
+ new IODWriteRequestHeader(
+ (short) 1,
+ (short) 0,
+ 1,
+ ARUUID,
+ 0x00000000,
+ 0x0000,
+ 0x8000,
+ 0x8071,
+ 12
+ ),
+ new PDInterfaceAdjust(
+ (short) 1,
+ (short) 0,
+ MultipleInterfaceModeNameOfDevice.NAME_PROVIDED_BY_LLDP
+ )
+ ))
+ );
+ }
+
+ @Override
+ public void handle(DceRpc_Packet packet) throws PlcException {
+ logger.debug("Received a Write Parameter Response");
+ }
+ }
+
+ public class WriteParametersEnd implements ProfinetCallable<DceRpc_Packet> {
+ public DceRpc_Packet create() {
+ 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(),
+ uuid,
+ 0, 1, DceRpc_Operation.CONTROL,
+ new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+ Arrays.asList(
+ new PnIoCm_Control_Request(
+ (short) 1,
+ (short) 0,
+ ARUUID,
+ 0x0001,
+ 0x0001
+ )
+ ))
+ );
+ }
+
+ @Override
+ public void handle(DceRpc_Packet packet) throws PlcException {
+ logger.debug("Received a Write Parameter End Response");
+ }
+ }
+
+ public class CyclicData implements ProfinetCallable<Ethernet_Frame> {
+ public Ethernet_Frame create() {
+ return new Ethernet_Frame(
+ macAddress,
+ macAddress,
+ 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 void handle(Ethernet_Frame packet) throws PlcException {
+ logger.debug("Received a Write Parameter End Response");
+ }
+ }
+
+
}
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
index bee0ae8d3..e56b92d61 100644
--- 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
@@ -1,5 +1,25 @@
+/*
+ * 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.plc4x.java.api.exceptions.PlcException;
import org.apache.plc4x.java.profinet.readwrite.*;
import org.apache.plc4x.java.spi.generation.ParseException;
@@ -13,7 +33,7 @@ import java.net.DatagramPacket;
public class ProfinetMessageWrapper {
- public static void sendMessage(ProfinetCallable callable, ProfinetDevice context) throws RuntimeException {
+ public static void sendUdpMessage(ProfinetCallable<DceRpc_Packet> callable, ProfinetDevice context) throws RuntimeException {
try {
DceRpc_Packet packet = callable.create();
// Serialize it to a byte-payload
@@ -43,6 +63,9 @@ public class ProfinetMessageWrapper {
} 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 fe931765c..8d31d198f 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
@@ -70,15 +70,6 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
private ProfinetConfiguration configuration;
- private static final Uuid ARUUID;
-
- static {
- try {
- ARUUID = new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51"));
- } catch (DecoderException e) {
- throw new RuntimeException(e);
- }
- }
@Override
public void setConfiguration(ProfinetConfiguration configuration) {
@@ -153,110 +144,8 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
device.getValue().onConnect();
}
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // Create the connection request.
- try {
- // Create the packet
- final DceRpc_Packet profinetConnectionRequest = createProfinetConnectionRequest();
- // 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");
- }
-
- // Create the packet
- final DceRpc_Packet profinetAdvancedConnectionWriteRequest = createProfinetAdvancedConnectionWriteRequest();
- // Serialize it to a byte-payload
- writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionWriteRequest.getLengthInBytes());
- profinetAdvancedConnectionWriteRequest.serialize(writeBuffer);
- // Create a udp packet.
- connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
- connectRequestPacket.setAddress(remoteAddress.getAddress());
- connectRequestPacket.setPort(remoteAddress.getPort());
- // Send it.
-
- udpSocket.send(connectRequestPacket);
-
- // Receive the response.
- resultBuffer = new byte[profinetAdvancedConnectionWriteRequest.getLengthInBytes()];
- connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
- udpSocket.receive(connectResponsePacket);
-
-
- // Create the packet
- final DceRpc_Packet profinetAdvancedConnectionParameterEnd = createProfinetAdvancedConnectionParameterEnd();
- // Serialize it to a byte-payload
- writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionParameterEnd.getLengthInBytes());
- profinetAdvancedConnectionParameterEnd.serialize(writeBuffer);
- // Create a udp packet.
- connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
- connectRequestPacket.setAddress(remoteAddress.getAddress());
- connectRequestPacket.setPort(remoteAddress.getPort());
- // Send it.
-
- udpSocket.send(connectRequestPacket);
-
- // Receive the response.
- resultBuffer = new byte[profinetAdvancedConnectionParameterEnd.getLengthInBytes()];
- connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
- udpSocket.receive(connectResponsePacket);
-
- // Create the packet
- final DceRpc_Packet profinetAdvancedConnectionApplicationReady = createProfinetAdvancedConnectionApplicationReady();
- // Serialize it to a byte-payload
- writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionApplicationReady.getLengthInBytes());
- profinetAdvancedConnectionApplicationReady.serialize(writeBuffer);
- // Create a udp packet.
- connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
- connectRequestPacket.setAddress(remoteAddress.getAddress());
- connectRequestPacket.setPort(remoteAddress.getPort());
- // Send it.
-
- udpSocket.send(connectRequestPacket);
-
- // Receive the response.
- 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);
- }
+ context.fireConnected();
+ connected = true;
}
@Override
@@ -390,188 +279,9 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> imp
return Optional.empty();
}
- private DceRpc_Packet createProfinetConnectionRequest() 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(),
- profinetDriverContext.getDceRpcActivityUuid(),
- 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")),
- profinetDriverContext.getSessionKey(),
- profinetDriverContext.getLocalMacAddress(),
- 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);
- }
- }
-
- private DceRpc_Packet createProfinetAdvancedConnectionWriteRequest() throws PlcException {
-
- 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(),
- profinetDriverContext.getDceRpcActivityUuid(),
- 0, 1, DceRpc_Operation.WRITE,
- new PnIoCm_Packet_Req(16696, 16696, 0, 244,
- Arrays.asList(
- new IODWriteRequestHeader(
- (short) 1,
- (short) 0,
- 0,
- ARUUID,
- 0x00000000,
- 0x0000,
- 0x0000,
- 0xe040,
- 180
- ),
- new IODWriteRequestHeader(
- (short) 1,
- (short) 0,
- 1,
- ARUUID,
- 0x00000000,
- 0x0000,
- 0x8000,
- 0x8071,
- 12
- ),
- new PDInterfaceAdjust(
- (short) 1,
- (short) 0,
- MultipleInterfaceModeNameOfDevice.NAME_PROVIDED_BY_LLDP
- )
- ))
- );
- }
-
- private DceRpc_Packet createProfinetAdvancedConnectionParameterEnd() throws PlcException {
-
- 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(),
- profinetDriverContext.getDceRpcActivityUuid(),
- 0, 1, DceRpc_Operation.CONTROL,
- new PnIoCm_Packet_Req(16696, 16696, 0, 244,
- Arrays.asList(
- new PnIoCm_Control_Request(
- (short) 1,
- (short) 0,
- ARUUID,
- 0x0001,
- 0x0001
- )
- ))
- );
- }
- private DceRpc_Packet createProfinetAdvancedConnectionApplicationReady() throws PlcException {
-
- 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(),
- profinetDriverContext.getDceRpcActivityUuid(),
- 0, 1, DceRpc_Operation.CONTROL,
- new PnIoCm_Packet_Req(16696, 16696, 0, 244,
- Arrays.asList(
- new PnIoCM_Block_Request(
- (short) 1,
- (short) 0,
- ARUUID,
- 0x0001,
- 0x0002,
- 0x0000
- )
- ))
- );
- }
-
}
diff --git a/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4xDiscoverAndBrowse.java b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4xDiscoverAndBrowse.java
index 9b8418dfc..e1b863745 100644
--- a/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4xDiscoverAndBrowse.java
+++ b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4xDiscoverAndBrowse.java
@@ -35,33 +35,22 @@ public class HelloPlc4xDiscoverAndBrowse {
public static void main(String[] args) throws Exception {
// Iterate over all installed drivers and execute their browse functionality (If they support it)
PlcDriverManager driverManager = new PlcDriverManager();
- for (String protocolCode : driverManager.listDrivers()) {
- PlcDriver driver = driverManager.getDriver(protocolCode);
- if (driver.getMetadata().canDiscover()) {
- logger.info("Performing discovery for {} protocol", driver.getProtocolName());
-
- PlcDiscoveryRequest discoveryRequest = driver.discoveryRequestBuilder().build();
-
- discoveryRequest.executeWithHandler(discoveryItem -> {
- logger.info(" - Found device with connection-url {}", discoveryItem.getConnectionUrl());
- try (PlcConnection connection = driverManager.getConnection(discoveryItem.getConnectionUrl())) {
- if (connection.getMetadata().canBrowse()) {
- PlcBrowseRequest browseRequest = connection.browseRequestBuilder().build();
- browseRequest.execute().whenComplete((browseResponse, throwable) -> {
- if (throwable != null) {
- throwable.printStackTrace();
- } else {
- for (PlcBrowseItem value : browseResponse.getValues()) {
- outputBrowseItem(value, 0);
- }
- }
- });
+ //try (PlcConnection connection = driverManager.getConnection("opcua:tcp://missy-nuc:53530/plc4x")) {
+ try (PlcConnection connection = driverManager.getConnection("profinet:raw://192.168.90.128")) {
+ if (connection.getMetadata().canBrowse()) {
+ PlcBrowseRequest browseRequest = connection.browseRequestBuilder().build();
+ browseRequest.execute().whenComplete((browseResponse, throwable) -> {
+ if (throwable != null) {
+ throwable.printStackTrace();
+ } else {
+ for (PlcBrowseItem value : browseResponse.getValues()) {
+ outputBrowseItem(value, 0);
}
- } catch (Exception e) {
- throw new RuntimeException(e);
}
});
}
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
}
diff --git a/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml
index 2641def3a..f64d08114 100644
--- a/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml
+++ b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml
@@ -28,13 +28,13 @@
</appender>
<!-- Remove the messages we're not interested in -->
- <logger name="io.netty.channel" level="ERROR"/>
- <logger name="org.pcap4j" level="WARN"/>
- <logger name="org.apache.plc4x.java.modbus.tcp.discovery" level="WARN"/>
- <logger name="org.apache.plc4x.java.transport" level="WARN"/>
- <logger name="org.apache.plc4x.java.spi" level="WARN"/>
+ <logger name="io.netty.channel" level="TRACE"/>
+ <logger name="org.pcap4j" level="TRACE"/>
+ <logger name="org.apache.plc4x.java.modbus.tcp.discovery" level="TRACE"/>
+ <logger name="org.apache.plc4x.java.transport" level="TRACE"/>
+ <logger name="org.apache.plc4x.java.spi" level="TRACE"/>
- <root level="INFO">
+ <root level="TRACE">
<appender-ref ref="STDOUT" />
</root>
[plc4x] 01/13: fix(plc4j/profinet): Fix to remove spaces from the generated connection string.
Posted by hu...@apache.org.
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 afeb1a491b2b11a4326b23cfe48af0ac1be7c042
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 4 09:14:24 2022 -0600
fix(plc4j/profinet): Fix to remove spaces from the generated connection string.
---
.../apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 5701f7c9c..5eb899bcc 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
@@ -145,13 +145,13 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
String deviceTypeName = "unknown";
if (blocks.containsKey(DEVICE_TYPE_NAME)) {
PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
- deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
+ deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "_");
}
String deviceName = "unknown";
if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
- deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
+ deviceName = new String(block.getNameOfStation()).replace(" ", "_");
}
String role = "unknown";
[plc4x] 05/13: fix(plc4j(profinet): Continued to add the LLDP broadcast
Posted by hu...@apache.org.
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 bb01815f87977da5b37885a88dd3e963ae47a195
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Sep 8 16:53:28 2022 -0600
fix(plc4j(profinet): Continued to add the LLDP broadcast
---
.../resources/protocols/profinet/profinet.mspec | 85 +++++++++++++++++++---
1 file changed, 73 insertions(+), 12 deletions(-)
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index b3c0d9ca0..19dcdcfee 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -89,22 +89,64 @@
]
[type Lldp_Pdu
- [simple TlvType_Pdu tlvChassisId]
- [simple TlvType_Pdu tlvPortId]
- [simple TlvTimeToLive tlvTimeToLive]
+ [array LldpUnit lldpParameters]
]
-[type TlvType_Pdu
- [simple uint 7 tlvId ]
- [implicit uint 9 tlvIdLength 'lengthInBytes' ]
- [simple typeSubType idSubType ]
- [simple vstring '(tlvIdLength * 8) + 1' Id ]
+[discriminatedType LldpUnit
+ [disciminator uint 7 tlvId ]
+ [implicit uint 9 tlvIdLength 'lengthInBytes' ] ]
+ [typeSwitch idSubType
+ ['END_OF_LLDP' EndOfLldp
+ [
+ ['CHASSIS_ID' TlvChassisId
+ [simple uint 8 chassisIdSubType ]
+ [simple vstring '(tlvIdLength * 8) + 1' chassisId ]
+ ]
+ ['PORT_ID' TlvPortId
+ [simple uint 8 portIdSubType ]
+ [simple vstring '(tlvIdLength * 8) + 1' portId ]
+ ]
+ ['PORT_ID' TlvPortId
+ [simple uint 16 tlvTimeToLive ]
+ ]
+ ['MANAGEMENT_ADDRESS' TlvManagementAddress
+ [implicit uint 8 addressStringLength ]
+ [simple ManagementAddressSubType addressSubType ]
+ [simple IpAddress ipAddress ]
+ [simple uint 8 interfaceSubType ]
+ [simple uint 32 interfaceNumber ]
+ [simple uint 8 oidStringLength ]
+ ]
+ ['ORGANIZATION_SPECIFIC' TlvOrganizationSpecific
+ [simple TlvOrganizationSpecificUnit organizationSpecificUnit ]
+ ]
+ ]
+[
+
+[type TlvOrganizationSpecificUnit(uint 9 unitLength)
+ [discriminator uint 24 uniqueCode]
+ [typeSwitch uniqueCode
+ ['0x000ECF' TlvOrgSpecificProfibus
+ [simple TlvOrgSpecificProfibusUnit specificUnit ]
+ ]
+ [´0x00120F' TlvOrgSpecificIeee8023
+ [simple uint 8 subType ]
+ [simple uint 8 negotiationSupport ]
+ [simple uint 16 negotiationCapability ]
+ [simple uint 16 operationalMauType ]
+ ]
+ ]
+]
+
+[discriminatedType TlvOrgSpecificProfibusUnit
+ [discriminator TlvProfibusSubType subType]
+ [typeSwitch subType
+ ]
]
-[type TlvTimeToLive
- [simple uint 7 tlvId ]
- [implicit uint 9 tlvIdLength 'lengthInBytes' ]
- [simple uint 16 tlvTimeToLive]
+[enum TlvProfibusSubType
+ ['0x02' PORT_STATUS]
+ [´0x05' CHASSIS_MAC]
]
// 4.10.3.2
@@ -259,6 +301,25 @@
['0x0A' CANCEL_ACKNOWLEDGE ]
]
+//LLDP Specific
+[enum uint 7 TlvType
+ ['0x00' END_OF_LLDP ]
+ ['0x01' CHASSIS_ID ]
+ ['0x02' PORT_ID ]
+ ['0x03' TIME_TO_LIVE ]
+ ['0x04' PORT_DESCRIPTION ]
+ ['0x05' SYSTEM_NAME ]
+ ['0x06' SYSTEM_DESCRIPTION ]
+ ['0x07' SYSTEM_CAPABILITIES ]
+ ['0x08' MANAGEMENT_ADDRESS ]
+ ['0xFF' ORGANIZATION_SPECIFIC]
+]
+
+[enum uint 8 ManagementAddressSubType
+ ['0x00' UNKNOWN ]
+ ['0x01' IPV4 ]
+]
+
// 4.10.3.2.14
[enum uint 16 DceRpc_Operation
['0x0000' CONNECT ]
[plc4x] 12/13: chore(plc4j/profinet): escape the device name and type within the connection string
Posted by hu...@apache.org.
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 36b3260cc1d99b869b8302bd3094300f28dc40f6
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 11 11:15:59 2022 -0600
chore(plc4j/profinet): escape the device name and type within the connection string
---
.../profinet/discovery/ProfinetPlcDiscoverer.java | 610 +++++++--------------
1 file changed, 192 insertions(+), 418 deletions(-)
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 f0a1a3a43..5701f7c9c 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
@@ -18,8 +18,7 @@
*/
package org.apache.plc4x.java.profinet.discovery;
-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.api.messages.PlcDiscoveryItemHandler;
import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
@@ -48,30 +47,20 @@ import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.function.Function;
public class ProfinetPlcDiscoverer implements PlcDiscoverer {
private static final EtherType PN_EtherType = EtherType.getInstance((short) 0x8892);
- private static final EtherType LLDP_EtherType = EtherType.getInstance((short) 0x88cc);
// The constants for the different block names and their actual meaning.
private static final String DEVICE_TYPE_NAME = "DEVICE_PROPERTIES_OPTION-1";
private static final String DEVICE_NAME_OF_STATION = "DEVICE_PROPERTIES_OPTION-2";
- private static final String PLC4X_LLDP_IDENTIFIER = "PLC4X PROFINET Controller Client";
- private static final String PLC4X_LLDP_PORT = "port001.plc4x";
private static final String DEVICE_ID = "DEVICE_PROPERTIES_OPTION-3";
private static final String DEVICE_ROLE = "DEVICE_PROPERTIES_OPTION-4";
private static final String DEVICE_OPTIONS = "DEVICE_PROPERTIES_OPTION-5";
private static final String DEVICE_INSTANCE = "DEVICE_PROPERTIES_OPTION-7";
private static final String IP_OPTION_IP = "IP_OPTION-2";
- ExecutorService pool = Executors.newSingleThreadExecutor();
- Map<MacAddress, PcapHandle> openHandles = new HashMap<>();
- List<PlcDiscoveryItem> values = new ArrayList<>();
-
- Set<Timer> periodicTimers = new HashSet<>();
-
private final Logger logger = LoggerFactory.getLogger(ProfinetPlcDiscoverer.class);
@Override
@@ -79,49 +68,200 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
return discoverWithHandler(discoveryRequest, null);
}
- public void openDiscoverHandles() {
+ public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
+ CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
+ Set<PcapHandle> openHandles = new HashSet<>();
+ List<PlcDiscoveryItem> values = new ArrayList<>();
try {
for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
// It turned out on some MAC network devices without any ip addresses
// the compiling of the filter expression was causing errors. As
// currently there was no other way to detect this, this check seems
// to be sufficient.
- if (dev.getAddresses().size() == 0) {
+ if(dev.getAddresses().size() == 0) {
continue;
}
if (!dev.isLoopBack()) {
for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
- openHandles.put(toPlc4xMacAddress(macAddress), handle);
+ openHandles.add(handle);
+
+ ExecutorService pool = Executors.newSingleThreadExecutor();
- // Only react on PROFINET DCP or LLDP packets targeted at our current MAC address.
+ // Only react on PROFINET DCP packets targeted at our current MAC address.
handle.setFilter(
- "(((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")) or (ether proto 0x88cc)",
+ "((ether proto 0x8100) or (ether proto 0x8892)) and (ether dst " + Pcaps.toBpfString(macAddress) + ")",
BpfProgram.BpfCompileMode.OPTIMIZE);
+
+ PacketListener listener =
+ packet -> {
+ // EthernetPacket is the highest level of abstraction we can be expecting.
+ // Everything inside this we will have to decode ourselves.
+ if (packet instanceof EthernetPacket) {
+ EthernetPacket ethernetPacket = (EthernetPacket) packet;
+ boolean isPnPacket = false;
+ // I have observed some times the ethernet packets being wrapped inside a VLAN
+ // Packet, in this case we simply unpack the content.
+ if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
+ Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
+ if (PN_EtherType.equals(vlanPacket.getHeader().getType())) {
+ isPnPacket = true;
+ }
+ } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType())) {
+ isPnPacket = true;
+ }
+
+ // It's a PROFINET packet.
+ if (isPnPacket) {
+ ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
+ try {
+ Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
+ PnDcp_Pdu pdu;
+ // Access the pdu data (either directly or by
+ // unpacking the content of the VLAN packet.
+ if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
+ Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
+ pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
+ } else {
+ pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
+ }
+ // Inspect the PDU itself
+ // (in this case we only process identify response packets)
+ if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
+ PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
+
+ Map<String, PnDcp_Block> blocks = new HashMap<>();
+ for (PnDcp_Block block : identifyResPDU.getBlocks()) {
+ String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
+ blocks.put(blockName, block);
+ }
+
+ // The mac address of the device we found
+ org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
+ // The mac address of the local network device
+ org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
+
+ String deviceTypeName = "unknown";
+ if (blocks.containsKey(DEVICE_TYPE_NAME)) {
+ PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
+ deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
+ }
+
+ String deviceName = "unknown";
+ if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
+ PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
+ deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
+ }
+
+ String role = "unknown";
+ if (blocks.containsKey(DEVICE_ROLE)) {
+ role = "";
+ PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
+ if (block.getPnioSupervisor()) {
+ role += ",SUPERVISOR";
+ }
+ if (block.getPnioMultidevive()) {
+ role += ",MULTIDEVICE";
+ }
+ if (block.getPnioController()) {
+ role += ",CONTROLLER";
+ }
+ if (block.getPnioDevice()) {
+ role += ",DEVICE";
+ }
+ // Cut off the first comma
+ if (role.length() > 0) {
+ role = role.substring(1);
+ } else {
+ role = "unknown";
+ }
+ }
+
+ String remoteIpAddress = "unknown";
+ String remoteSubnetMask = "unknown";
+ if (blocks.containsKey(IP_OPTION_IP)) {
+ PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
+ try {
+ InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
+ remoteIpAddress = addr.getHostAddress();
+ InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
+ remoteSubnetMask = netMask.getHostAddress();
+ } catch (UnknownHostException e) {
+ remoteIpAddress = "invalid";
+ }
+ }
+
+ // Get the Vendor Id and the Device Id
+ String vendorId = "unknown";
+ String deviceId = "unknown";
+ if (blocks.containsKey(DEVICE_ID)) {
+ PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
+ vendorId = String.format("%04X", block.getVendorId());
+ deviceId = String.format("%04X", block.getDeviceId());
+ }
+
+ Map<String, String> options = new HashMap<>();
+ options.put("remoteIpAddress", remoteIpAddress);
+ options.put("remoteSubnetMask", remoteSubnetMask);
+ options.put("remoteMacAddress", srcAddr.toString());
+ options.put("localMacAddress", dstAddr.toString());
+ options.put("deviceTypeName", deviceTypeName);
+ options.put("deviceName", deviceName);
+ options.put("vendorId", vendorId);
+ options.put("deviceId", deviceId);
+ options.put("role", role);
+ String name = deviceTypeName + " - " + deviceName;
+ PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
+ ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
+ remoteIpAddress, options, name, Collections.emptyMap());
+ values.add(value);
+
+ // If we have a discovery handler, pass it to the handler callback
+ if (handler != null) {
+ handler.handle(value);
+ }
+
+ logger.debug("Found new device: '{}' with connection-url '{}'",
+ value.getName(), value.getConnectionUrl());
+ }
+ } catch (ParseException e) {
+ logger.error("Got error decoding packet", e);
+ }
+ }
+ }
+ };
+ Task t = new Task(handle, listener);
+ pool.execute(t);
+
+ // Construct and send the search request.
+ Ethernet_Frame identificationRequest = new Ethernet_Frame(
+ // Pre-Defined PROFINET discovery MAC address
+ new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
+ toPlc4xMacAddress(macAddress),
+ new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
+ new Ethernet_FramePayload_PnDcp(
+ new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
+ 1,
+ 256,
+ Collections.singletonList(
+ new PnDcp_Block_ALLSelector()
+ )))));
+ WriteBufferByteBased buffer = new WriteBufferByteBased(34);
+ identificationRequest.serialize(buffer);
+ Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
+ handle.sendPacket(packet);
}
}
}
- } catch (NotOpenException | PcapNativeException e) {
+ } catch (IllegalRawDataException | NotOpenException | PcapNativeException | SerializationException e) {
logger.error("Got an exception while processing raw socket data", e);
- for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
- PcapHandle openHandle = entry.getValue();
- try {
- openHandle.breakLoop();
- openHandle.close();
- } catch (NotOpenException error) {
- logger.info("Handle already closed.");
- }
- }
- for (Timer timer : periodicTimers) {
- timer.cancel();
- timer.purge();
+ future.completeExceptionally(new PlcException("Got an internal error while performing discovery"));
+ for (PcapHandle openHandle : openHandles) {
+ openHandle.close();
}
+ return future;
}
- }
-
- public CompletableFuture<PlcDiscoveryResponse> setDiscoveryEndTimer(PlcDiscoveryRequest discoveryRequest, long delay) {
- CompletableFuture<PlcDiscoveryResponse> future = new CompletableFuture<>();
// Create a timer that completes the future after a given time with all the responses it found till then.
Timer timer = new Timer("Discovery Timeout");
@@ -129,387 +269,14 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
public void run() {
PlcDiscoveryResponse response =
new DefaultPlcDiscoveryResponse(discoveryRequest, PlcResponseCode.OK, values);
- for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
- PcapHandle openHandle = entry.getValue();
- try {
- openHandle.breakLoop();
- openHandle.close();
- } catch (Exception e) {
- logger.error("Error occurred while closing handle");
- }
-
- }
- for (Timer timer : periodicTimers) {
- timer.cancel();
- timer.purge();
- }
future.complete(response);
- }
- }, delay);
-
- return future;
- }
-
- public PacketListener createListener(PcapHandle handle, PlcDiscoveryItemHandler handler) {
- PacketListener listener =
- packet -> {
- // EthernetPacket is the highest level of abstraction we can be expecting.
- // Everything inside this we will have to decode ourselves.
- if (packet instanceof EthernetPacket) {
- EthernetPacket ethernetPacket = (EthernetPacket) packet;
- boolean isPnPacket = false;
- // I have observed sometimes the ethernet packets being wrapped inside a VLAN
- // Packet, in this case we simply unpack the content.
- if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
- Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
- if (PN_EtherType.equals(vlanPacket.getHeader().getType()) || LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
- isPnPacket = true;
- }
- } else if (PN_EtherType.equals(ethernetPacket.getHeader().getType()) || LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
- isPnPacket = true;
- }
-
- // It's a PROFINET or LLDP packet.
- if (isPnPacket) {
- ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
- try {
- Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
-
- // Access the pdu data (either directly or by
- // unpacking the content of the VLAN packet.
- if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
- Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
- if (vlefpl.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
- PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
- processPnDcp(pdu, ethernetPacket, handler);
- } else if (vlefpl.getPayload() instanceof Ethernet_FramePayload_LLDP) {
- Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) vlefpl.getPayload()).getPdu();
- processLldp(pdu, ethernetPacket, handler);
- }
- } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_PnDcp) {
- PnDcp_Pdu pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
- processPnDcp(pdu, ethernetPacket, handler);
- } else if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_LLDP) {
- Lldp_Pdu pdu = ((Ethernet_FramePayload_LLDP) ethernetFrame.getPayload()).getPdu();
- processLldp(pdu, ethernetPacket, handler);
- }
-
- } catch (ParseException e) {
- logger.error("Got error decoding packet", e);
- }
- }
- }
- };
- return listener;
- }
-
- public CompletableFuture<PlcDiscoveryResponse> discoverWithHandler(PlcDiscoveryRequest discoveryRequest, PlcDiscoveryItemHandler handler) {
- openDiscoverHandles();
- startListener(handler);
- 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)
- if (pdu instanceof PnDcp_Pdu_IdentifyRes) {
- PnDcp_Pdu_IdentifyRes identifyResPDU = (PnDcp_Pdu_IdentifyRes) pdu;
-
- Map<String, PnDcp_Block> blocks = new HashMap<>();
- for (PnDcp_Block block : identifyResPDU.getBlocks()) {
- String blockName = block.getOption().name() + "-" + block.getSuboption().toString();
- blocks.put(blockName, block);
- }
-
- // The mac address of the device we found
- org.pcap4j.util.MacAddress srcAddr = ethernetPacket.getHeader().getSrcAddr();
- // The mac address of the local network device
- org.pcap4j.util.MacAddress dstAddr = ethernetPacket.getHeader().getDstAddr();
-
- String deviceTypeName = "unknown";
- if (blocks.containsKey(DEVICE_TYPE_NAME)) {
- PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
- deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
- }
-
- String deviceName = "unknown";
- if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
- PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
- deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
- }
-
- String role = "unknown";
- if (blocks.containsKey(DEVICE_ROLE)) {
- role = "";
- PnDcp_Block_DevicePropertiesDeviceRole block = (PnDcp_Block_DevicePropertiesDeviceRole) blocks.get(DEVICE_ROLE);
- if (block.getPnioSupervisor()) {
- role += ",SUPERVISOR";
- }
- if (block.getPnioMultidevive()) {
- role += ",MULTIDEVICE";
- }
- if (block.getPnioController()) {
- role += ",CONTROLLER";
- }
- if (block.getPnioDevice()) {
- role += ",DEVICE";
- }
- // Cut off the first comma
- if (role.length() > 0) {
- role = role.substring(1);
- } else {
- role = "unknown";
- }
- }
-
- String remoteIpAddress = "unknown";
- String remoteSubnetMask = "unknown";
- if (blocks.containsKey(IP_OPTION_IP)) {
- PnDcp_Block_IpParameter block = (PnDcp_Block_IpParameter) blocks.get(IP_OPTION_IP);
- try {
- InetAddress addr = InetAddress.getByAddress(block.getIpAddress());
- remoteIpAddress = addr.getHostAddress();
- InetAddress netMask = InetAddress.getByAddress(block.getSubnetMask());
- remoteSubnetMask = netMask.getHostAddress();
- } catch (UnknownHostException e) {
- remoteIpAddress = "invalid";
+ for (PcapHandle openHandle : openHandles) {
+ openHandle.close();
}
}
+ }, 5000L);
- // Get the Vendor Id and the Device Id
- String vendorId = "unknown";
- String deviceId = "unknown";
- if (blocks.containsKey(DEVICE_ID)) {
- PnDcp_Block_DevicePropertiesDeviceId block = (PnDcp_Block_DevicePropertiesDeviceId) blocks.get(DEVICE_ID);
- vendorId = String.format("%04X", block.getVendorId());
- deviceId = String.format("%04X", block.getDeviceId());
- }
-
- Map<String, String> options = new HashMap<>();
- options.put("remoteIpAddress", remoteIpAddress);
- options.put("remoteSubnetMask", remoteSubnetMask);
- options.put("remoteMacAddress", srcAddr.toString());
- options.put("localMacAddress", dstAddr.toString());
- options.put("deviceTypeName", deviceTypeName);
- options.put("deviceName", deviceName);
- options.put("vendorId", vendorId);
- options.put("deviceId", deviceId);
- options.put("role", role);
- String name = deviceTypeName + " - " + deviceName;
- PlcDiscoveryItem value = new DefaultPlcDiscoveryItem(
- ProfinetDriver.DRIVER_CODE, RawSocketTransport.TRANSPORT_CODE,
- remoteIpAddress, options, name, Collections.emptyMap());
- values.add(value);
-
- // If we have a discovery handler, pass it to the handler callback
- if (handler != null) {
- handler.handle(value);
- }
-
- logger.debug("Found new device: '{}' with connection-url '{}'",
- value.getName(), value.getConnectionUrl());
- }
- }
-
- private void processLldp(Lldp_Pdu pdu, EthernetPacket ethernetPacket, PlcDiscoveryItemHandler handler) {
- logger.debug("Found new lldp device: '' with connection-url ''");
- }
-
- public void startPnDcpPoll(long period) {
- for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
- PcapHandle handle = entry.getValue();
- MacAddress macAddress = entry.getKey();
- // Construct and send the search request.
-
- Function<Object, Boolean> pnDcpTimer =
- message -> {
- Ethernet_Frame identificationRequest = new Ethernet_Frame(
- // Pre-Defined PROFINET discovery MAC address
- new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
- macAddress,
- new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
- new Ethernet_FramePayload_PnDcp(
- new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
- 1,
- 256,
- Collections.singletonList(
- new PnDcp_Block_ALLSelector()
- )))));
- WriteBufferByteBased buffer = new WriteBufferByteBased(34);
- try {
- identificationRequest.serialize(buffer);
- } catch (SerializationException e) {
- throw new RuntimeException(e);
- }
- Packet packet = null;
- try {
- packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
- } catch (IllegalRawDataException e) {
- throw new RuntimeException(e);
- }
- try {
- handle.sendPacket(packet);
- } catch (PcapNativeException e) {
- throw new RuntimeException(e);
- } catch (NotOpenException e) {
- throw new RuntimeException(e);
- }
- return null;
- };
-
- Timer timer = new Timer();
- periodicTimers.add(timer);
-
- // Schedule to run after every 3 second(3000 millisecond)
- timer.scheduleAtFixedRate(
- new PeriodicTask(handle, pnDcpTimer),
- 0,
- period);
- }
- }
-
- public void startListener(PlcDiscoveryItemHandler handler) {
- for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
- PcapHandle handle = entry.getValue();
- MacAddress macAddress = entry.getKey();
- // Construct and send the search request.
-
- Function<Object, Boolean> pnDcpTimer =
- message -> {
- PacketListener listener = createListener(handle, handler);
- try {
- handle.loop(-1, listener);
- } catch (InterruptedException e) {
- logger.error("Got error handling raw socket", e);
- Thread.currentThread().interrupt();
- } catch (PcapNativeException | NotOpenException e) {
- logger.error("Got error handling raw socket", e);
- }
- return null;
- };
-
- Timer timer = new Timer();
- periodicTimers.add(timer);
-
- // Schedule to run after every 3 second(3000 millisecond)
- timer.schedule(
- new PeriodicTask(handle, pnDcpTimer),
- 5000,
- 15000);
- }
- }
-
-
-
- public void startLldpPoll(long period) {
- for (Map.Entry<MacAddress, PcapHandle> entry : openHandles.entrySet()) {
- PcapHandle handle = entry.getValue();
- MacAddress macAddress = entry.getKey();
-
- Function<Object, Boolean> lldpTimer =
- message -> {
- // Construct and send the LLDP Probe
- TlvOrgSpecificProfibus portStatus = new TlvOrgSpecificProfibus(
- new TlvProfibusSubTypePortStatus(0x00)
- );
-
- TlvOrgSpecificProfibus chassisMac = new TlvOrgSpecificProfibus(
- new TlvProfibusSubTypeChassisMac(macAddress)
- );
-
- TlvOrgSpecificIeee8023 ieee = new TlvOrgSpecificIeee8023(
- (short) 0x01,
- (short) 0x03,
- 0x0020,
- 0x0010
- );
-
- Ethernet_Frame identificationRequest = null;
- try {
- identificationRequest = new Ethernet_Frame(
- // Pre-Defined LLDP discovery MAC address
- new MacAddress(new byte[]{0x01, (byte) 0x80, (byte) 0xc2, 0x00, 0x00, 0x0e}),
- macAddress,
- new Ethernet_FramePayload_LLDP(
- new Lldp_Pdu(
- Arrays.asList(
- new TlvChassisId(
- PLC4X_LLDP_IDENTIFIER.length() + 1,
- (short) 7,
- PLC4X_LLDP_IDENTIFIER
- ),
- new TlvPortId(
- PLC4X_LLDP_PORT.length() + 1,
- (short) 7,
- PLC4X_LLDP_PORT
- ),
- new TlvTimeToLive(2, 20),
- new TlvOrganizationSpecific(
- portStatus.getLengthInBytes(),
- portStatus
- ),
- new TlvOrganizationSpecific(
- chassisMac.getLengthInBytes(),
- chassisMac
- ),
- new TlvOrganizationSpecific(
- ieee.getLengthInBytes(),
- ieee
- ),
- new TlvManagementAddress(
- 12,
- ManagementAddressSubType.IPV4,
- new IpAddress(Hex.decodeHex("c0a85a6e")),
- (short) 0x03,
- 0x01L,
- (short) 0x00
- ),
- new EndOfLldp(0)
- )
- )));
- } catch (DecoderException e) {
- throw new RuntimeException(e);
- }
- WriteBufferByteBased buffer = new WriteBufferByteBased(identificationRequest.getLengthInBytes());
- try {
- identificationRequest.serialize(buffer);
- } catch (SerializationException e) {
- throw new RuntimeException(e);
- }
- Packet packet = null;
- try {
- packet = EthernetPacket.newPacket(buffer.getData(), 0, identificationRequest.getLengthInBytes());
- } catch (IllegalRawDataException e) {
- throw new RuntimeException(e);
- }
- try {
- handle.sendPacket(packet);
- } catch (PcapNativeException e) {
- throw new RuntimeException(e);
- } catch (NotOpenException e) {
- throw new RuntimeException(e);
- }
- return null;
- };
- Timer timer = new Timer();
- periodicTimers.add(timer);
-
- // Schedule to run after every 3 second(3000 millisecond)
- timer.scheduleAtFixedRate(
- new PeriodicTask(handle, lldpTimer),
- 0,
- period);
- }
+ return future;
}
private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
@@ -517,28 +284,35 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
}
- private static class PeriodicTask extends TimerTask {
+ private static class Task implements Runnable {
- private final Logger logger = LoggerFactory.getLogger(PeriodicTask.class);
+ private final Logger logger = LoggerFactory.getLogger(Task.class);
private final PcapHandle handle;
- private final Function<Object, Boolean> operator;
+ private final PacketListener listener;
- public PeriodicTask(PcapHandle handle, Function<Object, Boolean> operator) {
+ public Task(PcapHandle handle, PacketListener listener) {
this.handle = handle;
- this.operator = operator;
+ this.listener = listener;
}
@Override
public void run() {
- operator.apply(null);
+ try {
+ handle.loop(10, listener);
+ } catch (InterruptedException e) {
+ logger.error("Got error handling raw socket", e);
+ Thread.currentThread().interrupt();
+ } catch (PcapNativeException | NotOpenException e) {
+ logger.error("Got error handling raw socket", e);
+ }
}
-
}
public static void main(String[] args) throws Exception {
ProfinetPlcDiscoverer discoverer = new ProfinetPlcDiscoverer();
discoverer.discover(null);
+
Thread.sleep(10000);
}
[plc4x] 03/13: fix(plc4j(profinet): Add Dummy Advanced Connection Write Request
Posted by hu...@apache.org.
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 bf77f0d958314681986ed25ff5e134b8ae086f75
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Sep 8 10:43:35 2022 -0600
fix(plc4j(profinet): Add Dummy Advanced Connection Write Request
---
.../profinet/protocol/ProfinetProtocolLogic.java | 97 ++++++++++++++++++++--
.../resources/protocols/profinet/profinet.mspec | 56 +++++++++++--
2 files changed, 142 insertions(+), 11 deletions(-)
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 836732954..8e4e666ab 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
@@ -54,6 +54,16 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
private ProfinetDriverContext profinetDriverContext;
+ private static final Uuid ARUUID;
+
+ static {
+ try {
+ ARUUID = new Uuid(Hex.decodeHex("654519352df3b6428f874371217c2b51"));
+ } catch (DecoderException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@Override
public void setContext(ConversationContext<Ethernet_Frame> context) {
super.setContext(context);
@@ -160,11 +170,23 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
} else {
throw new PlcException("Unexpected response");
}
+
+ // Create the packet
+ final DceRpc_Packet profinetAdvancedConnectionWriteRequest = createProfinetAdvancedConnectionWriteRequest();
+ // Serialize it to a byte-payload
+ writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionWriteRequest.getLengthInBytes());
+ profinetAdvancedConnectionWriteRequest.serialize(writeBuffer);
+ // Create a udp packet.
+ connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
+ connectRequestPacket.setAddress(remoteAddress.getAddress());
+ connectRequestPacket.setPort(remoteAddress.getPort());
+ // Send it.
+
+ udpSocket.send(connectRequestPacket);
+
} catch (SerializationException | IOException | PlcException | ParseException e) {
logger.error("Error", e);
}
-
- //System.out.println(rawSocketChannel);
}
@Override
@@ -233,14 +255,14 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
// 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.
- "profinetxadriver4933"),
+ "controller"),
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,
- 3, 3, 0xC000,
+ 50, 50, 0xC000,
new org.apache.plc4x.java.profinet.readwrite.MacAddress(Hex.decodeHex("000000000000")),
Collections.singletonList(
new PnIoCm_IoCrBlockReqApi(
@@ -255,7 +277,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
0x0002, 0x8892, false, false,
false, false, PnIoCm_RtClass.RT_CLASS_2, 40,
0xFFFF, 128, 8, 1, 0, 0xffffffff,
- 3, 3, 0xC000,
+ 50, 50, 0xC000,
new MacAddress(Hex.decodeHex("000000000000")),
Collections.singletonList(
new PnIoCm_IoCrBlockReqApi(
@@ -303,6 +325,71 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
}
}
+ private DceRpc_Packet createProfinetAdvancedConnectionWriteRequest() throws PlcException {
+
+ 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(),
+ profinetDriverContext.getDceRpcActivityUuid(),
+ 0, 1, DceRpc_Operation.WRITE,
+ new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+ Arrays.asList(
+ new IODWriteRequestHeader(
+ (short) 1,
+ (short) 0,
+ 0,
+ ARUUID,
+ 0x00000000,
+ 0x0000,
+ 0x0000,
+ 0xe040,
+ 180
+ ),
+ new IODWriteRequestHeader(
+ (short) 1,
+ (short) 0,
+ 1,
+ ARUUID,
+ 0x00000000,
+ 0x0000,
+ 0x8000,
+ 0x8071,
+ 12
+ ),
+ new PDInterfaceAdjust(
+ (short) 1,
+ (short) 0,
+ MultipleInterfaceModeNameOfDevice.PORT_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("controller")
+ )
+ )
+ ))
+ );
+ }
+
protected static DceRpc_ActivityUuid generateActivityUuid() {
UUID number = UUID.randomUUID();
try {
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 35bdaf24c..09bfea4a5 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -681,6 +681,34 @@
[simple uint 8 blockVersionHigh ]
[simple uint 8 blockVersionLow ]
[typeSwitch blockType
+ ['IOD_WRITE_REQUEST_HEADER' IODWriteRequestHeader
+ [simple uint 16 sequenceNumber ]
+ [simple Uuid arUuid ]
+ [simple uint 32 api ]
+ [simple uint 16 slotNumber ]
+ [simple uint 16 subSlotNumber ]
+ [const uint 16 padField 0x0000 ]
+ [simple uint 16 index ]
+ [simple uint 32 recordDataLength ]
+ [padding uint 8 pad '0x00' '64 - 6 - 2 - 16 - 4 - 2 - 2 - 2 - 2 - 4']
+ ]
+ ['PD_INTERFACE_ADJUST' PDInterfaceAdjust
+ [const uint 16 padField 0x0000 ]
+ [const uint 16 multipleInterfaceModeReserved2 0x0000 ]
+ [const uint 15 multipleInterfaceModeReserved1 0x0000 ]
+ [simple MultipleInterfaceModeNameOfDevice multipleInterfaceModeNameOfDevice ]
+ ]
+ ['PD_PORT_DATA_CHECK' PDPortDataCheck
+ [const uint 16 padField 0x0000 ]
+ [simple uint 16 slotNumber ]
+ [simple uint 16 subSlotNumber ]
+ [simple PnIoCm_Block checkPeers ]
+ ]
+ ['CHECK_PEERS' CheckPeers
+ [const uint 8 noOfPeers 0x01 ]
+ [simple PascalString peerPortId ]
+ [simple PascalString peerChassisId ]
+ ]
['AR_BLOCK_REQ' PnIoCm_Block_ArReq
[simple PnIoCm_ArType arType ]
[simple Uuid arUuid ]
@@ -773,13 +801,18 @@
[array PnIoCm_ModuleDiffBlockApi apis count 'numberOfApis' ]
]
['AR_SERVER_BLOCK' PnIoCm_Block_ArServer
- //[implicit uint 16 stationNameLength 'STR_LEN(cmInitiatorStationName)']
- //[simple vstring 'stationNameLength * 8' cmInitiatorStationName ]
- //[padding byte 0x00 ]
+ [simple PascalString stationName ]
+ [padding uint 8 pad '0x00' '20 - 6 - (stationName.stringLength)' ]
]
]
]
+[type PascalString
+ [implicit int 16 sLength 'stringValue.length == 0 ? -1 : stringValue.length']
+ [simple vstring 'sLength == -1 ? 0 : sLength * 8' stringValue]
+ [virtual int 16 stringLength 'stringValue.length == -1 ? 0 : stringValue.length']
+]
+
[type PnIoCm_IoCrBlockReqApi
[const uint 32 api 0x00000000 ]
[implicit uint 16 numIoDataObjects 'COUNT(ioDataObjects)' ]
@@ -872,15 +905,21 @@
]
[enum uint 16 PnIoCm_BlockType
+ ['0x0008' IOD_WRITE_REQUEST_HEADER ]
['0x0101' AR_BLOCK_REQ ]
- ['0x8101' AR_BLOCK_RES ]
['0x0102' IO_CR_BLOCK_REQ ]
- ['0x8102' IO_CR_BLOCK_RES ]
['0x0103' ALARM_CR_BLOCK_REQ ]
- ['0x8103' ALARM_CR_BLOCK_RES ]
['0x0104' EXPECTED_SUBMODULE_BLOCK_REQ]
+ ['0x0110' IOD_CONTROL_REQ ]
+ ['0x0200' PD_PORT_DATA_CHECK ]
+ ['0x020a' CHECK_PEERS ]
+ ['0x0250' PD_INTERFACE_ADJUST ]
+ ['0x8101' AR_BLOCK_RES ]
+ ['0x8102' IO_CR_BLOCK_RES ]
+ ['0x8103' ALARM_CR_BLOCK_RES ]
['0x8104' MODULE_DIFF_BLOCK ]
['0x8106' AR_SERVER_BLOCK ]
+ ['0x8110' IOD_CONTROL_RES ]
]
[enum uint 16 PnIoCm_ArType
@@ -917,6 +956,11 @@
['0x3' INPUT_AND_OUTPUT_DATA]
]
+[enum bit MultipleInterfaceModeNameOfDevice
+ ['false' PORT_PROVIDED_BY_LLDP]
+ ['true' NAME_PROVIDED_BY_LLDP]
+]
+
[enum uint 16 PnIoCm_DescriptionType
['0x0001' INPUT]
]
[plc4x] 08/13: feat(plc4j/profinet): Escape spaces using %20
Posted by hu...@apache.org.
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 6f5b27b2b878bea0a716950d1d6c17745defd685
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 11 13:39:31 2022 -0600
feat(plc4j/profinet): Escape spaces using %20
---
.../apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 f4066d098..4486e1abc 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
@@ -152,13 +152,13 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
String deviceTypeName = "unknown";
if (blocks.containsKey(DEVICE_TYPE_NAME)) {
PnDcp_Block_DevicePropertiesDeviceVendor block = (PnDcp_Block_DevicePropertiesDeviceVendor) blocks.get(DEVICE_TYPE_NAME);
- deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "_");
+ deviceTypeName = new String(block.getDeviceVendorValue()).replace(" ", "%20");
}
String deviceName = "unknown";
if (blocks.containsKey(DEVICE_NAME_OF_STATION)) {
PnDcp_Block_DevicePropertiesNameOfStation block = (PnDcp_Block_DevicePropertiesNameOfStation) blocks.get(DEVICE_NAME_OF_STATION);
- deviceName = new String(block.getNameOfStation()).replace(" ", "_");
+ deviceName = new String(block.getNameOfStation()).replace(" ", "%20");
}
String role = "unknown";
[plc4x] 07/13: feat(plc4j/profinet): Fixed a few minor issues and added the Application Ready packet
Posted by hu...@apache.org.
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 dcdad3137d4a5b2a42e15af6fd45dfe6c491dcc8
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Sun Sep 11 13:38:47 2022 -0600
feat(plc4j/profinet): Fixed a few minor issues and added the Application Ready packet
---
.../apache/plc4x/java/profinet/ProfinetDriver.java | 7 ++-
.../profinet/discovery/ProfinetPlcDiscoverer.java | 2 +-
.../profinet/protocol/ProfinetProtocolLogic.java | 51 +++++++++++++++++++++-
.../resources/protocols/profinet/profinet.mspec | 44 +++++++++++++------
4 files changed, 88 insertions(+), 16 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 1fec4f741..05d96d8b4 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
@@ -99,11 +99,16 @@ public class ProfinetDriver extends GeneratedDriverBase<Ethernet_Frame> {
@Override
protected boolean canRead() {
- return true;
+ return false;
}
@Override
protected boolean canWrite() {
+ return false;
+ }
+
+ @Override
+ protected boolean canSubscribe() {
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 35681f795..f4066d098 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
@@ -410,7 +410,7 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
new TlvManagementAddress(
12,
ManagementAddressSubType.IPV4,
- new IpAddress(Hex.decodeHex("c0a8006e")),
+ new IpAddress(Hex.decodeHex("c0a85a6e")),
(short) 0x03,
0x01L,
(short) 0x00
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 d3c1d1024..3801c1dfa 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
@@ -208,6 +208,23 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
udpSocket.receive(connectResponsePacket);
+ // Create the packet
+ final DceRpc_Packet profinetAdvancedConnectionApplicationReady = createProfinetAdvancedConnectionApplicationReady();
+ // Serialize it to a byte-payload
+ writeBuffer = new WriteBufferByteBased(profinetAdvancedConnectionApplicationReady.getLengthInBytes());
+ profinetAdvancedConnectionApplicationReady.serialize(writeBuffer);
+ // Create a udp packet.
+ connectRequestPacket = new DatagramPacket(writeBuffer.getData(), writeBuffer.getData().length);
+ connectRequestPacket.setAddress(remoteAddress.getAddress());
+ connectRequestPacket.setPort(remoteAddress.getPort());
+ // Send it.
+
+ udpSocket.send(connectRequestPacket);
+
+ // Receive the response.
+ resultBuffer = new byte[profinetAdvancedConnectionApplicationReady.getLengthInBytes()];
+ connectResponsePacket = new DatagramPacket(resultBuffer, resultBuffer.length);
+ udpSocket.receive(connectResponsePacket);
} catch (SerializationException | IOException | PlcException | ParseException e) {
logger.error("Error", e);
@@ -233,6 +250,13 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
return future;
}
+ @Override
+ public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest subscriptionRequest) {
+ CompletableFuture<PlcSubscriptionResponse> future = new CompletableFuture<>();
+ future.completeExceptionally(new NotImplementedException());
+ return future;
+ }
+
@Override
protected void decode(ConversationContext<Ethernet_Frame> context, Ethernet_Frame msg) throws Exception {
super.decode(context, msg);
@@ -386,7 +410,7 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
new PDInterfaceAdjust(
(short) 1,
(short) 0,
- MultipleInterfaceModeNameOfDevice.PORT_PROVIDED_BY_LLDP
+ MultipleInterfaceModeNameOfDevice.NAME_PROVIDED_BY_LLDP
),
new IODWriteRequestHeader(
(short) 1,
@@ -437,6 +461,31 @@ public class ProfinetProtocolLogic extends Plc4xProtocolBase<Ethernet_Frame> {
);
}
+
+
+ private DceRpc_Packet createProfinetAdvancedConnectionApplicationReady() throws PlcException {
+
+ 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(),
+ profinetDriverContext.getDceRpcActivityUuid(),
+ 0, 1, DceRpc_Operation.CONTROL,
+ new PnIoCm_Packet_Req(16696, 16696, 0, 244,
+ Arrays.asList(
+ new PnIoCM_Block_Request(
+ (short) 1,
+ (short) 0,
+ ARUUID,
+ 0x0001,
+ 0x0002,
+ 0x0000
+ )
+ ))
+ );
+ }
+
protected static DceRpc_ActivityUuid generateActivityUuid() {
UUID number = UUID.randomUUID();
try {
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 57f3ced3a..2cabb832f 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -823,27 +823,43 @@
[simple vstring 'stationNameLength * 8' cmInitiatorStationName ]
]
['AR_BLOCK_RES' PnIoCm_Block_ArRes
- [simple PnIoCm_ArType arType ]
- [simple Uuid arUuid ]
- [simple uint 16 sessionKey ]
- [simple MacAddress cmResponderMacAddr ]
- [simple uint 16 responderUDPRTPort ]
+ [simple PnIoCm_ArType arType ]
+ [simple Uuid arUuid ]
+ [simple uint 16 sessionKey ]
+ [simple MacAddress cmResponderMacAddr ]
+ [simple uint 16 responderUDPRTPort ]
]
['IOD_CONTROL_REQ' PnIoCm_Control_Request
- [reserved uint 16 '0x0000' ]
+ [reserved uint 16 '0x0000' ]
[simple Uuid arUuid ]
[simple uint 16 sessionKey ]
- [reserved uint 16 '0x0000' ]
+ [reserved uint 16 '0x0000' ]
[simple uint 16 controlCommand ]
- [reserved uint 16 '0x0000' ]
+ [reserved uint 16 '0x0000' ]
+ ]
+ ['IOX_BLOCK_REQ' PnIoCM_Block_Request
+ [reserved uint 16 '0x0000' ]
+ [simple Uuid arUuid ]
+ [simple uint 16 sessionKey ]
+ [reserved uint 16 '0x0000' ]
+ [simple uint 16 controlCommand ]
+ [simple uint 16 controlBlockProperties ]
+ ]
+ ['IOX_BLOCK_RES' PnIoCM_Block_Response
+ [reserved uint 16 '0x0000' ]
+ [simple Uuid arUuid ]
+ [simple uint 16 sessionKey ]
+ [reserved uint 16 '0x0000' ]
+ [simple uint 16 controlCommand ]
+ [simple uint 16 controlBlockProperties ]
]
['IOD_CONTROL_RES' PnIoCm_Control_Response
- [reserved uint 16 '0x0000' ]
+ [reserved uint 16 '0x0000' ]
[simple Uuid arUuid ]
[simple uint 16 sessionKey ]
- [reserved uint 16 '0x0000' ]
+ [reserved uint 16 '0x0000' ]
[simple uint 16 controlCommand ]
- [reserved uint 16 '0x0000' ]
+ [reserved uint 16 '0x0000' ]
]
['IO_CR_BLOCK_REQ' PnIoCm_Block_IoCrReq
[simple PnIoCm_IoCrType ioCrType ]
@@ -913,9 +929,9 @@
]
[type PascalString
- [implicit int 16 sLength 'stringValue.length == 0 ? -1 : stringValue.length']
+ [implicit int 8 sLength 'stringValue.length == 0 ? -1 : stringValue.length']
[simple vstring 'sLength == -1 ? 0 : sLength * 8' stringValue]
- [virtual int 16 stringLength 'stringValue.length == -1 ? 0 : stringValue.length']
+ [virtual int 8 stringLength 'stringValue.length == -1 ? 0 : stringValue.length']
]
[type PnIoCm_IoCrBlockReqApi
@@ -1016,6 +1032,7 @@
['0x0103' ALARM_CR_BLOCK_REQ ]
['0x0104' EXPECTED_SUBMODULE_BLOCK_REQ]
['0x0110' IOD_CONTROL_REQ ]
+ ['0x0112' IOX_BLOCK_REQ ]
['0x0200' PD_PORT_DATA_CHECK ]
['0x020a' CHECK_PEERS ]
['0x0250' PD_INTERFACE_ADJUST ]
@@ -1025,6 +1042,7 @@
['0x8104' MODULE_DIFF_BLOCK ]
['0x8106' AR_SERVER_BLOCK ]
['0x8110' IOD_CONTROL_RES ]
+ ['0x8112' IOX_BLOCK_RES ]
]
[enum uint 16 PnIoCm_ArType
[plc4x] 04/13: fix(plc4j(profinet): Started to add the LLDP broadcast
Posted by hu...@apache.org.
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 83becf0591431f76d0b34d3ede14fd4f64db83b2
Author: Ben Hutcheson <be...@gmail.com>
AuthorDate: Thu Sep 8 12:49:59 2022 -0600
fix(plc4j(profinet): Started to add the LLDP broadcast
---
.../profinet/discovery/ProfinetPlcDiscoverer.java | 102 +++++++++++++++++++++
.../resources/protocols/profinet/profinet.mspec | 22 +++++
2 files changed, 124 insertions(+)
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 5eb899bcc..96b2dba67 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
@@ -51,6 +51,7 @@ import java.util.concurrent.Executors;
public class ProfinetPlcDiscoverer implements PlcDiscoverer {
private static final EtherType PN_EtherType = EtherType.getInstance((short) 0x8892);
+ private static final EtherType LLDP_EtherType = EtherType.getInstance((short) 0x88cc);
// The constants for the different block names and their actual meaning.
private static final String DEVICE_TYPE_NAME = "DEVICE_PROPERTIES_OPTION-1";
@@ -279,6 +280,107 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
return future;
}
+ public void lldpProbe() {
+ Set<PcapHandle> openHandles = new HashSet<>();
+ try {
+ for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
+ // It turned out on some MAC network devices without any ip addresses
+ // the compiling of the filter expression was causing errors. As
+ // currently there was no other way to detect this, this check seems
+ // to be sufficient.
+ if(dev.getAddresses().size() == 0) {
+ continue;
+ }
+ if (!dev.isLoopBack()) {
+ for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
+ org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
+ PcapHandle handle = dev.openLive(65536, PcapNetworkInterface.PromiscuousMode.PROMISCUOUS, 10);
+ openHandles.add(handle);
+
+ ExecutorService pool = Executors.newSingleThreadExecutor();
+
+ // Only react on PROFINET DCP packets targeted at our current MAC address.
+ handle.setFilter(
+ "(ether proto 0x88cc)",
+ BpfProgram.BpfCompileMode.OPTIMIZE);
+
+ PacketListener listener =
+ packet -> {
+ // EthernetPacket is the highest level of abstraction we can be expecting.
+ // Everything inside this we will have to decode ourselves.
+ if (packet instanceof EthernetPacket) {
+ EthernetPacket ethernetPacket = (EthernetPacket) packet;
+ boolean isLldpPacket = false;
+ // I have observed sometimes the ethernet packets being wrapped inside a VLAN
+ // Packet, in this case we simply unpack the content.
+ if (ethernetPacket.getPayload() instanceof Dot1qVlanTagPacket) {
+ Dot1qVlanTagPacket vlanPacket = (Dot1qVlanTagPacket) ethernetPacket.getPayload();
+ if (LLDP_EtherType.equals(vlanPacket.getHeader().getType())) {
+ isLldpPacket = true;
+ }
+ } else if (LLDP_EtherType.equals(ethernetPacket.getHeader().getType())) {
+ isLldpPacket = true;
+ }
+
+ // It's a LLDP packet.
+ if (isLldpPacket) {
+ ReadBuffer reader = new ReadBufferByteBased(ethernetPacket.getRawData());
+ try {
+ Ethernet_Frame ethernetFrame = Ethernet_Frame.staticParse(reader);
+ PnDcp_Pdu pdu;
+ // Access the pdu data (either directly or by
+ // unpacking the content of the VLAN packet.
+ if (ethernetFrame.getPayload() instanceof Ethernet_FramePayload_VirtualLan) {
+ Ethernet_FramePayload_VirtualLan vlefpl = (Ethernet_FramePayload_VirtualLan) ethernetFrame.getPayload();
+ pdu = ((Ethernet_FramePayload_PnDcp) vlefpl.getPayload()).getPdu();
+ } else {
+ pdu = ((Ethernet_FramePayload_PnDcp) ethernetFrame.getPayload()).getPdu();
+ }
+ // Inspect the PDU itself
+ // (in this case we only process identify response packets)
+
+
+ logger.debug("Found new device: '{}' using LLDP '{}'",
+ "Not Sure", "Not Sure");
+ } catch (ParseException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+ };
+ Task t = new Task(handle, listener);
+ pool.execute(t);
+
+ // Construct and send the LLDP Probe
+
+ Ethernet_Frame identificationRequest = new Ethernet_Frame(
+ // Pre-Defined PROFINET discovery MAC address
+ new MacAddress(new byte[]{0x01, 0x0E, (byte) 0xCF, 0x00, 0x00, 0x00}),
+ toPlc4xMacAddress(macAddress),
+ new Ethernet_FramePayload_VirtualLan(VirtualLanPriority.BEST_EFFORT, false, 0,
+ new Ethernet_FramePayload_PnDcp(
+ new PnDcp_Pdu_IdentifyReq(PnDcp_FrameId.DCP_Identify_ReqPDU.getValue(),
+ 1,
+ 256,
+ Collections.singletonList(
+ new PnDcp_Block_ALLSelector()
+ )))));
+ WriteBufferByteBased buffer = new WriteBufferByteBased(34);
+ identificationRequest.serialize(buffer);
+ Packet packet = EthernetPacket.newPacket(buffer.getData(), 0, 34);
+ handle.sendPacket(packet);
+ }
+ }
+ }
+ } catch (IllegalRawDataException | NotOpenException | PcapNativeException | SerializationException e) {
+ logger.error("Got an exception while processing raw socket data", e);
+
+ for (PcapHandle openHandle : openHandles) {
+ openHandle.close();
+ }
+ }
+ }
+
private static MacAddress toPlc4xMacAddress(org.pcap4j.util.MacAddress pcap4jMacAddress) {
byte[] address = pcap4jMacAddress.getAddress();
return new MacAddress(new byte[]{address[0], address[1], address[2], address[3], address[4], address[5]});
diff --git a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
index 09bfea4a5..b3c0d9ca0 100644
--- a/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
+++ b/protocols/profinet/src/main/resources/protocols/profinet/profinet.mspec
@@ -82,9 +82,31 @@
['0x8892' Ethernet_FramePayload_PnDcp
[simple PnDcp_Pdu pdu ]
]
+ ['0x88cc' Ethernet_FramePayload_LLDP
+ [simple Lldp_Pdu pdu ]
+ ]
]
]
+[type Lldp_Pdu
+ [simple TlvType_Pdu tlvChassisId]
+ [simple TlvType_Pdu tlvPortId]
+ [simple TlvTimeToLive tlvTimeToLive]
+]
+
+[type TlvType_Pdu
+ [simple uint 7 tlvId ]
+ [implicit uint 9 tlvIdLength 'lengthInBytes' ]
+ [simple typeSubType idSubType ]
+ [simple vstring '(tlvIdLength * 8) + 1' Id ]
+]
+
+[type TlvTimeToLive
+ [simple uint 7 tlvId ]
+ [implicit uint 9 tlvIdLength 'lengthInBytes' ]
+ [simple uint 16 tlvTimeToLive]
+]
+
// 4.10.3.2
// A lot of the fields are set to constant values, which would
// usually be dynamic. However are we trying to limit the number of