|
| 1 | +/* |
| 2 | + * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | + * or more contributor license agreements. See the NOTICE file |
| 4 | + * distributed with this work for additional information |
| 5 | + * regarding copyright ownership. The ASF licenses this file |
| 6 | + * to you under the Apache License, Version 2.0 (the |
| 7 | + * "License"); you may not use this file except in compliance |
| 8 | + * with the License. You may obtain a copy of the License at |
| 9 | + * |
| 10 | + * https://www.apache.org/licenses/LICENSE-2.0 |
| 11 | + * |
| 12 | + * Unless required by applicable law or agreed to in writing, |
| 13 | + * software distributed under the License is distributed on an |
| 14 | + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | + * KIND, either express or implied. See the License for the |
| 16 | + * specific language governing permissions and limitations |
| 17 | + * under the License. |
| 18 | + */ |
| 19 | + |
| 20 | +// SLMP (Seamless Message Protocol) / MELSEC Communication protocol. |
| 21 | +// |
| 22 | +// Specification: Mitsubishi "MELSEC Communication Protocol Reference Manual" |
| 23 | +// (SH(NA)-080008, public). All field layouts below are taken from that manual: |
| 24 | +// - 3E frame message format ....... section 5.2 |
| 25 | +// - Subheader (50 00 / D0 00) ...... section 5.3 |
| 26 | +// - Access route (3E fixed value) .. chapter 6 |
| 27 | +// - Request/response data length ... section 5.3 (2-byte, little-endian) |
| 28 | +// - Commands (Batch/Random/block Read) chapter 7 / 8.1 / 8.3 / 8.4 |
| 29 | +// - Device code list ............... section 8.1 (MELSEC-Q/L, 1-byte binary) |
| 30 | +// - Batch Read data layout ......... section 8.1 (binary, word units) |
| 31 | +// - Random Read data layout ........ section 8.3 (binary, word units) |
| 32 | +// - Multi-block Read data layout ... section 8.4 (binary, word units) |
| 33 | +// |
| 34 | +// Scope of this initial version: 3E binary frame, read-only, Batch Read |
| 35 | +// (command 0x0401), Random Read (command 0x0403) and Batch Read Multiple Blocks |
| 36 | +// (command 0x0406) in word units (subcommand 0x0000). This is the wire layer |
| 37 | +// only; typed value decoding |
| 38 | +// (INT/WORD/DINT/REAL) and the device-addressing tag layer are intentionally |
| 39 | +// NOT modelled here yet and will follow once the driver logic is built. |
| 40 | +// Validated hardware-free via the ParserSerializer test suite. |
| 41 | +// |
| 42 | +// All multi-byte numeric fields are little-endian (transmitted least-significant byte first). |
| 43 | +// The 2-byte subheader (0x50/0xD0 then 0x00) is modelled as two single bytes so |
| 44 | +// its on-wire order is preserved regardless of the frame byte order. |
| 45 | + |
| 46 | +[constants |
| 47 | + // Typical SLMP TCP port; the actual port is configured on the device and is |
| 48 | + // overridable by the driver, so this is only a default. |
| 49 | + [const uint 16 slmpDefaultPort 5007] |
| 50 | +] |
| 51 | + |
| 52 | +// Device codes for MELSEC-Q/L series commands (subcommand 0x0000 / 0x0001), |
| 53 | +// 1-byte binary, from the device code list in SH-080008 section 8.1. |
| 54 | +// Only the word/bit devices needed by the read-only road-map are listed. |
| 55 | +// |
| 56 | +// These 1-byte binary device codes are confirmed identical in the Mitsubishi |
| 57 | +// MELSEC iQ-F FX5 SLMP manual (JY997D56001) -- D=A8 W=B4 R=AF M=90 X=9C Y=9D |
| 58 | +// B=A0 -- so the same frame works on FX5 hardware (e.g. FX5S) as well as |
| 59 | +// MELSEC-Q/L. (FX5 also offers a 2-byte/extended code form via subcommand |
| 60 | +// 0x0002/0x0003 for ZR and large device numbers, which is out of scope here.) |
| 61 | +[enum uint 8 SlmpDeviceCode |
| 62 | + ['0xA8' D] // Data register (word, decimal addressing) |
| 63 | + ['0xB4' W] // Link register (word, hex addressing) |
| 64 | + ['0xAF' R] // File register (word, decimal addressing) |
| 65 | + ['0xC2' TN] // Timer, current value (word, decimal addressing) -- used by the |
| 66 | + // Random Read worked example in SH-080008 section 8.3 |
| 67 | + ['0x90' M] // Internal relay (bit, decimal addressing) |
| 68 | + ['0x9C' X] // Input (bit, hex addressing) |
| 69 | + ['0x9D' Y] // Output (bit, hex addressing) |
| 70 | + ['0xA0' B] // Link relay (bit, hex addressing) |
| 71 | +] |
| 72 | +
|
| 73 | +// 3E frame. The Ethernet/TCP header is added by the transport and is not part |
| 74 | +// of this message. Request and response are distinguished by the subheader byte |
| 75 | +// (0x50 request / 0xD0 response). |
| 76 | +[discriminatedType SlmpMessage byteOrder='"LITTLE_ENDIAN"' unsignedIntegerEncoding='"unsigned-binary"' signedIntegerEncoding='"twos-complement"' floatEncoding='"IEEE754"' stringEncoding='"UTF8"' |
| 77 | + [discriminator uint 8 subHeader] // 0x50 request, 0xD0 response |
| 78 | + [const uint 8 subHeaderReserved 0x00] // second subheader byte is always 0x00 |
| 79 | + [typeSwitch subHeader |
| 80 | + ['0x50' SlmpRequestFrame3E |
| 81 | + // Access route - fixed value for 3E frame (chapter 6): 00 FF FF 03 00 |
| 82 | + [const uint 8 network 0x00] |
| 83 | + [const uint 8 pcNumber 0xFF] |
| 84 | + [const uint 16 requestDestModuleIoNo 0x03FF] |
| 85 | + [const uint 8 requestDestModuleStation 0x00] |
| 86 | + // Number of bytes from the monitoring timer up to the end of the request data. |
| 87 | + // = monitoringTimer(2) + command(2) + subCommand(2) + requestData |
| 88 | + [implicit uint 16 requestDataLength '6 + requestData.lengthInBytes'] |
| 89 | + [simple uint 16 monitoringTimer] // 0x0000 = wait infinitely |
| 90 | + [simple uint 16 command] // 0x0401 = Batch Read |
| 91 | + [simple uint 16 subCommand] // 0x0000 = word units |
| 92 | + [simple SlmpRequestData('command') requestData] |
| 93 | + ] |
| 94 | + ['0xD0' SlmpResponseFrame3E |
| 95 | + [const uint 8 network 0x00] |
| 96 | + [const uint 8 pcNumber 0xFF] |
| 97 | + [const uint 16 requestDestModuleIoNo 0x03FF] |
| 98 | + [const uint 8 requestDestModuleStation 0x00] |
| 99 | + // Number of bytes from the end code up to the end of the response data. |
| 100 | + // = endCode(2) + responseData |
| 101 | + [implicit uint 16 responseDataLength '2 + COUNT(responseData)'] |
| 102 | + [simple uint 16 endCode] // 0x0000 = normal completion |
| 103 | + // Raw payload. On normal completion this is the read words (little-endian, |
| 104 | + // 2 bytes per point); on abnormal completion it carries error information. |
| 105 | + // Typed decoding is left to the driver layer. |
| 106 | + [array byte responseData count 'responseDataLength - 2'] |
| 107 | + ] |
| 108 | + ] |
| 109 | +] |
| 110 | + |
| 111 | +// Request data, dispatched by command. Batch Read (0x0401) and Random Read |
| 112 | +// (0x0403) are modelled here; both are read-only, word units (subcommand 0x0000). |
| 113 | +[discriminatedType SlmpRequestData(uint 16 command) |
| 114 | + [typeSwitch command |
| 115 | + ['0x0401' SlmpReadRequest |
| 116 | + // Binary, MELSEC-Q/L series: device number (3 bytes, little-endian) |
| 117 | + // then device code (1 byte). See SH-080008 section 8.1. |
| 118 | + [simple uint 24 headDeviceNumber] |
| 119 | + [simple SlmpDeviceCode deviceCode] |
| 120 | + // Number of points to read. In word units this is the number of 16-bit words. |
| 121 | + [simple uint 16 numberOfPoints] |
| 122 | + ] |
| 123 | + ['0x0403' SlmpRandomReadRequest |
| 124 | + // Random Read in word units (SH-080008 section 8.3): read discontinuous |
| 125 | + // devices, given as a list of word-access points followed by a list of |
| 126 | + // double-word-access points. The point counts are single bytes; each |
| 127 | + // device is a 3-byte device number + 1-byte device code (SlmpDeviceSpec). |
| 128 | + [simple uint 8 numberOfWordAccessPoints] |
| 129 | + [simple uint 8 numberOfDoubleWordAccessPoints] |
| 130 | + [array SlmpDeviceSpec wordAccessDevices count 'numberOfWordAccessPoints'] |
| 131 | + [array SlmpDeviceSpec doubleWordAccessDevices count 'numberOfDoubleWordAccessPoints'] |
| 132 | + ] |
| 133 | + ['0x0406' SlmpMultiBlockReadRequest |
| 134 | + // Batch Read multiple blocks in word units (SH-080008 section 8.4): read a |
| 135 | + // number of word-device blocks followed by a number of bit-device blocks, |
| 136 | + // where each block is a run of consecutive devices. The two block counts are |
| 137 | + // single bytes; each block (SlmpDeviceBlock) is a 3-byte head device number, |
| 138 | + // a 1-byte device code and a 2-byte number of points. Bit-device blocks are |
| 139 | + // read in 16-bit word units (one point = 16 bits), so the wire layout of a |
| 140 | + // bit-device block is identical to that of a word-device block. |
| 141 | + [simple uint 8 numberOfWordDeviceBlocks] |
| 142 | + [simple uint 8 numberOfBitDeviceBlocks] |
| 143 | + [array SlmpDeviceBlock wordDeviceBlocks count 'numberOfWordDeviceBlocks'] |
| 144 | + [array SlmpDeviceBlock bitDeviceBlocks count 'numberOfBitDeviceBlocks'] |
| 145 | + ] |
| 146 | + ] |
| 147 | +] |
| 148 | + |
| 149 | +// A single device specification used by commands that address discontinuous |
| 150 | +// devices (e.g. Random Read 0x0403): a device number (3 bytes, little-endian) |
| 151 | +// followed by the 1-byte device code. See SH-080008 section 8.1 / 8.3. |
| 152 | +[type SlmpDeviceSpec |
| 153 | + [simple uint 24 deviceNumber] |
| 154 | + [simple SlmpDeviceCode deviceCode] |
| 155 | +] |
| 156 | + |
| 157 | +// A single block of consecutive devices used by commands that read whole blocks |
| 158 | +// (e.g. Batch Read Multiple Blocks 0x0406): a head device number (3 bytes, |
| 159 | +// little-endian) and 1-byte device code identifying the start of the run, |
| 160 | +// followed by the 2-byte number of points (consecutive devices) in the block. |
| 161 | +// This is the same field layout as a single-block Batch Read request |
| 162 | +// (SlmpReadRequest). See SH-080008 section 8.4. |
| 163 | +[type SlmpDeviceBlock |
| 164 | + [simple uint 24 headDeviceNumber] |
| 165 | + [simple SlmpDeviceCode deviceCode] |
| 166 | + [simple uint 16 numberOfPoints] |
| 167 | +] |
0 commit comments