diff --git a/protocols/pom.xml b/protocols/pom.xml
index aef6e7a883..07c5fa1003 100644
--- a/protocols/pom.xml
+++ b/protocols/pom.xml
@@ -59,6 +59,7 @@
profinet
s7
simulated
+ slmp
socketcan
umas
diff --git a/protocols/slmp/pom.xml b/protocols/slmp/pom.xml
new file mode 100644
index 0000000000..91ebbc3b34
--- /dev/null
+++ b/protocols/slmp/pom.xml
@@ -0,0 +1,53 @@
+
+
+
+
+ 4.0.0
+
+
+ org.apache.plc4x
+ plc4x-protocols
+ 0.14.0-SNAPSHOT
+
+
+ plc4x-protocols-slmp
+
+ Protocols: SLMP
+ Base protocol specifications for the SLMP / MELSEC Communication protocol
+
+
+ 2025-08-02T13:55:11Z
+
+
+
+
+ org.apache.plc4x
+ plc4x-code-generation-protocol-base-mspec
+ 0.14.0-SNAPSHOT
+
+
+
+ ch.qos.logback
+ logback-classic
+ test
+
+
+
+
diff --git a/protocols/slmp/src/main/java/org/apache/plc4x/protocol/slmp/SlmpProtocol.java b/protocols/slmp/src/main/java/org/apache/plc4x/protocol/slmp/SlmpProtocol.java
new file mode 100644
index 0000000000..2c8959c98f
--- /dev/null
+++ b/protocols/slmp/src/main/java/org/apache/plc4x/protocol/slmp/SlmpProtocol.java
@@ -0,0 +1,42 @@
+/*
+ * 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
+ *
+ * https://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.
+ */
+package org.apache.plc4x.protocol.slmp;
+
+import org.apache.plc4x.plugins.codegenerator.language.mspec.parser.MessageFormatParser;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ProtocolHelpers;
+import org.apache.plc4x.plugins.codegenerator.language.mspec.protocol.ValidatableTypeContext;
+import org.apache.plc4x.plugins.codegenerator.protocol.Protocol;
+import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
+import org.apache.plc4x.plugins.codegenerator.types.exceptions.GenerationException;
+
+public class SlmpProtocol implements Protocol, ProtocolHelpers {
+
+ @Override
+ public String getName() {
+ return "slmp";
+ }
+
+ @Override
+ public TypeContext getTypeContext() throws GenerationException {
+ ValidatableTypeContext typeContext = new MessageFormatParser().parse(getMspecStream());
+ typeContext.validate();
+ return typeContext;
+ }
+
+}
diff --git a/protocols/slmp/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol b/protocols/slmp/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol
new file mode 100644
index 0000000000..2195c2e214
--- /dev/null
+++ b/protocols/slmp/src/main/resources/META-INF/services/org.apache.plc4x.plugins.codegenerator.protocol.Protocol
@@ -0,0 +1,19 @@
+#
+# 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
+#
+# https://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.
+#
+org.apache.plc4x.protocol.slmp.SlmpProtocol
diff --git a/protocols/slmp/src/main/resources/protocols/slmp/slmp.mspec b/protocols/slmp/src/main/resources/protocols/slmp/slmp.mspec
new file mode 100644
index 0000000000..20170095ae
--- /dev/null
+++ b/protocols/slmp/src/main/resources/protocols/slmp/slmp.mspec
@@ -0,0 +1,167 @@
+/*
+ * 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
+ *
+ * https://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.
+ */
+
+// SLMP (Seamless Message Protocol) / MELSEC Communication protocol.
+//
+// Specification: Mitsubishi "MELSEC Communication Protocol Reference Manual"
+// (SH(NA)-080008, public). All field layouts below are taken from that manual:
+// - 3E frame message format ....... section 5.2
+// - Subheader (50 00 / D0 00) ...... section 5.3
+// - Access route (3E fixed value) .. chapter 6
+// - Request/response data length ... section 5.3 (2-byte, little-endian)
+// - Commands (Batch/Random/block Read) chapter 7 / 8.1 / 8.3 / 8.4
+// - Device code list ............... section 8.1 (MELSEC-Q/L, 1-byte binary)
+// - Batch Read data layout ......... section 8.1 (binary, word units)
+// - Random Read data layout ........ section 8.3 (binary, word units)
+// - Multi-block Read data layout ... section 8.4 (binary, word units)
+//
+// Scope of this initial version: 3E binary frame, read-only, Batch Read
+// (command 0x0401), Random Read (command 0x0403) and Batch Read Multiple Blocks
+// (command 0x0406) in word units (subcommand 0x0000). This is the wire layer
+// only; typed value decoding
+// (INT/WORD/DINT/REAL) and the device-addressing tag layer are intentionally
+// NOT modelled here yet and will follow once the driver logic is built.
+// Validated hardware-free via the ParserSerializer test suite.
+//
+// All multi-byte numeric fields are little-endian (transmitted least-significant byte first).
+// The 2-byte subheader (0x50/0xD0 then 0x00) is modelled as two single bytes so
+// its on-wire order is preserved regardless of the frame byte order.
+
+[constants
+ // Typical SLMP TCP port; the actual port is configured on the device and is
+ // overridable by the driver, so this is only a default.
+ [const uint 16 slmpDefaultPort 5007]
+]
+
+// Device codes for MELSEC-Q/L series commands (subcommand 0x0000 / 0x0001),
+// 1-byte binary, from the device code list in SH-080008 section 8.1.
+// Only the word/bit devices needed by the read-only road-map are listed.
+//
+// These 1-byte binary device codes are confirmed identical in the Mitsubishi
+// MELSEC iQ-F FX5 SLMP manual (JY997D56001) -- D=A8 W=B4 R=AF M=90 X=9C Y=9D
+// B=A0 -- so the same frame works on FX5 hardware (e.g. FX5S) as well as
+// MELSEC-Q/L. (FX5 also offers a 2-byte/extended code form via subcommand
+// 0x0002/0x0003 for ZR and large device numbers, which is out of scope here.)
+[enum uint 8 SlmpDeviceCode
+ ['0xA8' D] // Data register (word, decimal addressing)
+ ['0xB4' W] // Link register (word, hex addressing)
+ ['0xAF' R] // File register (word, decimal addressing)
+ ['0xC2' TN] // Timer, current value (word, decimal addressing) -- used by the
+ // Random Read worked example in SH-080008 section 8.3
+ ['0x90' M] // Internal relay (bit, decimal addressing)
+ ['0x9C' X] // Input (bit, hex addressing)
+ ['0x9D' Y] // Output (bit, hex addressing)
+ ['0xA0' B] // Link relay (bit, hex addressing)
+]
+
+// 3E frame. The Ethernet/TCP header is added by the transport and is not part
+// of this message. Request and response are distinguished by the subheader byte
+// (0x50 request / 0xD0 response).
+[discriminatedType SlmpMessage byteOrder='"LITTLE_ENDIAN"' unsignedIntegerEncoding='"unsigned-binary"' signedIntegerEncoding='"twos-complement"' floatEncoding='"IEEE754"' stringEncoding='"UTF8"'
+ [discriminator uint 8 subHeader] // 0x50 request, 0xD0 response
+ [const uint 8 subHeaderReserved 0x00] // second subheader byte is always 0x00
+ [typeSwitch subHeader
+ ['0x50' SlmpRequestFrame3E
+ // Access route - fixed value for 3E frame (chapter 6): 00 FF FF 03 00
+ [const uint 8 network 0x00]
+ [const uint 8 pcNumber 0xFF]
+ [const uint 16 requestDestModuleIoNo 0x03FF]
+ [const uint 8 requestDestModuleStation 0x00]
+ // Number of bytes from the monitoring timer up to the end of the request data.
+ // = monitoringTimer(2) + command(2) + subCommand(2) + requestData
+ [implicit uint 16 requestDataLength '6 + requestData.lengthInBytes']
+ [simple uint 16 monitoringTimer] // 0x0000 = wait infinitely
+ [simple uint 16 command] // 0x0401 = Batch Read
+ [simple uint 16 subCommand] // 0x0000 = word units
+ [simple SlmpRequestData('command') requestData]
+ ]
+ ['0xD0' SlmpResponseFrame3E
+ [const uint 8 network 0x00]
+ [const uint 8 pcNumber 0xFF]
+ [const uint 16 requestDestModuleIoNo 0x03FF]
+ [const uint 8 requestDestModuleStation 0x00]
+ // Number of bytes from the end code up to the end of the response data.
+ // = endCode(2) + responseData
+ [implicit uint 16 responseDataLength '2 + COUNT(responseData)']
+ [simple uint 16 endCode] // 0x0000 = normal completion
+ // Raw payload. On normal completion this is the read words (little-endian,
+ // 2 bytes per point); on abnormal completion it carries error information.
+ // Typed decoding is left to the driver layer.
+ [array byte responseData count 'responseDataLength - 2']
+ ]
+ ]
+]
+
+// Request data, dispatched by command. Batch Read (0x0401) and Random Read
+// (0x0403) are modelled here; both are read-only, word units (subcommand 0x0000).
+[discriminatedType SlmpRequestData(uint 16 command)
+ [typeSwitch command
+ ['0x0401' SlmpReadRequest
+ // Binary, MELSEC-Q/L series: device number (3 bytes, little-endian)
+ // then device code (1 byte). See SH-080008 section 8.1.
+ [simple uint 24 headDeviceNumber]
+ [simple SlmpDeviceCode deviceCode]
+ // Number of points to read. In word units this is the number of 16-bit words.
+ [simple uint 16 numberOfPoints]
+ ]
+ ['0x0403' SlmpRandomReadRequest
+ // Random Read in word units (SH-080008 section 8.3): read discontinuous
+ // devices, given as a list of word-access points followed by a list of
+ // double-word-access points. The point counts are single bytes; each
+ // device is a 3-byte device number + 1-byte device code (SlmpDeviceSpec).
+ [simple uint 8 numberOfWordAccessPoints]
+ [simple uint 8 numberOfDoubleWordAccessPoints]
+ [array SlmpDeviceSpec wordAccessDevices count 'numberOfWordAccessPoints']
+ [array SlmpDeviceSpec doubleWordAccessDevices count 'numberOfDoubleWordAccessPoints']
+ ]
+ ['0x0406' SlmpMultiBlockReadRequest
+ // Batch Read multiple blocks in word units (SH-080008 section 8.4): read a
+ // number of word-device blocks followed by a number of bit-device blocks,
+ // where each block is a run of consecutive devices. The two block counts are
+ // single bytes; each block (SlmpDeviceBlock) is a 3-byte head device number,
+ // a 1-byte device code and a 2-byte number of points. Bit-device blocks are
+ // read in 16-bit word units (one point = 16 bits), so the wire layout of a
+ // bit-device block is identical to that of a word-device block.
+ [simple uint 8 numberOfWordDeviceBlocks]
+ [simple uint 8 numberOfBitDeviceBlocks]
+ [array SlmpDeviceBlock wordDeviceBlocks count 'numberOfWordDeviceBlocks']
+ [array SlmpDeviceBlock bitDeviceBlocks count 'numberOfBitDeviceBlocks']
+ ]
+ ]
+]
+
+// A single device specification used by commands that address discontinuous
+// devices (e.g. Random Read 0x0403): a device number (3 bytes, little-endian)
+// followed by the 1-byte device code. See SH-080008 section 8.1 / 8.3.
+[type SlmpDeviceSpec
+ [simple uint 24 deviceNumber]
+ [simple SlmpDeviceCode deviceCode]
+]
+
+// A single block of consecutive devices used by commands that read whole blocks
+// (e.g. Batch Read Multiple Blocks 0x0406): a head device number (3 bytes,
+// little-endian) and 1-byte device code identifying the start of the run,
+// followed by the 2-byte number of points (consecutive devices) in the block.
+// This is the same field layout as a single-block Batch Read request
+// (SlmpReadRequest). See SH-080008 section 8.4.
+[type SlmpDeviceBlock
+ [simple uint 24 headDeviceNumber]
+ [simple SlmpDeviceCode deviceCode]
+ [simple uint 16 numberOfPoints]
+]
diff --git a/protocols/slmp/src/test/java/org/apache/plc4x/protocol/slmp/SlmpProtocolTest.java b/protocols/slmp/src/test/java/org/apache/plc4x/protocol/slmp/SlmpProtocolTest.java
new file mode 100644
index 0000000000..06f0b490ec
--- /dev/null
+++ b/protocols/slmp/src/test/java/org/apache/plc4x/protocol/slmp/SlmpProtocolTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ *
+ * https://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.
+ */
+package org.apache.plc4x.protocol.slmp;
+
+import org.apache.plc4x.plugins.codegenerator.protocol.TypeContext;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+class SlmpProtocolTest {
+
+ @Test
+ void getTypeContext() throws Exception {
+ TypeContext typeContext = new SlmpProtocol().getTypeContext();
+ assertNotNull(typeContext);
+ assertNotNull(typeContext.getUnresolvedTypeReferences());
+ assertSame(0, typeContext.getUnresolvedTypeReferences().size());
+ }
+
+}
diff --git a/protocols/slmp/src/test/resources/logback-test.xml b/protocols/slmp/src/test/resources/logback-test.xml
new file mode 100644
index 0000000000..0524f110fc
--- /dev/null
+++ b/protocols/slmp/src/test/resources/logback-test.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
diff --git a/protocols/slmp/src/test/resources/protocols/slmp/ParserSerializerTestsuite.xml b/protocols/slmp/src/test/resources/protocols/slmp/ParserSerializerTestsuite.xml
new file mode 100644
index 0000000000..9c332d509c
--- /dev/null
+++ b/protocols/slmp/src/test/resources/protocols/slmp/ParserSerializerTestsuite.xml
@@ -0,0 +1,253 @@
+
+
+
+
+
+ SLMP
+
+ slmp
+ read-write
+
+
+ 3E Batch Read Request - D350, 2 words
+ 500000ffff03000c000000010400005e0100a80200
+ SlmpMessage
+
+
+ 80
+ 0
+
+ 0
+ 255
+ 1023
+ 0
+ 12
+ 0
+ 1025
+ 0
+
+
+
+ 350
+ D
+ 2
+
+
+
+
+
+
+
+
+
+ 3E Batch Read Response - D350=0x56AB, D351=0x170F
+ d00000ffff030006000000ab560f17
+ SlmpMessage
+
+
+ 208
+ 0
+
+ 0
+ 255
+ 1023
+ 0
+ 6
+ 0
+ 0xab560f17
+
+
+
+
+
+
+ 3E Random Read Request - D0,T0,M100,X20 word + D1500,Y160,M1111 dword
+ 500000ffff030024000000030400000403000000a8000000c2640000902000009cdc0500a86001009d57040090
+ SlmpMessage
+
+
+ 80
+ 0
+
+ 0
+ 255
+ 1023
+ 0
+ 36
+ 0
+ 1027
+ 0
+
+
+
+ 4
+ 3
+
+
+ 0
+ D
+
+
+ 0
+ TN
+
+
+ 100
+ M
+
+
+ 32
+ X
+
+
+
+
+ 1500
+ D
+
+
+ 352
+ Y
+
+
+ 1111
+ M
+
+
+
+
+
+
+
+
+
+
+
+ 3E Random Read Response - 4 word + 3 dword values
+ d00000ffff03001600000095190212302049484e4f544cafb9dec3b7bcddba
+ SlmpMessage
+
+
+ 208
+ 0
+
+ 0
+ 255
+ 1023
+ 0
+ 22
+ 0
+ 0x95190212302049484e4f544cafb9dec3b7bcddba
+
+
+
+
+
+
+ 3E Multi-block Read Request - 2 word blocks (D0-D3, W100-W107) + 3 bit blocks (M0-M31, M128-M159, B100-B12F)
+ 500000ffff030026000000060400000203000000a80400000100b40800000000900200800000900200000100a00300
+ SlmpMessage
+
+
+ 80
+ 0
+
+ 0
+ 255
+ 1023
+ 0
+ 38
+ 0
+ 1030
+ 0
+
+
+
+ 2
+ 3
+
+
+ 0
+ D
+ 4
+
+
+ 256
+ W
+ 8
+
+
+
+
+ 0
+ M
+ 2
+
+
+ 128
+ M
+ 2
+
+
+ 256
+ B
+ 3
+
+
+
+
+
+
+
+
+
+
+