Skip to content

Commit 7ac57b3

Browse files
feat: Add SLMP (MELSEC Communication 3E) protocol module (read-only wire layer) (#2597)
* feat: Add SLMP (MELSEC Communication 3E) protocol module - read-only wire layer Adds the codegen-layer protocol module for SLMP / MELSEC Communication protocol (Mitsubishi PLCs), as proposed in the SLMP driver proposal issue: - slmp.mspec modelling the 3E binary frame (request/response), with Batch Read (0x0401), Random Read (0x0403) and Batch Read Multiple Blocks (0x0406) in word units, read-only - ParserSerializer test suite with vectors derived from the worked examples in the public Mitsubishi reference manual SH-080008 (sections 8.1/8.3/8.4) - Root type declares the new-SPI encoding defaults (little-endian) The driver module (plc4j/drivers/slmp) will follow on top of the new SPI as a separate step. Signed-off-by: Jooyoung Jung <livinglikekrillin@gmail.com> * fix(slmp): use canonical enum form for deviceCode in test vectors deviceCode is a numeric enum (SlmpDeviceCode), but the ParserSerializer test vectors used the shorthand <deviceCode>D</deviceCode> form. That does not round-trip through the generated parser/serializer, which expects the full enum element with its numeric value: <deviceCode> <SlmpDeviceCode dataType="uint" bitLength="8" stringRepresentation="D">168</SlmpDeviceCode> </deviceCode> Convert all deviceCode occurrences in ParserSerializerTestsuite.xml to the canonical form so the vectors round-trip once the generated driver types exercise them. --------- Signed-off-by: Jooyoung Jung <livinglikekrillin@gmail.com>
1 parent 0a02198 commit 7ac57b3

8 files changed

Lines changed: 634 additions & 0 deletions

File tree

protocols/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
<module>profinet</module>
6060
<module>s7</module>
6161
<module>simulated</module>
62+
<module>slmp</module>
6263
<module>socketcan</module>
6364
<module>umas</module>
6465
</modules>

protocols/slmp/pom.xml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
https://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
22+
<modelVersion>4.0.0</modelVersion>
23+
24+
<parent>
25+
<groupId>org.apache.plc4x</groupId>
26+
<artifactId>plc4x-protocols</artifactId>
27+
<version>0.14.0-SNAPSHOT</version>
28+
</parent>
29+
30+
<artifactId>plc4x-protocols-slmp</artifactId>
31+
32+
<name>Protocols: SLMP</name>
33+
<description>Base protocol specifications for the SLMP / MELSEC Communication protocol</description>
34+
35+
<properties>
36+
<project.build.outputTimestamp>2025-08-02T13:55:11Z</project.build.outputTimestamp>
37+
</properties>
38+
39+
<dependencies>
40+
<dependency>
41+
<groupId>org.apache.plc4x</groupId>
42+
<artifactId>plc4x-code-generation-protocol-base-mspec</artifactId>
43+
<version>0.14.0-SNAPSHOT</version>
44+
</dependency>
45+
46+
<dependency>
47+
<groupId>ch.qos.logback</groupId>
48+
<artifactId>logback-classic</artifactId>
49+
<scope>test</scope>
50+
</dependency>
51+
</dependencies>
52+
53+
</project>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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+
package org.apache.plc4x.protocol.slmp;
20+
21+
import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
22+
import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
23+
import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
24+
import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
25+
import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
26+
import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
27+
28+
public class SlmpProtocol implements Protocol, ProtocolHelpers {
29+
30+
@Override
31+
public String getName() {
32+
return "slmp";
33+
}
34+
35+
@Override
36+
public TypeContext getTypeContext() throws GenerationException {
37+
ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
38+
typeContext.validate();
39+
return typeContext;
40+
}
41+
42+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
org.apache.plc4x.protocol.slmp.SlmpProtocol
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
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+
]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
package org.apache.plc4x.protocol.slmp;
20+
21+
import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
22+
import org.junit.jupiter.api.Test;
23+
24+
import static org.junit.jupiter.api.Assertions.assertNotNull;
25+
import static org.junit.jupiter.api.Assertions.assertSame;
26+
27+
class SlmpProtocolTest {
28+
29+
@Test
30+
void getTypeContext() throws Exception {
31+
TypeContext typeContext = new SlmpProtocol().getTypeContext();
32+
assertNotNull(typeContext);
33+
assertNotNull(typeContext.getUnresolvedTypeReferences());
34+
assertSame(0, typeContext.getUnresolvedTypeReferences().size());
35+
}
36+
37+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
https://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
21+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
22+
xsi:schemaLocation="
23+
http://ch.qos.logback/xml/ns/logback
24+
https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">
25+
26+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
27+
<encoder>
28+
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
29+
</encoder>
30+
</appender>
31+
32+
<root level="error">
33+
<appender-ref ref="STDOUT" />
34+
</root>
35+
36+
</configuration>

0 commit comments

Comments
 (0)