From 9b4fdac9fff7a6c5978af66e2ed24bf3faa7e1a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Dywicki?= Date: Sat, 23 Mar 2024 19:46:45 +0100 Subject: [PATCH] feat(plc4x/modbus): Add support for unit-id option for modbus tags. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce support for tag config at the tag level. Closes #1234. Signed-off-by: Ɓukasz Dywicki --- .../all/src/site/generated/modbus-ascii.adoc | 2 +- .../all/src/site/generated/modbus-rtu.adoc | 2 +- .../all/src/site/generated/modbus-tcp.adoc | 2 +- .../config/ModbusAsciiConfiguration.java | 14 ++--- .../protocol/ModbusAsciiProtocolLogic.java | 11 ++-- .../base/protocol/ModbusProtocolLogic.java | 13 ++-- .../plc4x/java/modbus/base/tag/ModbusTag.java | 23 +++++-- .../java/modbus/base/tag/ModbusTagCoil.java | 14 +++-- .../base/tag/ModbusTagDiscreteInput.java | 14 +++-- .../base/tag/ModbusTagExtendedRegister.java | 14 +++-- .../base/tag/ModbusTagHoldingRegister.java | 14 +++-- .../base/tag/ModbusTagInputRegister.java | 14 +++-- .../rtu/config/ModbusRtuConfiguration.java | 14 ++--- .../rtu/protocol/ModbusRtuProtocolLogic.java | 11 ++-- .../tcp/config/ModbusTcpConfiguration.java | 14 ++--- .../tcp/protocol/ModbusTcpProtocolLogic.java | 15 +++-- .../plc4x/java/modbus/ModbusEncodeTest.java | 3 +- .../plc4x/java/spi/tag/TagConfigParser.java | 60 +++++++++++++++++++ .../java/spi/tag/TagTagConfigParserTest.java | 59 ++++++++++++++++++ src/site/asciidoc/users/protocols/modbus.adoc | 14 +++++ 20 files changed, 250 insertions(+), 77 deletions(-) create mode 100644 plc4j/spi/src/main/java/org/apache/plc4x/java/spi/tag/TagConfigParser.java create mode 100644 plc4j/spi/src/test/java/org/apache/plc4x/java/spi/tag/TagTagConfigParserTest.java diff --git a/plc4j/drivers/all/src/site/generated/modbus-ascii.adoc b/plc4j/drivers/all/src/site/generated/modbus-ascii.adoc index 183e829cd6d..5828f74ad3c 100644 --- a/plc4j/drivers/all/src/site/generated/modbus-ascii.adoc +++ b/plc4j/drivers/all/src/site/generated/modbus-ascii.adoc @@ -38,7 +38,7 @@ - `serial` 5+|Config options: |`request-timeout` |INT |5000| |Default timeout for all types of requests. -|`unit-identifier` |INT |1| |Unit-identifier that identifies the target PLC (On RS485 multiple Modbus Devices can be listening). Defaults to 1. +|`default-unit-identifier` |STRUCT |1| |Unit-identifier or slave-id that identifies the target PLC (On RS485 multiple Modbus Devices can be listening). Defaults to 1. 5+|Transport config options: 5+| +++ diff --git a/plc4j/drivers/all/src/site/generated/modbus-rtu.adoc b/plc4j/drivers/all/src/site/generated/modbus-rtu.adoc index 624f56a4a9e..0932a4bb6e4 100644 --- a/plc4j/drivers/all/src/site/generated/modbus-rtu.adoc +++ b/plc4j/drivers/all/src/site/generated/modbus-rtu.adoc @@ -38,7 +38,7 @@ - `serial` 5+|Config options: |`request-timeout` |INT |5000| |Default timeout for all types of requests. -|`unit-identifier` |INT |1| |Unit-identifier that identifies the target PLC (On RS485 multiple Modbus Devices can be listening). Defaults to 1. +|`default-unit-identifier` |INT |1| |Unit-identifier or slave-id that identifies the target PLC (On RS485 multiple Modbus Devices can be listening). Defaults to 1. 5+|Transport config options: 5+| +++ diff --git a/plc4j/drivers/all/src/site/generated/modbus-tcp.adoc b/plc4j/drivers/all/src/site/generated/modbus-tcp.adoc index 82fa5ea2846..c8cbfb62cd4 100644 --- a/plc4j/drivers/all/src/site/generated/modbus-tcp.adoc +++ b/plc4j/drivers/all/src/site/generated/modbus-tcp.adoc @@ -37,7 +37,7 @@ - `tcp` 5+|Config options: |`request-timeout` |INT |5000| |Default timeout for all types of requests. -|`unit-identifier` |INT |1| |Unit-identifier that identifies the target PLC (On RS485 multiple Modbus Devices can be listening). Defaults to 1. +|`default-unit-identifier` |INT |1| |Unit-identifier or slave-id that identifies the target PLC (On RS485 multiple Modbus Devices can be listening). Defaults to 1. |`ping-address` |STRING |4x00001:BOOL| |Simple address, that the driver will use to check, if the connection to a given device is active (Defaults to reading holding-register 1). 5+|Transport config options: 5+| diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ascii/config/ModbusAsciiConfiguration.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ascii/config/ModbusAsciiConfiguration.java index 47562dc16e6..662a63f4100 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ascii/config/ModbusAsciiConfiguration.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ascii/config/ModbusAsciiConfiguration.java @@ -30,10 +30,10 @@ public class ModbusAsciiConfiguration implements PlcConnectionConfiguration { @Description("Default timeout for all types of requests.") private int requestTimeout; - @ConfigurationParameter("unit-identifier") + @ConfigurationParameter("default-unit-identifier") @IntDefaultValue(1) @Description("Unit-identifier or slave-id that identifies the target PLC (On RS485 multiple Modbus Devices can be listening). Defaults to 1.") - private int unitIdentifier; + private short defaultUnitIdentifier; public int getRequestTimeout() { return requestTimeout; @@ -43,19 +43,19 @@ public void setRequestTimeout(int requestTimeout) { this.requestTimeout = requestTimeout; } - public int getUnitIdentifier() { - return unitIdentifier; + public short getDefaultUnitIdentifier() { + return defaultUnitIdentifier; } - public void setUnitIdentifier(int unitIdentifier) { - this.unitIdentifier = unitIdentifier; + public void setDefaultUnitIdentifier(short defaultUnitIdentifier) { + this.defaultUnitIdentifier = defaultUnitIdentifier; } @Override public String toString() { return "ModbusAsciiConfiguration{" + "requestTimeout=" + requestTimeout + - ", unitIdentifier=" + unitIdentifier + + ", defaultUnitIdentifier=" + defaultUnitIdentifier + '}'; } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ascii/protocol/ModbusAsciiProtocolLogic.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ascii/protocol/ModbusAsciiProtocolLogic.java index f00970f2be8..c8a1ac023af 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ascii/protocol/ModbusAsciiProtocolLogic.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/ascii/protocol/ModbusAsciiProtocolLogic.java @@ -47,7 +47,7 @@ public ModbusAsciiProtocolLogic() { @Override public void setConfiguration(ModbusAsciiConfiguration configuration) { this.requestTimeout = Duration.ofMillis(configuration.getRequestTimeout()); - this.unitIdentifier = (short) configuration.getUnitIdentifier(); + this.unitIdentifier = configuration.getDefaultUnitIdentifier(); this.tm = new RequestTransactionManager(1); } @@ -64,7 +64,8 @@ public CompletableFuture ping(PlcPingRequest pingRequest) { // So we fall back to a request, that most certainly is implemented by any device. Even if the device doesn't // have any holding-register:1, it should still gracefully respond. ModbusPDU readRequestPdu = getReadRequestPdu(pingAddress); - ModbusAsciiADU modbusTcpADU = new ModbusAsciiADU(unitIdentifier, readRequestPdu); + short unitId = getUnitId(pingAddress); + ModbusAsciiADU modbusTcpADU = new ModbusAsciiADU(unitId, readRequestPdu); RequestTransactionManager.RequestTransaction transaction = tm.startRequest(); transaction.submit(() -> context.sendRequest(modbusTcpADU) @@ -99,8 +100,9 @@ public CompletableFuture read(PlcReadRequest readRequest) { String tagName = request.getTagNames().iterator().next(); ModbusTag tag = (ModbusTag) request.getTag(tagName); final ModbusPDU requestPdu = getReadRequestPdu(tag); + final short unitId = getUnitId(tag); - ModbusAsciiADU modbusAsciiADU = new ModbusAsciiADU(unitIdentifier, requestPdu); + ModbusAsciiADU modbusAsciiADU = new ModbusAsciiADU(unitId, requestPdu); RequestTransactionManager.RequestTransaction transaction = tm.startRequest(); transaction.submit(() -> context.sendRequest(modbusAsciiADU) .expectResponse(ModbusAsciiADU.class, requestTimeout) @@ -158,7 +160,8 @@ public CompletableFuture write(PlcWriteRequest writeRequest) { String tagName = request.getTagNames().iterator().next(); PlcTag tag = request.getTag(tagName); final ModbusPDU requestPdu = getWriteRequestPdu(tag, writeRequest.getPlcValue(tagName)); - ModbusAsciiADU modbusAsciiADU = new ModbusAsciiADU(unitIdentifier, requestPdu); + final short unitId = getUnitId(tag); + ModbusAsciiADU modbusAsciiADU = new ModbusAsciiADU(unitId, requestPdu); RequestTransactionManager.RequestTransaction transaction = tm.startRequest(); transaction.submit(() -> context.sendRequest(modbusAsciiADU) .expectResponse(ModbusAsciiADU.class, requestTimeout) diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/protocol/ModbusProtocolLogic.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/protocol/ModbusProtocolLogic.java index 7b62f03b8e4..2d14bcd1387 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/protocol/ModbusProtocolLogic.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/protocol/ModbusProtocolLogic.java @@ -19,9 +19,6 @@ package org.apache.plc4x.java.modbus.base.protocol; import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; -import org.apache.plc4x.java.api.messages.PlcPingRequest; -import org.apache.plc4x.java.api.messages.PlcPingResponse; -import org.apache.plc4x.java.api.messages.PlcReadResponse; import org.apache.plc4x.java.api.model.PlcTag; import org.apache.plc4x.java.api.value.*; import org.apache.plc4x.java.api.types.PlcResponseCode; @@ -40,7 +37,6 @@ import java.util.BitSet; import java.util.Collections; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; public abstract class ModbusProtocolLogic extends Plc4xProtocolBase { @@ -97,6 +93,15 @@ protected PlcResponseCode getErrorCode(ModbusPDUError errorResponse) { } } + protected short getUnitId(PlcTag tag) { + if (tag instanceof ModbusTag) { + Short unitId = ((ModbusTag) tag).getUnitId(); + return unitId == null ? unitIdentifier : unitId; + } + + return unitIdentifier; + } + protected ModbusPDU getReadRequestPdu(PlcTag tag) { if (tag instanceof ModbusTagDiscreteInput) { ModbusTagDiscreteInput discreteInput = (ModbusTagDiscreteInput) tag; diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTag.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTag.java index 99270f9d3eb..1b4cc0c29f2 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTag.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTag.java @@ -30,9 +30,7 @@ import org.apache.plc4x.java.spi.utils.Serializable; import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.regex.Pattern; public abstract class ModbusTag implements PlcTag, Serializable { @@ -47,6 +45,7 @@ public abstract class ModbusTag implements PlcTag, Serializable { private final int quantity; private final ModbusDataType dataType; + private final Short unitId; public static ModbusTag of(String addressString) { if (ModbusTagCoil.matches(addressString)) { @@ -88,6 +87,10 @@ public String getAddressString() { * @param dataType The type for the interpretation of the registers. */ protected ModbusTag(int address, Integer quantity, ModbusDataType dataType) { + this(address, quantity, dataType, new HashMap<>()); + } + + protected ModbusTag(int address, Integer quantity, ModbusDataType dataType, Map config) { this.address = address; if (getLogicalAddress() <= 0) { throw new IllegalArgumentException("address must be greater than zero. Was " + getLogicalAddress()); @@ -97,6 +100,9 @@ protected ModbusTag(int address, Integer quantity, ModbusDataType dataType) { throw new IllegalArgumentException("quantity must be greater than zero. Was " + this.quantity); } this.dataType = dataType != null ? dataType : ModbusDataType.INT; + this.unitId = Optional.ofNullable(config.get("unit-id")) + .map(Short::parseShort) + .orElse(null); } /** @@ -107,6 +113,10 @@ public int getAddress() { return address; } + public Short getUnitId() { + return unitId; + } + /** * Get the logical (configured) address * @return The address which was configured and is different from what is used on the wire. @@ -154,12 +164,13 @@ public boolean equals(Object o) { return address == that.address && quantity == that.quantity && dataType == that.dataType && + unitId == that.unitId && getClass() == that.getClass(); // MUST be identical } @Override public int hashCode() { - return Objects.hash(this.getClass(), address, quantity, dataType); + return Objects.hash(this.getClass(), address, quantity, dataType, unitId); } @Override @@ -168,6 +179,7 @@ public String toString() { "address=" + address + ", quantity=" + quantity + ", dataType=" + dataType + + ", unitId=" + unitId + " }"; } @@ -182,6 +194,9 @@ public void serialize(WriteBuffer writeBuffer) throws SerializationException { dataType.getBytes(StandardCharsets.UTF_8).length * 8, dataType, WithOption.WithEncoding(StandardCharsets.UTF_8.name())); + if (unitId != null) { + writeBuffer.writeUnsignedInt("unitId", 8, unitId); + } writeBuffer.popContext(getClass().getSimpleName()); } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagCoil.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagCoil.java index 95e4e7c409a..ca3aae25d9f 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagCoil.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagCoil.java @@ -20,21 +20,23 @@ import org.apache.plc4x.java.api.exceptions.PlcInvalidTagException; import org.apache.plc4x.java.modbus.readwrite.ModbusDataType; +import org.apache.plc4x.java.spi.tag.TagConfigParser; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ModbusTagCoil extends ModbusTag { public static final String ADDRESS_PREFIX = "0x"; - public static final Pattern ADDRESS_PATTERN = Pattern.compile("coil:" + ModbusTag.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("0" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("0x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("coil:" + ModbusTag.ADDRESS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("0" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("0x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); protected static final int REGISTER_MAXADDRESS = 65535; - public ModbusTagCoil(int address, Integer quantity, ModbusDataType dataType) { - super(address, quantity, dataType); + public ModbusTagCoil(int address, Integer quantity, ModbusDataType dataType, Map config) { + super(address, quantity, dataType, config); } protected String getAddressStringPrefix() { @@ -88,7 +90,7 @@ public static ModbusTagCoil of(String addressString) { ModbusDataType dataType = (matcher.group("datatype") != null) ? ModbusDataType.valueOf(matcher.group("datatype")) : ModbusDataType.BOOL; - return new ModbusTagCoil(address, quantity, dataType); + return new ModbusTagCoil(address, quantity, dataType, TagConfigParser.parse(addressString)); } } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagDiscreteInput.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagDiscreteInput.java index 5f44e872994..d818f874535 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagDiscreteInput.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagDiscreteInput.java @@ -20,21 +20,23 @@ import org.apache.plc4x.java.api.exceptions.PlcInvalidTagException; import org.apache.plc4x.java.modbus.readwrite.ModbusDataType; +import org.apache.plc4x.java.spi.tag.TagConfigParser; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ModbusTagDiscreteInput extends ModbusTag { public static final String ADDRESS_PREFIX = "1x"; - public static final Pattern ADDRESS_PATTERN = Pattern.compile("discrete-input:" + ModbusTag.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("1" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("1x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("discrete-input:" + ModbusTag.ADDRESS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("1" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("1x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); protected static final int REGISTER_MAX_ADDRESS = 65535; - public ModbusTagDiscreteInput(int address, Integer quantity, ModbusDataType dataType) { - super(address, quantity, dataType); + public ModbusTagDiscreteInput(int address, Integer quantity, ModbusDataType dataType, Map config) { + super(address, quantity, dataType, config); } protected String getAddressStringPrefix() { @@ -88,6 +90,6 @@ public static ModbusTagDiscreteInput of(String addressString) { ModbusDataType dataType = (matcher.group("datatype") != null) ? ModbusDataType.valueOf(matcher.group("datatype")) : ModbusDataType.BOOL; - return new ModbusTagDiscreteInput(address, quantity, dataType); + return new ModbusTagDiscreteInput(address, quantity, dataType, TagConfigParser.parse(addressString)); } } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagExtendedRegister.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagExtendedRegister.java index e9abdfba4b2..b515b53c08c 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagExtendedRegister.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagExtendedRegister.java @@ -20,21 +20,23 @@ import org.apache.plc4x.java.api.exceptions.PlcInvalidTagException; import org.apache.plc4x.java.modbus.readwrite.ModbusDataType; +import org.apache.plc4x.java.spi.tag.TagConfigParser; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ModbusTagExtendedRegister extends ModbusTag { public static final String ADDRESS_PREFIX = "6x"; - public static final Pattern ADDRESS_PATTERN = Pattern.compile("extended-register:" + ModbusTag.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("6" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("6x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("extended-register:" + ModbusTag.ADDRESS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("6" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("6x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); protected static final int REGISTER_MAXADDRESS = 655359999; - protected ModbusTagExtendedRegister(int address, Integer quantity, ModbusDataType dataType) { - super(address, quantity, dataType); + protected ModbusTagExtendedRegister(int address, Integer quantity, ModbusDataType dataType, Map config) { + super(address, quantity, dataType, config); } protected String getAddressStringPrefix() { @@ -89,6 +91,6 @@ public static ModbusTagExtendedRegister of(String addressString) { ModbusDataType dataType = (matcher.group("datatype") != null) ? ModbusDataType.valueOf(matcher.group("datatype")) : ModbusDataType.INT; - return new ModbusTagExtendedRegister(address, quantity, dataType); + return new ModbusTagExtendedRegister(address, quantity, dataType, TagConfigParser.parse(addressString)); } } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagHoldingRegister.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagHoldingRegister.java index 900d315c39f..c548d54eb90 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagHoldingRegister.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagHoldingRegister.java @@ -20,21 +20,23 @@ import org.apache.plc4x.java.api.exceptions.PlcInvalidTagException; import org.apache.plc4x.java.modbus.readwrite.ModbusDataType; +import org.apache.plc4x.java.spi.tag.TagConfigParser; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ModbusTagHoldingRegister extends ModbusTag { public static final String ADDRESS_PREFIX = "4x"; - public static final Pattern ADDRESS_PATTERN = Pattern.compile("holding-register:" + ModbusTag.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("4" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("4x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("holding-register:" + ModbusTag.ADDRESS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("4" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("4x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); protected static final int REGISTER_MAXADDRESS = 65535; - protected ModbusTagHoldingRegister(int address, Integer quantity, ModbusDataType dataType) { - super(address, quantity, dataType); + protected ModbusTagHoldingRegister(int address, Integer quantity, ModbusDataType dataType, Map config) { + super(address, quantity, dataType, config); } protected String getAddressStringPrefix() { @@ -87,7 +89,7 @@ public static ModbusTagHoldingRegister of(String addressString) { ModbusDataType dataType = (matcher.group("datatype") != null) ? ModbusDataType.valueOf(matcher.group("datatype")) : ModbusDataType.INT; - return new ModbusTagHoldingRegister(address, quantity, dataType); + return new ModbusTagHoldingRegister(address, quantity, dataType, TagConfigParser.parse(addressString)); } } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagInputRegister.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagInputRegister.java index aa7e338b629..8f027e65665 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagInputRegister.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/tag/ModbusTagInputRegister.java @@ -20,21 +20,23 @@ import org.apache.plc4x.java.api.exceptions.PlcInvalidTagException; import org.apache.plc4x.java.modbus.readwrite.ModbusDataType; +import org.apache.plc4x.java.spi.tag.TagConfigParser; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ModbusTagInputRegister extends ModbusTag { public static final String ADDRESS_PREFIX = "3x"; - public static final Pattern ADDRESS_PATTERN = Pattern.compile("input-register:" + ModbusTag.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("3" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("3x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("input-register:" + ModbusTag.ADDRESS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("3" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("3x" + ModbusTag.FIXED_DIGIT_MODBUS_PATTERN + TagConfigParser.TAG_CONFIG_PATTERN); protected static final int REGISTER_MAXADDRESS = 65535; - protected ModbusTagInputRegister(int address, Integer quantity, ModbusDataType dataType) { - super(address, quantity, dataType); + protected ModbusTagInputRegister(int address, Integer quantity, ModbusDataType dataType, Map config) { + super(address, quantity, dataType, config); } protected String getAddressStringPrefix() { @@ -87,6 +89,6 @@ public static ModbusTagInputRegister of(String addressString) { ModbusDataType dataType = (matcher.group("datatype") != null) ? ModbusDataType.valueOf(matcher.group("datatype")) : ModbusDataType.INT; - return new ModbusTagInputRegister(address, quantity, dataType); + return new ModbusTagInputRegister(address, quantity, dataType, TagConfigParser.parse(addressString)); } } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/rtu/config/ModbusRtuConfiguration.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/rtu/config/ModbusRtuConfiguration.java index 90b9b543013..43952abab42 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/rtu/config/ModbusRtuConfiguration.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/rtu/config/ModbusRtuConfiguration.java @@ -30,10 +30,10 @@ public class ModbusRtuConfiguration implements PlcConnectionConfiguration { @Description("Default timeout for all types of requests.") private int requestTimeout; - @ConfigurationParameter("unit-identifier") + @ConfigurationParameter("default-unit-identifier") @IntDefaultValue(1) @Description("Unit-identifier or slave-id that identifies the target PLC (On RS485 multiple Modbus Devices can be listening). Defaults to 1.") - private int unitIdentifier; + private int defaultUnitIdentifier; public int getRequestTimeout() { return requestTimeout; @@ -43,19 +43,19 @@ public void setRequestTimeout(int requestTimeout) { this.requestTimeout = requestTimeout; } - public int getUnitIdentifier() { - return unitIdentifier; + public int getDefaultUnitIdentifier() { + return defaultUnitIdentifier; } - public void setUnitIdentifier(int unitIdentifier) { - this.unitIdentifier = unitIdentifier; + public void setDefaultUnitIdentifier(int defaultUnitIdentifier) { + this.defaultUnitIdentifier = defaultUnitIdentifier; } @Override public String toString() { return "ModbusRtuConfiguration{" + "requestTimeout=" + requestTimeout + - ", unitIdentifier=" + unitIdentifier + + ", unitIdentifier=" + defaultUnitIdentifier + '}'; } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/rtu/protocol/ModbusRtuProtocolLogic.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/rtu/protocol/ModbusRtuProtocolLogic.java index e6812527ac8..9785eea345b 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/rtu/protocol/ModbusRtuProtocolLogic.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/rtu/protocol/ModbusRtuProtocolLogic.java @@ -47,7 +47,7 @@ public ModbusRtuProtocolLogic() { @Override public void setConfiguration(ModbusRtuConfiguration configuration) { this.requestTimeout = Duration.ofMillis(configuration.getRequestTimeout()); - this.unitIdentifier = (short) configuration.getUnitIdentifier(); + this.unitIdentifier = (short) configuration.getDefaultUnitIdentifier(); this.tm = new RequestTransactionManager(1); } @@ -64,7 +64,8 @@ public CompletableFuture ping(PlcPingRequest pingRequest) { // So we fall back to a request, that most certainly is implemented by any device. Even if the device doesn't // have any holding-register:1, it should still gracefully respond. ModbusPDU readRequestPdu = getReadRequestPdu(pingAddress); - ModbusRtuADU modbusRtuADU = new ModbusRtuADU(unitIdentifier, readRequestPdu); + final short unitId = getUnitId(pingAddress); + ModbusRtuADU modbusRtuADU = new ModbusRtuADU(unitId, readRequestPdu); RequestTransactionManager.RequestTransaction transaction = tm.startRequest(); transaction.submit(() -> context.sendRequest(modbusRtuADU) @@ -99,8 +100,9 @@ public CompletableFuture read(PlcReadRequest readRequest) { String tagName = request.getTagNames().iterator().next(); ModbusTag tag = (ModbusTag) request.getTag(tagName); final ModbusPDU requestPdu = getReadRequestPdu(tag); + final short unitId = getUnitId(tag); - ModbusRtuADU modbusRtuADU = new ModbusRtuADU(unitIdentifier, requestPdu); + ModbusRtuADU modbusRtuADU = new ModbusRtuADU(unitId, requestPdu); RequestTransactionManager.RequestTransaction transaction = tm.startRequest(); transaction.submit(() -> context.sendRequest(modbusRtuADU) .expectResponse(ModbusRtuADU.class, requestTimeout) @@ -158,7 +160,8 @@ public CompletableFuture write(PlcWriteRequest writeRequest) { String tagName = request.getTagNames().iterator().next(); PlcTag tag = request.getTag(tagName); final ModbusPDU requestPdu = getWriteRequestPdu(tag, writeRequest.getPlcValue(tagName)); - ModbusRtuADU modbusRtuADU = new ModbusRtuADU(unitIdentifier, requestPdu); + final short unitId = getUnitId(tag); + ModbusRtuADU modbusRtuADU = new ModbusRtuADU(unitId, requestPdu); RequestTransactionManager.RequestTransaction transaction = tm.startRequest(); transaction.submit(() -> context.sendRequest(modbusRtuADU) .expectResponse(ModbusRtuADU.class, requestTimeout) diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/tcp/config/ModbusTcpConfiguration.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/tcp/config/ModbusTcpConfiguration.java index e87b3e06f1b..7ecd6672be7 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/tcp/config/ModbusTcpConfiguration.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/tcp/config/ModbusTcpConfiguration.java @@ -31,10 +31,10 @@ public class ModbusTcpConfiguration implements PlcConnectionConfiguration { @Description("Default timeout for all types of requests.") private int requestTimeout; - @ConfigurationParameter("unit-identifier") + @ConfigurationParameter("default-unit-identifier") @IntDefaultValue(1) @Description("Unit-identifier or slave-id that identifies the target PLC (On RS485 multiple Modbus Devices can be listening). Defaults to 1.") - private int unitIdentifier; + private int defaultUnitIdentifier; @ConfigurationParameter("ping-address") @StringDefaultValue("4x00001:BOOL") @@ -49,12 +49,12 @@ public void setRequestTimeout(int requestTimeout) { this.requestTimeout = requestTimeout; } - public int getUnitIdentifier() { - return unitIdentifier; + public int getDefaultUnitIdentifier() { + return defaultUnitIdentifier; } - public void setUnitIdentifier(int unitIdentifier) { - this.unitIdentifier = unitIdentifier; + public void setDefaultUnitIdentifier(int defaultUnitIdentifier) { + this.defaultUnitIdentifier = defaultUnitIdentifier; } public String getPingAddress() { @@ -65,7 +65,7 @@ public String getPingAddress() { public String toString() { return "ModbusTcpConfiguration{" + "requestTimeout=" + requestTimeout + - ", unitIdentifier=" + unitIdentifier + + ", unitIdentifier=" + defaultUnitIdentifier + ", pingAddress=" + pingAddress + '}'; } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/tcp/protocol/ModbusTcpProtocolLogic.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/tcp/protocol/ModbusTcpProtocolLogic.java index 53710f51773..39d035c87f0 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/tcp/protocol/ModbusTcpProtocolLogic.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/tcp/protocol/ModbusTcpProtocolLogic.java @@ -48,7 +48,7 @@ public ModbusTcpProtocolLogic() { @Override public void setConfiguration(ModbusTcpConfiguration configuration) { this.requestTimeout = Duration.ofMillis(configuration.getRequestTimeout()); - this.unitIdentifier = (short) configuration.getUnitIdentifier(); + this.unitIdentifier = (short) configuration.getDefaultUnitIdentifier(); this.pingAddress = new ModbusTagHandler().parseTag(configuration.getPingAddress()); this.tm = new RequestTransactionManager(1); } @@ -66,12 +66,13 @@ public CompletableFuture ping(PlcPingRequest pingRequest) { // So we fall back to a request, that most certainly is implemented by any device. Even if the device doesn't // have any holding-register:1, it should still gracefully respond. ModbusPDU readRequestPdu = getReadRequestPdu(pingAddress); + final short unitId = getUnitId(pingAddress); int transactionIdentifier = transactionIdentifierGenerator.getAndIncrement(); // If we've reached the max value for a 16 bit transaction identifier, reset back to 1 if (transactionIdentifierGenerator.get() == 0xFFFF) { transactionIdentifierGenerator.set(1); } - ModbusTcpADU modbusTcpADU = new ModbusTcpADU(transactionIdentifier, unitIdentifier, readRequestPdu); + ModbusTcpADU modbusTcpADU = new ModbusTcpADU(transactionIdentifier, unitId, readRequestPdu); RequestTransactionManager.RequestTransaction transaction = tm.startRequest(); transaction.submit(() -> context.sendRequest(modbusTcpADU) @@ -79,7 +80,7 @@ public CompletableFuture ping(PlcPingRequest pingRequest) { .onTimeout(future::completeExceptionally) .onError((p, e) -> future.completeExceptionally(e)) .check(p -> ((p.getTransactionIdentifier() == transactionIdentifier) && - (p.getUnitIdentifier() == unitIdentifier))) + (p.getUnitIdentifier() == unitId))) .unwrap(ModbusTcpADU::getPdu) .handle(responsePdu -> { transaction.endRequest(); @@ -108,20 +109,21 @@ public CompletableFuture read(PlcReadRequest readRequest) { String tagName = request.getTagNames().iterator().next(); ModbusTag tag = (ModbusTag) request.getTag(tagName); final ModbusPDU requestPdu = getReadRequestPdu(tag); + final short unitId = getUnitId(tag); int transactionIdentifier = transactionIdentifierGenerator.getAndIncrement(); // If we've reached the max value for a 16 bit transaction identifier, reset back to 1 if (transactionIdentifierGenerator.get() == 0xFFFF) { transactionIdentifierGenerator.set(1); } - ModbusTcpADU modbusTcpADU = new ModbusTcpADU(transactionIdentifier, unitIdentifier, requestPdu); + ModbusTcpADU modbusTcpADU = new ModbusTcpADU(transactionIdentifier, unitId, requestPdu); RequestTransactionManager.RequestTransaction transaction = tm.startRequest(); transaction.submit(() -> context.sendRequest(modbusTcpADU) .expectResponse(ModbusTcpADU.class, requestTimeout) .onTimeout(future::completeExceptionally) .onError((p, e) -> future.completeExceptionally(e)) .check(p -> ((p.getTransactionIdentifier() == transactionIdentifier) && - (p.getUnitIdentifier() == unitIdentifier))) + (p.getUnitIdentifier() == unitId))) .unwrap(ModbusTcpADU::getPdu) .handle(responsePdu -> { // Try to decode the response data based on the corresponding request. @@ -174,12 +176,13 @@ public CompletableFuture write(PlcWriteRequest writeRequest) { String tagName = request.getTagNames().iterator().next(); PlcTag tag = request.getTag(tagName); final ModbusPDU requestPdu = getWriteRequestPdu(tag, writeRequest.getPlcValue(tagName)); + final short unitId = getUnitId(tag); int transactionIdentifier = transactionIdentifierGenerator.getAndIncrement(); // If we've reached the max value for a 16 bit transaction identifier, reset back to 1 if (transactionIdentifierGenerator.get() == 0xFFFF) { transactionIdentifierGenerator.set(1); } - ModbusTcpADU modbusTcpADU = new ModbusTcpADU(transactionIdentifier, unitIdentifier, requestPdu); + ModbusTcpADU modbusTcpADU = new ModbusTcpADU(transactionIdentifier, unitId, requestPdu); RequestTransactionManager.RequestTransaction transaction = tm.startRequest(); transaction.submit(() -> context.sendRequest(modbusTcpADU) .expectResponse(ModbusTcpADU.class, requestTimeout) diff --git a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ModbusEncodeTest.java b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ModbusEncodeTest.java index 94adac3cceb..a88d37e3ec4 100644 --- a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ModbusEncodeTest.java +++ b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ModbusEncodeTest.java @@ -40,7 +40,8 @@ public void testEncodeBooleanBOOL() { @Test public void testEncodeIntegerSINT() { Integer[] object = {1,-1,127,-128,5,6,7,8}; - ModbusTagHoldingRegister holdingregister = ModbusTagHoldingRegister.of("holding-register:8:SINT"); + ModbusTagHoldingRegister holdingregister = ModbusTagHoldingRegister.of("holding-register:8:SINT{unit-id: 10}"); + Assertions.assertEquals((short) 10, holdingregister.getUnitId()); PlcList list = (PlcList) PlcValueHandler.of(holdingregister, object); Assertions.assertEquals("[1,-1,127,-128,5,6,7,8]", list.toString()); } diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/tag/TagConfigParser.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/tag/TagConfigParser.java new file mode 100644 index 00000000000..fca77a4b540 --- /dev/null +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/tag/TagConfigParser.java @@ -0,0 +1,60 @@ +/* + * 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. + */ + +package org.apache.plc4x.java.spi.tag; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Dedicated parser which parses config portion of tag address. + * + * Tag config comes in curly braces and follows json syntax with key value pairs (fields are not quoted) + */ +public class TagConfigParser { + + public static final Pattern TAG_CONFIG_PATTERN = Pattern.compile("(\\{(?.*?)\\})?$"); + + protected static final Pattern KEY_VALUE_PATTERN = Pattern.compile("(?[\\w\\-_]+):\\s*(?-?\\d+.\\d+|-?\\d+|\"[^\"]*\"|'[^']*'|true|false),?"); + + public static Map parse(String tagAddress) { + Map params = new HashMap<>(); + Matcher matcher = TAG_CONFIG_PATTERN.matcher(tagAddress); + if (matcher.find() && matcher.group("config") != null) { + Matcher kv = KEY_VALUE_PATTERN.matcher(matcher.group("config")); + while (kv.find()) { + params.put(kv.group("parameter"), clean(kv.group("value"))); + } + } + return params; + } + + private static String clean(String value) { + if (value.startsWith("'") && value.endsWith("'")) { + return value.substring(1, value.length() - 1); + } + if (value.startsWith("\"") && value.endsWith("\"")) { + return value.substring(1, value.length() - 1); + } + return value; + } + +} diff --git a/plc4j/spi/src/test/java/org/apache/plc4x/java/spi/tag/TagTagConfigParserTest.java b/plc4j/spi/src/test/java/org/apache/plc4x/java/spi/tag/TagTagConfigParserTest.java new file mode 100644 index 00000000000..7279dffb85d --- /dev/null +++ b/plc4j/spi/src/test/java/org/apache/plc4x/java/spi/tag/TagTagConfigParserTest.java @@ -0,0 +1,59 @@ +/* + * 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. + */ + +package org.apache.plc4x.java.spi.tag; + +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; + +class TagTagConfigParserTest { + + @Test + public void testConfigOptions() { + parse("aaa:123{unit-id: 10}", "unit-id", "10"); + parse("aaa:123{unit-id: -10}", "unit-id", "-10"); + parse("aaa:123{unit-id: 10.0}", "unit-id", "10.0"); + parse("aaa:123{unit-id: -10.0}", "unit-id", "-10.0"); + parse("aaa:123{unit-id: '10.0'}", "unit-id", "10.0"); + parse("aaa:123{unit-id: \"10.0\"}", "unit-id", "10.0"); + parse("aaa:123{unit-id: true}", "unit-id", "true"); + parse("aaa:123{unit-id: false}", "unit-id", "false"); + parse("aaa:123{val1: 1, val2: 2}", "val1", "1", "val2", "2"); + } + + private void parse(String address, String key, String value) { + Map config = TagConfigParser.parse(address); + verify(config, key, value); + } + + private void parse(String address, String key1, String value1, String key2, String value2) { + Map config = TagConfigParser.parse(address); + verify(config, key1, value1); + verify(config, key2, value2); + } + + private void verify(Map config, String key, String value) { + assertTrue(config.containsKey(key), "Config does not contain key: " + key); + assertEquals(value, config.get(key), "Value mismatch"); + } + +} \ No newline at end of file diff --git a/src/site/asciidoc/users/protocols/modbus.adoc b/src/site/asciidoc/users/protocols/modbus.adoc index 4a8930c817d..688b02a99fe 100644 --- a/src/site/asciidoc/users/protocols/modbus.adoc +++ b/src/site/asciidoc/users/protocols/modbus.adoc @@ -74,6 +74,12 @@ In general all Modbus addresses have this format: If the array-size part is omitted, the size-default of `1` is assumed. If the data-type part is omitted, it defaults to BOOL for Coils and Discrete Inputs and INT for input, holding and extended registers. +Additionally address can contain tag configuration: +---- +{unit-id: 123} +---- +Specifying this value overrides value of `default-unit-id` parameter specified at the connection string. + ==== Memory Areas There are a number of memory areas defined in the Modbus specification. @@ -167,6 +173,14 @@ To read 1 holding register at address 5678 the following examples are valid. - 45678 - 4x5678 +To read 1 holding register of unit 10 at address 5678 the following examples are valid. + +- holding-register:5678{unit-id: 10} +- 405678{unit-id: 10} +- 4x05678{unit-id: 10} +- 45678{unit-id: 10} +- 4x5678{unit-id: 10} + To read 10 extended registers starting at address 50 the following examples are valid. - extended-register:50[10]