Example: DF1 MSpec

The DF1 protocol has three basic messages: a command message, acknowledge and not acknowledge. A 0x10 is used as delimiter to differentiate between the messages and parts of the command message.

ACK NAK

10 06

10 15

This is what a read command message looks like in full-duplex mode using CRC as checksum:

Name DLE STX DST SRC CMD STS TNS ADDR SIZE DLE ETX CRC

Value

10

02

XX

XX

01

00

XX XX

XX XX

02

10

03

XX XX

The according response with the requested byte data:

Name DLE STX DST SRC CMD STS TNS DATA SIZE DLE ETX CRC

Value

10

02

XX

XX

41

00

XX XX

XX XX

02

10

03

XX XX

Every message starts with a DLE = 0x10. The second byte is used to differentiate what type of message will follow. In the case of a command message it is 0x02. DST and SRC specify the target and source of the message. CMD = 0x01 means an unprotected read is requested, the command response code is always attained with the addition of 0x40. STS is a status byte, TNS is the transaction counter that is incremented by the master and used to recognize the specific message response. ADDR is the memory address being requested, size specifies how many bytes are to be read. DLE + ETX mark the end of the message, the last two bytes are the CRC that is calculated using the previously sent bytes.

Here is the MSpec to model the behaviour:

[discriminatedType DF1Symbol
    [const            uint 8       messageStart 0x10]
    [discriminator    uint 8       symbolType]
    [typeSwitch 'symbolType'
        ['0x02' DF1SymbolMessageFrame
            [simple   uint 8       destinationAddress]
            [simple   uint 8       sourceAddress]
            [simple   DF1Command   command]
            [const    uint 8       messageEnd 0x10]
            [const    uint 8       endTransaction 0x03]
            [checksum uint 16      'crc' 'STATIC_CALL("org.apache.plc4x.java.df1.readwrite.utils.StaticHelper.crcCheck", destinationAddress, sourceAddress, command)']
        ]
        ['0x06' DF1SymbolMessageFrameACK
        ]
        ['0x15' DF1SymbolMessageFrameNAK
        ]
    ]
]

[discriminatedType DF1Command
    [discriminator uint 8  commandCode]
    [simple    uint 8       status]
    [simple    uint 16      transactionCounter]
    [typeSwitch 'commandCode'
        ['0x01' DF1UnprotectedReadRequest
            [simple uint 16    address]
            [simple uint 8     size]
        ]
        ['0x41' DF1UnprotectedReadResponse
            [manualArray  uint 8 'data' terminated 'STATIC_CALL("org.apache.plc4x.java.df1.readwrite.utils.StaticHelper.dataTerminate", io)' 'STATIC_CALL("org.apache.plc4x.java.df1.readwrite.utils.StaticHelper.readData", io)' 'STATIC_CALL("org.apache.plc4x.java.df1.readwrite.utils.StaticHelper.writeData", io, element)' 'STATIC_CALL("org.apache.plc4x.java.df1.readwrite.utils.StaticHelper.dataLength", data)']
        ]
    ]
]

The basic object is the DF1Symbol, where the second byte is used to distinguish between the different message types using a typeSwitch. In the case of a command message, the message frame contains the DF1Command to further differentiate between the command types. Currently only the unprotected read and its response are implemented.