Hi all, I'm currently working on the PROFINET ... here I have the challenge, that I have multiple layers of different protocols.
UDP -> DCE/RPC -> PROFINET IO CM (Block Container) -> PROFINET IO CM Blocks The problem is: * UDP is Big Endian * PROFINET IO CM Blocks are Big Endian * DCE/RPC is big or little endian depending on a variable inside the DCE/RPC packet * Profinet IO CM (block container) is the same encoding as the DCE/RPC packet At first I thought if I force my request to big endian, then the response would be accordingly. Unfortunately that's not the case ... So if I send a request with Big Endian DCE/RPC (Hence, makeing the entire message Big endian). I still get a response back with DCE/RPC in Little Endian. So I have to be able to handle both. Now I had multiple ideas: * A new sort of block "endianessSwitch" (or a better name) ... which takes an argument and which simply switches the endianess of the buffers to the given one. This would probably be pretty simple to implement with a "try-finally" block in Java. * We add an optional additional option to the complex-type declarations. This would however have us split types into two. Here comes the example of the PN DCE/RPC packet with an endianessSwitch: [discriminatedType 'DceRpc_Packet' [const uint 8 'version' '0x04' ] [discriminator DceRpc_PacketType 'packetType' ] [typeSwitch 'packetType' ['REQUEST' DceRpc_Packet_Req [reserved uint 1 '0x0' ] [const uint 1 'broadcast' '0' ] [const uint 1 'idempotent' '1' ] [const uint 1 'maybe' '0' ] [const uint 1 'noFragmentAcknowledgeRequested' '0' ] [const uint 1 'fragment' '0' ] [const uint 1 'lastFragment' '0' ] [reserved uint 1 '0x0' ] ] ['RESPONSE' DceRpc_Packet_Res [reserved uint 1 '0x0' ] [const uint 1 'broadcast' '0' ] [const uint 1 'idempotent' '0' ] [const uint 1 'maybe' '0' ] [const uint 1 'noFragmentAcknowledgeRequested' '1' ] [const uint 1 'fragment' '0' ] [const uint 1 'lastFragment' '1' ] [reserved uint 1 '0x0' ] ] ['REJECT' DceRpc_Packet_Rej [reserved uint 1 '0x0' ] [const uint 1 'broadcast' '0' ] [const uint 1 'idempotent' '0' ] [const uint 1 'maybe' '0' ] [const uint 1 'noFragmentAcknowledgeRequested' '0' ] [const uint 1 'fragment' '0' ] [const uint 1 'lastFragment' '0' ] [reserved uint 1 '0x0' ] ] ] [reserved uint 6 '0x00' ] [const uint 1 'cancelWasPending' '0' ] [reserved uint 1 '0x0' ] [simple IntegerEncoding 'integerEncoding' ] [simple CharacterEncoding 'characterEncoding' ] [simple FloatingPointEncoding 'floatingPointEncoding' ] [endianessSwitch 'integerEncoding == IntegerEncoding.BIG_ENDIAN' [const uint 8 'serialHigh' '0x00' ] [const uint 8 'serialLow' '0x00' ] // 4.10.3.2.8 Coding of the field RPCObjectUUID DEA00000-6C97-11D1-8271-{instanceOrNodeNumber}{deviceId}{vendorId} // Apache Vendor Id: 0x060B // PLC4X Profinet Driver Device ID (can be chosen freely): 0xCAFE // NOTE: We can get the Device-Id and Vendor-Id from the PN-DCP search result of the browser. [const uint 32 'uuid1' '0xDEA00000' ] [const uint 16 'uuid2' '0x6C97' ] [const uint 16 'uuid3' '0x11D1' ] [const uint 16 'uuid4' '0x8271' ] [simple uint 16 'instanceOrNodeNumber' ] [simple uint 16 'deviceId' ] [simple uint 16 'vendorId' ] // 4.10.3.2.9 // Device Interface: DEA00001-6C97-11D1-8271-00A02442DF7D // Controller Interface: DEA00002-6C97-11D1-8271-00A02442DF7D // Supervisor Interface: DEA00003-6C97-11D1-8271-00A02442DF7D // Parameter Server Interface: DEA00004-6C97-11D1-8271-00A02442DF7D [const uint 32 'interface1' '0xDEA00001' ] [const uint 16 'interface2' '0x6C97' ] [const uint 16 'interface3' '0x11D1' ] [const uint 16 'interface4' '0x8271' ] [const uint 16 'interface5' '0x00A0' ] [const uint 32 'interface6' '0x2442DF7D' ] // 4.10.3.2.10 // The Controller and the Device generate the uuid for each AR (Application Relationship) and use them as long as the AR exists [const uint 32 'activity' ] [const uint 16 'activity2' '0x0000' ] [const uint 16 'activity3' '0x1010' ] [const uint 16 'activity4' '0xAA25' ] [const uint 32 'activity5' '0x606D3C3D' ] [const uint 16 'activity6' '0xA9A3' ] [const uint 32 'serverBootTime' ] [const uint 32 'interfaceVer' '0x00000001' ] [const uint 32 'sequenceNumber' ] [const DceRpc_Operation 'operation' ] [const uint 16 'interfaceHint' '0xFFFF' ] [const uint 16 'activityHint' '0xFFFF' ] [const uint 16 'fragmentLength' 'payload.lengthInBytes'] [const uint 16 'fragmentNum' '0x0000' ] [const uint 8 'authProto' '0x00' ] [const uint 8 'serialLow2' '0x00' ]// TODO: Check this ... [simple PnIoCm_Packet 'payload' ['packetType'] ] ] ] The endianess switching would also apply tot he PnIoCm_Packet automatically. The other option would be: [discriminatedType 'DceRpc_Packet' [const uint 8 'version' '0x04' ] [discriminator DceRpc_PacketType 'packetType' ] [typeSwitch 'packetType' ['REQUEST' DceRpc_Packet_Req [reserved uint 1 '0x0' ] [const uint 1 'broadcast' '0' ] [const uint 1 'idempotent' '1' ] [const uint 1 'maybe' '0' ] [const uint 1 'noFragmentAcknowledgeRequested' '0' ] [const uint 1 'fragment' '0' ] [const uint 1 'lastFragment' '0' ] [reserved uint 1 '0x0' ] ] ['RESPONSE' DceRpc_Packet_Res [reserved uint 1 '0x0' ] [const uint 1 'broadcast' '0' ] [const uint 1 'idempotent' '0' ] [const uint 1 'maybe' '0' ] [const uint 1 'noFragmentAcknowledgeRequested' '1' ] [const uint 1 'fragment' '0' ] [const uint 1 'lastFragment' '1' ] [reserved uint 1 '0x0' ] ] ['REJECT' DceRpc_Packet_Rej [reserved uint 1 '0x0' ] [const uint 1 'broadcast' '0' ] [const uint 1 'idempotent' '0' ] [const uint 1 'maybe' '0' ] [const uint 1 'noFragmentAcknowledgeRequested' '0' ] [const uint 1 'fragment' '0' ] [const uint 1 'lastFragment' '0' ] [reserved uint 1 '0x0' ] ] ] [reserved uint 6 '0x00' ] [const uint 1 'cancelWasPending' '0' ] [reserved uint 1 '0x0' ] [simple IntegerEncoding 'integerEncoding' ] [simple CharacterEncoding 'characterEncoding' ] [simple FloatingPointEncoding 'floatingPointEncoding' ] [simple DceRpc_PacketPayload 'payload' endianess='integerEncoding' ] ] [type 'DceRpc_PacketPayload' [const uint 8 'serialHigh' '0x00' ] [const uint 8 'serialLow' '0x00' ] // 4.10.3.2.8 Coding of the field RPCObjectUUID DEA00000-6C97-11D1-8271-{instanceOrNodeNumber}{deviceId}{vendorId} // Apache Vendor Id: 0x060B // PLC4X Profinet Driver Device ID (can be chosen freely): 0xCAFE // NOTE: We can get the Device-Id and Vendor-Id from the PN-DCP search result of the browser. [const uint 32 'uuid1' '0xDEA00000' ] [const uint 16 'uuid2' '0x6C97' ] [const uint 16 'uuid3' '0x11D1' ] [const uint 16 'uuid4' '0x8271' ] [simple uint 16 'instanceOrNodeNumber' ] [simple uint 16 'deviceId' ] [simple uint 16 'vendorId' ] // 4.10.3.2.9 // Device Interface: DEA00001-6C97-11D1-8271-00A02442DF7D // Controller Interface: DEA00002-6C97-11D1-8271-00A02442DF7D // Supervisor Interface: DEA00003-6C97-11D1-8271-00A02442DF7D // Parameter Server Interface: DEA00004-6C97-11D1-8271-00A02442DF7D [const uint 32 'interface1' '0xDEA00001' ] [const uint 16 'interface2' '0x6C97' ] [const uint 16 'interface3' '0x11D1' ] [const uint 16 'interface4' '0x8271' ] [const uint 16 'interface5' '0x00A0' ] [const uint 32 'interface6' '0x2442DF7D' ] // 4.10.3.2.10 // The Controller and the Device generate the uuid for each AR (Application Relationship) and use them as long as the AR exists [const uint 32 'activity' ] [const uint 16 'activity2' '0x0000' ] [const uint 16 'activity3' '0x1010' ] [const uint 16 'activity4' '0xAA25' ] [const uint 32 'activity5' '0x606D3C3D' ] [const uint 16 'activity6' '0xA9A3' ] [const uint 32 'serverBootTime' ] [const uint 32 'interfaceVer' '0x00000001' ] [const uint 32 'sequenceNumber' ] [const DceRpc_Operation 'operation' ] [const uint 16 'interfaceHint' '0xFFFF' ] [const uint 16 'fragmentNum' '0x0000' ] [const uint 16 'activityHint' '0xFFFF' ] [const uint 16 'fragmentLength' 'payload.lengthInBytes'] [const uint 8 'authProto' '0x00' ] [const uint 8 'serialLow2' '0x00' ]// TODO: Check this ... [simple PnIoCm_Packet 'payload' ['packetType'] ] ] Thinking about it ... we might even have a third option: Explicitly defininig the endianess in the complex type (Just a quick thought perhaps name things differently): // Big Endian [discriminatedType 'PnIoCm_Block' endianess=BIG_ENDIAN [discriminator PnIoCm_BlockType 'blockType' ] [implicit uint 16 'blockLength' 'lengthInBytes - 4'] [simple uint 8 'blockVersionHigh' ] [simple uint 8 'blockVersionLow' ] ... I have to admit, that I sort of like option 1 and 3 ... but we might even support all 3 variants. The implementation shouldn't be too hard. And thinking even more about it ... perhaps this concept of adding name=value pairs to the type declaration and the fields could also be used to controll Sebasitan's "try" flag. Chris