Skip to content

Commit

Permalink
feat(plc4x/modbus): Add support for unit-id option for modbus tags.
Browse files Browse the repository at this point in the history
Introduce support for tag config at the tag level.
Closes #1234.

Signed-off-by: Łukasz Dywicki <[email protected]>
  • Loading branch information
splatch committed Mar 24, 2024
1 parent 57a0f62 commit 9b4fdac
Show file tree
Hide file tree
Showing 20 changed files with 250 additions and 77 deletions.
2 changes: 1 addition & 1 deletion plc4j/drivers/all/src/site/generated/modbus-ascii.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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+|
+++
Expand Down
2 changes: 1 addition & 1 deletion plc4j/drivers/all/src/site/generated/modbus-rtu.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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+|
+++
Expand Down
2 changes: 1 addition & 1 deletion plc4j/drivers/all/src/site/generated/modbus-tcp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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+|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 +
'}';
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -64,7 +64,8 @@ public CompletableFuture<PlcPingResponse> 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)
Expand Down Expand Up @@ -99,8 +100,9 @@ public CompletableFuture<PlcReadResponse> 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)
Expand Down Expand Up @@ -158,7 +160,8 @@ public CompletableFuture<PlcWriteResponse> 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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<T extends ModbusADU> extends Plc4xProtocolBase<T> {
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)) {
Expand Down Expand Up @@ -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<String, String> config) {
this.address = address;
if (getLogicalAddress() <= 0) {
throw new IllegalArgumentException("address must be greater than zero. Was " + getLogicalAddress());
Expand All @@ -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);
}

/**
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -168,6 +179,7 @@ public String toString() {
"address=" + address +
", quantity=" + quantity +
", dataType=" + dataType +
", unitId=" + unitId +
" }";
}

Expand All @@ -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());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> config) {
super(address, quantity, dataType, config);
}

protected String getAddressStringPrefix() {
Expand Down Expand Up @@ -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));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> config) {
super(address, quantity, dataType, config);
}

protected String getAddressStringPrefix() {
Expand Down Expand Up @@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> config) {
super(address, quantity, dataType, config);
}

protected String getAddressStringPrefix() {
Expand Down Expand Up @@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> config) {
super(address, quantity, dataType, config);
}

protected String getAddressStringPrefix() {
Expand Down Expand Up @@ -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));
}

}
Loading

0 comments on commit 9b4fdac

Please sign in to comment.