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