//
// 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
//
//     http://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.
//

////////////////////////////////////////////////////////////////
// AMS/TCP Paket
////////////////////////////////////////////////////////////////

[type 'AmsTCPPacket'
    // The ams - tcp to be sent.
    [simple AmsTcpHeader 'amsTcpHeader']
    // The AMS packet to be sent.
    [simple AmsPacket    'userdata'    ]
]

// AMS/TCP Header	6 bytes	contains the tcpLength of the data packet.
[type 'AmsTcpHeader'
    // These bytes must be set to 0.
    [reserved   uint       16       '0x0000' ]
    // This array contains the length of the data packet.
    // It consists of the AMS-Header and the enclosed ADS data. The unit is bytes.
    [simple     uint       32       'length']
]

////////////////////////////////////////////////////////////////
// AMS/Serial Paket
////////////////////////////////////////////////////////////////

// If an AMS serial frame has been received and the frame is OK (magic cookie OK, CRC OK, correct fragment number etc.),
// then the receiver has to send an acknowledge frame, to inform the transmitter that the frame has arrived.
//
// @see <a href="https://infosys.beckhoff.com/content/1033/tcadsamsserialspec/html/tcamssericalspec_amsframe.htm?id=8115637053270715044">TwinCAT AMS via RS232 Specification</a>
[type 'AmsSerialAcknowledgeFrame'
    // Id for detecting an AMS serial frame.
    [simple     uint        16  'magicCookie'        ]
    // Address of the sending participant. This value can always be set to 0 for an RS232 communication,
    // since it is a 1 to 1 connection and hence the participants are unique.
    [simple     int          8  'transmitterAddress' ]
    // Receiver’s address. This value can always be set to 0 for an RS232 communication, since it is a 1 to 1
    // connection and hence the participants are unique.
    [simple     int          8  'receiverAddress'    ]
    // Number of the frame sent. Once the number 255 has been sent, it starts again from 0. The receiver checks this
    // number with an internal counter.
    [simple     int          8  'fragmentNumber'     ]
    // The max. length of the AMS packet to be sent is 255. If larger AMS packets are to be sent then they have to be
    // fragmented (not published at the moment).
    [simple     int          8  'length'     ]
    [simple     uint        16  'crc'                ]
]

// An AMS packet can be transferred via RS232 with the help of an AMS serial frame.
// The actual AMS packet is in the user data field of the frame.
// The max. length of the AMS packet is limited to 255 bytes.
// Therefore the max. size of an AMS serial frame is 263 bytes.
// The fragment number is compared with an internal counter by the receiver.
// The frame number is simply accepted and not checked when receiving the first AMS frame or in case a timeout is
// exceeded. The CRC16 algorithm is used for calculating the checksum.
// @see <a href="https://infosys.beckhoff.com/content/1033/tcadsamsserialspec/html/tcamssericalspec_amsframe.htm?id=8115637053270715044">TwinCAT AMS via RS232 Specification</a>
[type 'AmsSerialFrame'
    // Id for detecting an AMS serial frame.
    [simple     uint        16  'magicCookie'        ]
    // Address of the sending participant. This value can always be set to 0 for an RS232 communication,
    // since it is a 1 to 1 connection and hence the participants are unique.
    [simple     int          8  'transmitterAddress' ]
    // Receiver’s address. This value can always be set to 0 for an RS232 communication, since it is a 1 to 1
    // connection and hence the participants are unique.
    [simple     int          8  'receiverAddress'    ]
    // Number of the frame sent. Once the number 255 has been sent, it starts again from 0. The receiver checks this
    // number with an internal counter.
    [simple     int          8  'fragmentNumber'     ]
    // The max. length of the AMS packet to be sent is 255. If larger AMS packets are to be sent then they have to be
    // fragmented (not published at the moment).
    [simple     int          8  'length'     ]
    // The AMS packet to be sent.
    [simple AmsPacket           'userdata'           ]
    [simple     uint        16  'crc'                ]
]

// In case the transmitter does not receive a valid acknowledgement after multiple transmission, then a reset frame is
// sent. In this way the receiver is informed that a new communication is running and the receiver then accepts the
// fragment number during the next AMS-Frame, without carrying out a check.
[type 'AmsSerialResetFrame'
    // Id for detecting an AMS serial frame.
    [simple     uint        16  'magicCookie'        ]
    // Address of the sending participant. This value can always be set to 0 for an RS232 communication,
    // since it is a 1 to 1 connection and hence the participants are unique.
    [simple     int          8  'transmitterAddress' ]
    // Receiver’s address. This value can always be set to 0 for an RS232 communication, since it is a 1 to 1
    // connection and hence the participants are unique.
    [simple     int          8  'receiverAddress'    ]
    // Number of the frame sent. Once the number 255 has been sent, it starts again from 0. The receiver checks this
    // number with an internal counter.
    [simple     int          8  'fragmentNumber'     ]
    // The max. length of the AMS packet to be sent is 255. If larger AMS packets are to be sent then they have to be
    // fragmented (not published at the moment).
    [simple     int          8  'length'     ]
    [simple     uint        16  'crc'                ]
]

////////////////////////////////////////////////////////////////
// AMS Common
////////////////////////////////////////////////////////////////

[type 'AmsPacket'
    [simple     AmsHeader  'amsHeader'                                                            ]
    [simple     ADSData    'data'   ['amsHeader.commandId', 'amsHeader.state.response']              ]
]

// AMS Header	32 bytes	The AMS/TCP-Header contains the addresses of the transmitter and receiver. In addition the AMS error code , the ADS command Id and some other information.
[type 'AmsHeader'
    // This is the AmsNetId of the station, for which the packet is intended. Remarks see below.
    [simple     AmsNetId        'targetAmsNetId'                            ]
    // This is the AmsPort of the station, for which the packet is intended.
    [simple     uint        16  'targetAmsPort'                             ]
    // This contains the AmsNetId of the station, from which the packet was sent.
    [simple     AmsNetId        'sourceAmsNetId'                            ]
    // This contains the AmsPort of the station, from which the packet was sent.
    [simple     uint        16  'sourceAmsPort'                             ]
    // 2 bytes.
    [enum       CommandId       'commandId'                                 ]
    // 2 bytes.
    [simple     State           'state'                                     ]
    // 4 bytes	Size of the data range. The unit is byte.
    [simple     uint        32  'length'                                ]
    // 4 bytes	AMS error number. See ADS Return Codes.
    [simple     uint        32  'errorCode'                                 ]
    // free usable field of 4 bytes
    // 4 bytes	Free usable 32 bit array. Usually this array serves to send an Id. This Id makes is possible to assign a received response to a request, which was sent before.
    [simple      uint        32  'invokeId'                                 ]
]

[enum uint 16 'CommandId'
    ['0x00' INVALID]
    ['0x01' ADS_READ_DEVICE_INFO]
    ['0x02' ADS_READ]
    ['0x03' ADS_WRITE]
    ['0x04' ADS_READ_STATE]
    ['0x05' ADS_WRITE_CONTROL]
    ['0x06' ADS_ADD_DEVICE_NOTIFICATION]
    ['0x07' ADS_DELETE_DEVICE_NOTIFICATION]
    ['0x08' ADS_DEVICE_NOTIFICATION]
    ['0x09' ADS_READ_WRITE]
]

[type 'State'
    [simple     bit 'broadcast'             ]
    [reserved   int 7 '0x0'                 ]
    [simple     bit 'initCommand'           ]
    [simple     bit 'updCommand'            ]
    [simple     bit 'timestampAdded'        ]
    [simple     bit 'highPriorityCommand'   ]
    [simple     bit 'systemCommand'         ]
    [simple     bit 'adsCommand'            ]
    [simple     bit 'noReturn'              ]
    [simple     bit 'response'              ]
]

// It is not only possible to exchange data between TwinCAT modules on one PC, it is even possible to do so by ADS
// methods between multiple TwinCAT PC's on the network.
// <p>
// Every PC on the network can be uniquely identified by a TCP/IP address, such as "172.1.2.16". The AdsAmsNetId is an
// extension of the TCP/IP address and identifies a TwinCAT message router, e.g. "172.1.2.16.1.1". TwinCAT message
// routers exist on every TwinCAT PC, and on every Beckhoff BCxxxx bus controller (e.g. BC3100, BC8100, BC9000, ...).
// <p>
// The AmsNetId consists of 6 bytes and addresses the transmitter or receiver. One possible AmsNetId would be e.g.
// "172.16.17.10.1.1". The storage arrangement in this example is as follows:
// <p>
// _____0     1     2     3     4     5
// __+-----------------------------------+
// 0 | 127 |  16 |  17 |  10 |   1 |   1 |
// __+-----------------------------------+
// <p>
// The AmsNetId is purely logical and has usually no relation to the IP address. The AmsNetId is configured at the
// target system. At the PC for this the TwinCAT System Control is used. If you use other hardware, see the considering
// documentation for notes about settings of the AMS NetId.
// @see <a href="https://infosys.beckhoff.com/content/1033/tcadscommon/html/tcadscommon_identadsdevice.htm?id=3991659524769593444">ADS device identification</a>
[type 'AmsNetId'
    [simple     uint        8   'octet1'            ]
    [simple     uint        8   'octet2'            ]
    [simple     uint        8   'octet3'            ]
    [simple     uint        8   'octet4'            ]
    [simple     uint        8   'octet5'            ]
    [simple     uint        8   'octet6'            ]
]

[discriminatedType 'ADSData' [CommandId 'commandId', bit 'response']
    [typeSwitch 'commandId', 'response'
        ['CommandId.INVALID', 'true' AdsInvalidResponse]
        ['CommandId.INVALID', 'false' AdsInvalidRequest]
        ['CommandId.ADS_READ_DEVICE_INFO', 'true' AdsReadDeviceInfoResponse
            // 4 bytes	ADS error number.
            [simple uint 32 'result']
            // Version	1 byte	Major version number
            [simple uint 8  'majorVersion']
            // Version	1 byte	Minor version number
            [simple uint 8  'minorVersion']
            // Build	2 bytes	Build number
            [simple uint 16  'version']
            // Name	16 bytes	Name of ADS device
            [array int 8  'device' count '16']
        ]
        ['CommandId.ADS_READ_DEVICE_INFO', 'false' AdsReadDeviceInfoRequest]
        ['CommandId.ADS_READ', 'true' AdsReadResponse
            // 4 bytes	ADS error number
            [simple uint 32 'result']
            // 4 bytes	Length of data which are supplied back.
            [simple uint 32 'length']
            // n bytes	Data which are supplied back.
            [array int 8 'data' count 'length']
        ]
        ['CommandId.ADS_READ', 'false' AdsReadRequest
            // 4 bytes	Index Group of the data which should be read.
            [simple uint 32 'indexGroup']
            // 4 bytes	Index Offset of the data which should be read.
            [simple uint 32 'indexOffset']
            // 4 bytes	Length of the data (in bytes) which should be read.
            [simple uint 32 'length']
        ]
        ['CommandId.ADS_WRITE', 'true' AdsWriteResponse
            // 4 bytes	ADS error number
            [simple uint 32 'result']
        ]
        ['CommandId.ADS_WRITE', 'false' AdsWriteRequest
            // 4 bytes	Index Group of the data which should be written.
            [simple uint 32 'indexGroup']
            // 4 bytes	Index Offset of the data which should be written.
            [simple uint 32 'indexOffset']
            // 4 bytes	Length of the data (in bytes) which should be written.
            [simple uint 32 'length']
            // n bytes	Data which are written in the ADS device.
            [array int 8 'data' count 'length']
        ]
        ['CommandId.ADS_READ_STATE', 'true' AdsReadStateResponse
            // 4 bytes	ADS error number
            [simple uint 32 'result']
            // 2 bytes	New ADS status (see data type ADSSTATE of the ADS-DLL).
            [simple uint 16 'adsState']
            // 2 bytes	New device status.
            [simple uint 16 'deviceState']
        ]
        ['CommandId.ADS_READ_STATE', 'false' AdsReadStateRequest]
        ['CommandId.ADS_WRITE_CONTROL', 'true' AdsWriteControlResponse
            // 4 bytes	ADS error number
            [simple uint 32 'result']
        ]
        ['CommandId.ADS_WRITE_CONTROL', 'false' AdsWriteControlRequest
            // 2 bytes	New ADS status (see data type ADSSTATE of the ADS-DLL).
            [simple uint 16 'adsState']
            // 2 bytes	New device status.
            [simple uint 16 'deviceState']
            // 4 bytes	Length of data in byte.
            [simple uint 32 'length']
            // n bytes	Additional data which are sent to the ADS device
            [array int 8 'data' count 'length']
        ]
        ['CommandId.ADS_ADD_DEVICE_NOTIFICATION', 'true' AdsAddDeviceNotificationResponse
            // 4 bytes	ADS error number
            [simple uint 32 'result']
            // 4 bytes	Handle of notification
            [simple uint 32 'notificationHandle']
        ]
        ['CommandId.ADS_ADD_DEVICE_NOTIFICATION', 'false' AdsAddDeviceNotificationRequest
            // 4 bytes	Index Group of the data, which should be sent per notification.
            [simple uint 32 'indexGroup']
            // 4 bytes	Index Offset of the data, which should be sent per notification.
            [simple uint 32 'indexOffset']
            // 4 bytes	Index Offset of the data, which should be sent per notification.
            // 4 bytes	Length of data in bytes, which should be sent per notification.
            [simple uint 32 'length']
            // 4 bytes	See description of the structure ADSTRANSMODE at the ADS-DLL.
            [simple uint 32 'transmissionMode']
            // 4 bytes	At the latest after this time, the ADS Device Notification is called. The unit is 1ms.
            [simple uint 32 'maxDelay']
            // 4 bytes	The ADS server checks if the value changes in this time slice. The unit is 1ms
            [simple uint 32 'cycleTime']
            // 16bytes	Must be set to 0
            [reserved   uint       128       '0x0000' ]
        ]
        ['CommandId.ADS_DELETE_DEVICE_NOTIFICATION', 'true' AdsDeleteDeviceNotificationResponse
            // 4 bytes	ADS error number
            [simple uint 32 'result']
        ]
        ['CommandId.ADS_DELETE_DEVICE_NOTIFICATION', 'false' AdsDeleteDeviceNotificationRequest
            // 4 bytes	Handle of notification
            [simple uint 32 'notificationHandle']
        ]
        ['CommandId.ADS_DEVICE_NOTIFICATION', 'true' AdsDeviceNotificationResponse]
        ['CommandId.ADS_DEVICE_NOTIFICATION', 'false' AdsDeviceNotificationRequest
            // 4 bytes	Size of data in byte.
            [simple uint 32 'length']
            // 4 bytes	Number of elements of type AdsStampHeader.
            [simple uint 32 'stamps']
            // n bytes	Array with elements of type AdsStampHeader.
            [array AdsStampHeader 'adsStampHeaders' count 'stamps']
        ]
        ['CommandId.ADS_READ_WRITE', 'true' AdsReadWriteResponse
            // 4 bytes	ADS error number
            [simple uint 32 'result']
            // 4 bytes	Length of data in byte.
            [simple uint 32 'length']
            // n bytes	Additional data which are sent to the ADS device
            [array int 8 'data' count 'length']
        ]
        ['CommandId.ADS_READ_WRITE', 'false' AdsReadWriteRequest
            // 4 bytes	Index Group of the data which should be written.
            [simple uint 32 'indexGroup']
            // 4 bytes	Index Offset of the data which should be written.
            [simple uint 32 'indexOffset']
            // 4 bytes	Length of data in bytes, which should be read.
            [simple uint 32 'readLength']
            // 4 bytes	Length of the data (in bytes) which should be written.
            [simple uint 32 'writeLength']
            // n bytes	Data which are written in the ADS device.
            [array int 8 'data' count 'writeLength']
        ]
    ]
]

[type 'AdsStampHeader'
    // 8 bytes	The timestamp is coded after the Windows FILETIME format. I.e. the value contains the number of the nano seconds, which passed since 1.1.1601. In addition, the local time change is not considered. Thus the time stamp is present as universal Coordinated time (UTC).
    [simple uint 64 'timestamp']
    // 4 bytes	Number of elements of type AdsNotificationSample.
    [simple uint 32 'samples']
    // n bytes	Array with elements of type AdsNotificationSample.
    [array AdsNotificationSample 'adsNotificationSamples' count 'samples']
]

[type 'AdsNotificationSample'
    // 4 bytes	Handle of notification
    [simple uint 32 'notificationHandle']
    // 4 Bytes	Size of data range in bytes.
    [simple uint 32 'sampleSize']
    // n Bytes	Data
    [array int 8 'data' count 'sampleSize']
]