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