diff --git a/code-generation/language-java/src/main/resources/templates/java/data-io-template.java.ftlh b/code-generation/language-java/src/main/resources/templates/java/data-io-template.java.ftlh index 7780429eb71..6c505cccb14 100644 --- a/code-generation/language-java/src/main/resources/templates/java/data-io-template.java.ftlh +++ b/code-generation/language-java/src/main/resources/templates/java/data-io-template.java.ftlh @@ -317,9 +317,14 @@ public class ${type.name} { return new PlcStruct(_map); <#break> <#case "List"> + <#if helper.getNonPrimitiveLanguageTypeNameForField(simpleField) == 'String'> + return new PlcSTRING(value); + <#else> return new PlcList(value); + <#break> <#default> + // default ${case.name} return new Plc${case.name}(value); 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 63a54f2f56f..7a5f3a2b2ed 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 @@ -27,10 +27,9 @@ import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.api.value.PlcValue; import org.apache.plc4x.java.modbus.ascii.config.ModbusAsciiConfiguration; -import org.apache.plc4x.java.modbus.base.field.ModbusField; +import org.apache.plc4x.java.modbus.base.field.ModbusFieldBase; import org.apache.plc4x.java.modbus.base.protocol.ModbusProtocolLogic; import org.apache.plc4x.java.modbus.readwrite.*; -import org.apache.plc4x.java.modbus.rtu.config.ModbusRtuConfiguration; import org.apache.plc4x.java.spi.configuration.HasConfiguration; import org.apache.plc4x.java.spi.generation.ParseException; import org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest; @@ -74,7 +73,7 @@ public CompletableFuture read(PlcReadRequest readRequest) { // Example for sending a request ... if (request.getFieldNames().size() == 1) { String fieldName = request.getFieldNames().iterator().next(); - ModbusField field = (ModbusField) request.getField(fieldName); + ModbusFieldBase field = (ModbusFieldBase) request.getField(fieldName); final ModbusPDU requestPdu = getReadRequestPdu(field); ModbusAsciiADU modbusAsciiADU = new ModbusAsciiADU(unitIdentifier, requestPdu, false); @@ -94,7 +93,7 @@ public CompletableFuture read(PlcReadRequest readRequest) { responseCode = getErrorCode(errorResponse); } else { try { - plcValue = toPlcValue(requestPdu, responsePdu, field.getDataType()); + plcValue = toPlcValue(requestPdu, responsePdu, field); responseCode = PlcResponseCode.OK; } catch (ParseException e) { // Add an error response code ... diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusExtendedRegister.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusExtendedRegister.java index 40dda0f5751..bbd638a7bce 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusExtendedRegister.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusExtendedRegister.java @@ -24,11 +24,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ModbusExtendedRegister extends ModbusField { +public class ModbusExtendedRegister extends ModbusFieldBase { - public static final Pattern ADDRESS_PATTERN = Pattern.compile("extended-register:" + ModbusField.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("6" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("6x" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("extended-register:" + ModbusFieldBase.ADDRESS_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("6" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("6x" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); protected static final int REGISTER_MAXADDRESS = 655359999; diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusField.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusField.java index 9b3a6be7459..678967047e8 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusField.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusField.java @@ -18,31 +18,12 @@ */ package org.apache.plc4x.java.modbus.base.field; -import com.fasterxml.jackson.annotation.JsonIgnore; import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException; import org.apache.plc4x.java.api.model.PlcField; -import org.apache.plc4x.java.modbus.readwrite.*; -import org.apache.plc4x.java.spi.generation.SerializationException; -import org.apache.plc4x.java.spi.generation.WriteBuffer; import org.apache.plc4x.java.spi.utils.Serializable; -import java.nio.charset.StandardCharsets; -import java.util.Objects; -import java.util.regex.Pattern; - public abstract class ModbusField implements PlcField, Serializable { - public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?
\\d+)(:(?[a-zA-Z_]+))?(\\[(?\\d+)])?"); - public static final Pattern FIXED_DIGIT_MODBUS_PATTERN = Pattern.compile("(?
\\d{4,5})?(:(?[a-zA-Z_]+))?(\\[(?\\d+)])?"); - - protected static final int PROTOCOL_ADDRESS_OFFSET = 1; - - private final int address; - - private final int quantity; - - private final ModbusDataType dataType; - public static ModbusField of(String addressString) { if (ModbusFieldCoil.matches(addressString)) { return ModbusFieldCoil.of(addressString); @@ -62,80 +43,4 @@ public static ModbusField of(String addressString) { throw new PlcInvalidFieldException("Unable to parse address: " + addressString); } - protected ModbusField(int address, Integer quantity, ModbusDataType dataType) { - this.address = address; - if ((this.address + PROTOCOL_ADDRESS_OFFSET) <= 0) { - throw new IllegalArgumentException("address must be greater than zero. Was " + (this.address + PROTOCOL_ADDRESS_OFFSET)); - } - this.quantity = quantity != null ? quantity : 1; - if (this.quantity <= 0) { - throw new IllegalArgumentException("quantity must be greater than zero. Was " + this.quantity); - } - this.dataType = dataType != null ? dataType : ModbusDataType.INT; - } - - public int getAddress() { - return address; - } - - public int getNumberOfElements() { - return quantity; - } - - public int getLengthBytes() { - return quantity * dataType.getDataTypeSize(); - } - - @JsonIgnore - public int getLengthWords() { - return (int) ((quantity * (float) dataType.getDataTypeSize()) / 2.0f); - } - - public ModbusDataType getDataType() { - return dataType; - } - - @Override - public String getPlcDataType() { - return dataType.name(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ModbusField)) { - return false; - } - ModbusField that = (ModbusField) o; - return address == that.address; - } - - @Override - public int hashCode() { - return Objects.hash(address); - } - - @Override - public String toString() { - return "ModbusField{" + - "address=" + address + - "datatype=" + dataType + - "quantity=" + quantity + - '}'; - } - - @Override - public void serialize(WriteBuffer writeBuffer) throws SerializationException { - writeBuffer.pushContext(getClass().getSimpleName()); - - writeBuffer.writeUnsignedInt("address", 16, address); - writeBuffer.writeUnsignedInt("numberOfElements", 16, getNumberOfElements()); - String dataType = getPlcDataType(); - writeBuffer.writeString("dataType", dataType.getBytes(StandardCharsets.UTF_8).length * 8, StandardCharsets.UTF_8.name(), dataType); - - writeBuffer.popContext(getClass().getSimpleName()); - } - } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldBase.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldBase.java new file mode 100644 index 00000000000..14576faf2e1 --- /dev/null +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldBase.java @@ -0,0 +1,119 @@ +/* + * 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.modbus.base.field; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.apache.plc4x.java.modbus.readwrite.*; +import org.apache.plc4x.java.spi.generation.SerializationException; +import org.apache.plc4x.java.spi.generation.WriteBuffer; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.regex.Pattern; + +public abstract class ModbusFieldBase extends ModbusField { + + public static final Pattern ADDRESS_PATTERN = Pattern.compile("(?
\\d+)(:(?[a-zA-Z_]+))?(\\[(?\\d+)])?"); + public static final Pattern FIXED_DIGIT_MODBUS_PATTERN = Pattern.compile("(?
\\d{4,5})?(:(?[a-zA-Z_]+))?(\\[(?\\d+)])?"); + + protected static final int PROTOCOL_ADDRESS_OFFSET = 1; + + private final int address; + + private final int quantity; + + private final ModbusDataType dataType; + + protected ModbusFieldBase(int address, Integer quantity, ModbusDataType dataType) { + this.address = address; + if ((this.address + PROTOCOL_ADDRESS_OFFSET) <= 0) { + throw new IllegalArgumentException("address must be greater than zero. Was " + (this.address + PROTOCOL_ADDRESS_OFFSET)); + } + this.quantity = quantity != null ? quantity : 1; + if (this.quantity <= 0) { + throw new IllegalArgumentException("quantity must be greater than zero. Was " + this.quantity); + } + this.dataType = dataType != null ? dataType : ModbusDataType.INT; + } + + public int getAddress() { + return address; + } + + public int getNumberOfElements() { + return quantity; + } + + public int getLengthBytes() { + return quantity * dataType.getDataTypeSize(); + } + + @JsonIgnore + public int getLengthWords() { + return (int) ((quantity * (float) dataType.getDataTypeSize()) / 2.0f); + } + + public ModbusDataType getDataType() { + return dataType; + } + + @Override + public String getPlcDataType() { + return dataType.name(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ModbusFieldBase)) { + return false; + } + ModbusFieldBase that = (ModbusFieldBase) o; + return address == that.address; + } + + @Override + public int hashCode() { + return Objects.hash(address); + } + + @Override + public String toString() { + return "ModbusField{" + + "address=" + address + + "datatype=" + dataType + + "quantity=" + quantity + + '}'; + } + + @Override + public void serialize(WriteBuffer writeBuffer) throws SerializationException { + writeBuffer.pushContext(getClass().getSimpleName()); + + writeBuffer.writeUnsignedInt("address", 16, address); + writeBuffer.writeUnsignedInt("numberOfElements", 16, getNumberOfElements()); + String dataType = getPlcDataType(); + writeBuffer.writeString("dataType", dataType.getBytes(StandardCharsets.UTF_8).length * 8, StandardCharsets.UTF_8.name(), dataType); + + writeBuffer.popContext(getClass().getSimpleName()); + } + +} diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldCoil.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldCoil.java index 631f55758bd..a128c798e91 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldCoil.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldCoil.java @@ -24,11 +24,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ModbusFieldCoil extends ModbusField { +public class ModbusFieldCoil extends ModbusFieldBase { - public static final Pattern ADDRESS_PATTERN = Pattern.compile("coil:" + ModbusField.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("0" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("0x" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("coil:" + ModbusFieldBase.ADDRESS_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("0" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("0x" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); protected static final int REGISTER_MAXADDRESS = 65535; diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldDiscreteInput.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldDiscreteInput.java index bd365a66d9d..3beb835ca39 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldDiscreteInput.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldDiscreteInput.java @@ -24,11 +24,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ModbusFieldDiscreteInput extends ModbusField { +public class ModbusFieldDiscreteInput extends ModbusFieldBase { - public static final Pattern ADDRESS_PATTERN = Pattern.compile("discrete-input:" + ModbusField.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("1" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("1x" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("discrete-input:" + ModbusFieldBase.ADDRESS_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("1" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("1x" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); protected static final int REGISTER_MAX_ADDRESS = 65535; diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldHandler.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldHandler.java index c62d3d0955c..21e6fb2d181 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldHandler.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldHandler.java @@ -36,6 +36,8 @@ public PlcField createField(String fieldQuery) { return ModbusFieldCoil.of(fieldQuery); } else if (ModbusExtendedRegister.matches(fieldQuery)) { return ModbusExtendedRegister.of(fieldQuery); + } else if (ModbusIdentificationRegister.matches(fieldQuery)) { + return ModbusIdentificationRegister.of(fieldQuery); } throw new PlcInvalidFieldException(fieldQuery); } diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldHoldingRegister.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldHoldingRegister.java index 3872ffa2e3c..ab10830c7dd 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldHoldingRegister.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldHoldingRegister.java @@ -24,11 +24,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ModbusFieldHoldingRegister extends ModbusField { +public class ModbusFieldHoldingRegister extends ModbusFieldBase { - public static final Pattern ADDRESS_PATTERN = Pattern.compile("holding-register:" + ModbusField.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("4" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("4x" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("holding-register:" + ModbusFieldBase.ADDRESS_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("4" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("4x" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); protected static final int REGISTER_MAXADDRESS = 65535; diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldInputRegister.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldInputRegister.java index 6c26e27312b..d5d7bd56fac 100644 --- a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldInputRegister.java +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusFieldInputRegister.java @@ -24,11 +24,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class ModbusFieldInputRegister extends ModbusField { +public class ModbusFieldInputRegister extends ModbusFieldBase { - public static final Pattern ADDRESS_PATTERN = Pattern.compile("input-register:" + ModbusField.ADDRESS_PATTERN); - public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("3" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); - public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("3x" + ModbusField.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_PATTERN = Pattern.compile("input-register:" + ModbusFieldBase.ADDRESS_PATTERN); + public static final Pattern ADDRESS_SHORTER_PATTERN = Pattern.compile("3" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); + public static final Pattern ADDRESS_SHORT_PATTERN = Pattern.compile("3x" + ModbusFieldBase.FIXED_DIGIT_MODBUS_PATTERN); protected static final int REGISTER_MAXADDRESS = 65535; diff --git a/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusIdentificationRegister.java b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusIdentificationRegister.java new file mode 100644 index 00000000000..0f40a843e8c --- /dev/null +++ b/plc4j/drivers/modbus/src/main/java/org/apache/plc4x/java/modbus/base/field/ModbusIdentificationRegister.java @@ -0,0 +1,97 @@ +/* + * 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.modbus.base.field; + +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException; +import org.apache.plc4x.java.modbus.readwrite.ModbusDeviceInformationLevel; +import org.apache.plc4x.java.spi.generation.SerializationException; +import org.apache.plc4x.java.spi.generation.WriteBuffer; + +public class ModbusIdentificationRegister extends ModbusField { + + public static final String HEX = "0[xX][0-9a-fA-F]+"; + public static final String LEVEL = "(?\\d+|" + HEX + "|(?:BASIC|REGULAR|EXTENDED|INDIVIDUAL))"; + public static final String OBJECT_ID = "(?\\d+|" + HEX + ")"; + public static final Pattern ADDRESS_PATTERN = Pattern.compile("identification:" + LEVEL + ":" + OBJECT_ID); + + private final ModbusDeviceInformationLevel level; + private final short objectId; + + protected ModbusIdentificationRegister(ModbusDeviceInformationLevel level, short objectId) { + this.level = level; + this.objectId = objectId; + } + + public static boolean matches(String addressString) { + return ADDRESS_PATTERN.matcher(addressString).matches(); + } + + public static Matcher getMatcher(String addressString) { + Matcher matcher = ADDRESS_PATTERN.matcher(addressString); + if (matcher.matches()) { + return matcher; + } + + throw new PlcInvalidFieldException(addressString, ADDRESS_PATTERN); + } + + public static ModbusIdentificationRegister of(String addressString) { + Matcher matcher = getMatcher(addressString); + String levelGroup = matcher.group("level"); + String objectidGroup = matcher.group("objectId"); + + ModbusDeviceInformationLevel level = parseNumber(levelGroup) + .map(Integer::byteValue) + .map(ModbusDeviceInformationLevel::enumForValue) + .orElseGet(() -> ModbusDeviceInformationLevel.valueOf(levelGroup.toUpperCase())); + + int objectId = parseNumber(objectidGroup) + .orElseThrow(() -> new IllegalArgumentException("Invalid field definition detected, unknown object id")); + + return new ModbusIdentificationRegister(level, (short) objectId); + } + + private static Optional parseNumber(String value) { + if (value.matches("\\d+")) { + return Optional.of(Integer.parseInt(value)); + } else if (value.matches(HEX)) { + return Optional.of(Integer.parseInt(value.substring(2), 16)); + } + return Optional.empty(); + } + + public ModbusDeviceInformationLevel getLevel() { + return level; + } + + public short getObjectId() { + return objectId; + } + + @Override + public void serialize(WriteBuffer writeBuffer) throws SerializationException { + writeBuffer.pushContext(getClass().getSimpleName()); + writeBuffer.writeShort("level", 8, level.getValue()); + writeBuffer.writeShort("objectId", 8, objectId); + writeBuffer.popContext(getClass().getSimpleName()); + } +} 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 1aa8fe19b51..22890612b33 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 @@ -18,13 +18,15 @@ */ package org.apache.plc4x.java.modbus.base.protocol; +import java.util.LinkedHashMap; +import java.util.Map; import org.apache.plc4x.java.api.exceptions.PlcRuntimeException; -import org.apache.plc4x.java.api.messages.*; import org.apache.plc4x.java.api.value.*; import org.apache.plc4x.java.api.model.PlcField; import org.apache.plc4x.java.api.types.PlcResponseCode; -import org.apache.plc4x.java.modbus.tcp.config.ModbusTcpConfiguration; import org.apache.plc4x.java.modbus.base.field.ModbusField; +import org.apache.plc4x.java.modbus.base.field.ModbusFieldBase; +import org.apache.plc4x.java.modbus.base.field.ModbusIdentificationRegister; import org.apache.plc4x.java.modbus.base.field.ModbusFieldCoil; import org.apache.plc4x.java.modbus.base.field.ModbusFieldDiscreteInput; import org.apache.plc4x.java.modbus.base.field.ModbusFieldHoldingRegister; @@ -33,16 +35,11 @@ import org.apache.plc4x.java.modbus.readwrite.*; import org.apache.plc4x.java.spi.ConversationContext; import org.apache.plc4x.java.spi.Plc4xProtocolBase; -import org.apache.plc4x.java.spi.configuration.HasConfiguration; import org.apache.plc4x.java.spi.generation.*; -import org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest; -import org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse; -import org.apache.plc4x.java.spi.messages.DefaultPlcWriteRequest; -import org.apache.plc4x.java.spi.messages.DefaultPlcWriteResponse; -import org.apache.plc4x.java.spi.messages.utils.ResponseItem; import org.apache.plc4x.java.spi.transaction.RequestTransactionManager; import org.apache.commons.lang3.ArrayUtils; import org.apache.plc4x.java.spi.values.PlcBOOL; +import org.apache.plc4x.java.spi.values.PlcByteArray; import org.apache.plc4x.java.spi.values.PlcList; import java.time.Duration; @@ -51,8 +48,10 @@ import java.util.BitSet; import java.util.Collections; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.plc4x.java.spi.values.PlcSINT; +import org.apache.plc4x.java.spi.values.PlcStruct; +import org.apache.plc4x.java.spi.values.PlcUSINT; public abstract class ModbusProtocolLogic extends Plc4xProtocolBase { @@ -147,6 +146,11 @@ protected ModbusPDU getReadRequestPdu(PlcField field) { itemArray = Arrays.asList(group1, group2); } return new ModbusPDUReadFileRecordRequest(itemArray); + } else if (field instanceof ModbusIdentificationRegister) { + ModbusIdentificationRegister identification = (ModbusIdentificationRegister) field; + return new ModbusPDUReadDeviceIdentificationRequest( + identification.getLevel(), identification.getObjectId() + ); } throw new PlcRuntimeException("Unsupported read field type " + field.getClass().getName()); } @@ -211,7 +215,44 @@ protected ModbusPDU getWriteRequestPdu(PlcField field, PlcValue plcValue) { throw new PlcRuntimeException("Unsupported write field type " + field.getClass().getName()); } - protected PlcValue toPlcValue(ModbusPDU request, ModbusPDU response, ModbusDataType dataType) throws ParseException { + protected PlcValue toPlcValue(ModbusPDU request, ModbusPDU response, ModbusField field) throws ParseException { + if (field instanceof ModbusFieldBase) { + return toPlcValue(request, response, ((ModbusFieldBase) field).getDataType()); + } + if (request instanceof ModbusPDUReadDeviceIdentificationRequest) { + if (!(response instanceof ModbusPDUReadDeviceIdentificationResponse)) { + throw new PlcRuntimeException("Unexpected response type. " + + "Expected ModbusPDUReadDeviceIdentificationResponse, but got " + response.getClass().getName()); + } + + ModbusPDUReadDeviceIdentificationResponse rsp = (ModbusPDUReadDeviceIdentificationResponse) response; +// protected final ModbusDeviceInformationLevel level; +// protected final boolean individualAccess; +// protected final ModbusDeviceInformationConformityLevel conformityLevel; +// protected final ModbusDeviceInformationMoreFollows moreFollows; +// protected final short nextObjectId; +// protected final List objects; + Map data = new LinkedHashMap<>(); + data.put("level", new PlcUSINT(rsp.getLevel().getValue())); + data.put("individualAccess", new PlcBOOL(rsp.getIndividualAccess())); + data.put("conformityLevel", new PlcUSINT(rsp.getConformityLevel().getValue())); + data.put("moreFollows", new PlcBOOL(rsp.getMoreFollows() == ModbusDeviceInformationMoreFollows.MORE_OBJECTS_AVAILABLE)); + data.put("nextObjectId", new PlcUSINT(rsp.getNextObjectId())); + List objectList = new ArrayList<>(); + for (ModbusDeviceInformationObject object : rsp.getObjects()) { + Map objectMap = new LinkedHashMap<>(); + objectMap.put("objectId", new PlcUSINT(object.getObjectId())); + objectMap.put("data", new PlcByteArray(object.getData())); + objectList.add(new PlcStruct(objectMap)); + } + data.put("objects", new PlcList(objectList)); + return new PlcStruct(data); + + } + return null; + } + + private PlcValue toPlcValue(ModbusPDU request, ModbusPDU response, ModbusDataType dataType) throws ParseException { short fieldDataTypeSize = dataType.getDataTypeSize(); if (request instanceof ModbusPDUReadDiscreteInputsRequest) { @@ -285,13 +326,13 @@ protected PlcValue toPlcValue(ModbusPDU request, ModbusPDU response, ModbusDataT } protected byte[] fromPlcValue(PlcField field, PlcValue plcValue) { - ModbusDataType fieldDataType = ((ModbusField) field).getDataType(); + ModbusDataType fieldDataType = ((ModbusFieldBase) field).getDataType(); try { if (plcValue instanceof PlcList) { WriteBufferByteBased writeBuffer = new WriteBufferByteBased(DataItem.getLengthInBytes(plcValue, fieldDataType, plcValue.getLength())); DataItem.staticSerialize(writeBuffer, plcValue, fieldDataType, plcValue.getLength(), ByteOrder.BIG_ENDIAN); byte[] data = writeBuffer.getData(); - if (((ModbusField) field).getDataType() == ModbusDataType.BOOL) { + if (((ModbusFieldBase) field).getDataType() == ModbusDataType.BOOL) { //Reverse Bits in each byte as //they should be ordered like this: 8 7 6 5 4 3 2 1 | 0 0 0 0 0 0 0 9 byte[] bytes = new byte[data.length]; 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 09accd236ed..d191198ff3f 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 @@ -26,11 +26,10 @@ import org.apache.plc4x.java.api.model.PlcField; import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.api.value.PlcValue; -import org.apache.plc4x.java.modbus.base.field.ModbusField; +import org.apache.plc4x.java.modbus.base.field.ModbusFieldBase; import org.apache.plc4x.java.modbus.base.protocol.ModbusProtocolLogic; import org.apache.plc4x.java.modbus.readwrite.*; import org.apache.plc4x.java.modbus.rtu.config.ModbusRtuConfiguration; -import org.apache.plc4x.java.modbus.tcp.config.ModbusTcpConfiguration; import org.apache.plc4x.java.spi.configuration.HasConfiguration; import org.apache.plc4x.java.spi.generation.ParseException; import org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest; @@ -74,7 +73,7 @@ public CompletableFuture read(PlcReadRequest readRequest) { // Example for sending a request ... if (request.getFieldNames().size() == 1) { String fieldName = request.getFieldNames().iterator().next(); - ModbusField field = (ModbusField) request.getField(fieldName); + ModbusFieldBase field = (ModbusFieldBase) request.getField(fieldName); final ModbusPDU requestPdu = getReadRequestPdu(field); ModbusRtuADU modbusRtuADU = new ModbusRtuADU(unitIdentifier, requestPdu, false); @@ -94,7 +93,7 @@ public CompletableFuture read(PlcReadRequest readRequest) { responseCode = getErrorCode(errorResponse); } else { try { - plcValue = toPlcValue(requestPdu, responsePdu, field.getDataType()); + plcValue = toPlcValue(requestPdu, responsePdu, field); responseCode = PlcResponseCode.OK; } catch (ParseException e) { // Add an error response code ... 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 e4aad244d0b..f020f9c8786 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 @@ -27,9 +27,9 @@ import org.apache.plc4x.java.api.types.PlcResponseCode; import org.apache.plc4x.java.api.value.PlcValue; import org.apache.plc4x.java.modbus.base.field.ModbusField; +import org.apache.plc4x.java.modbus.base.field.ModbusFieldBase; import org.apache.plc4x.java.modbus.base.protocol.ModbusProtocolLogic; import org.apache.plc4x.java.modbus.readwrite.*; -import org.apache.plc4x.java.modbus.rtu.config.ModbusRtuConfiguration; import org.apache.plc4x.java.modbus.tcp.config.ModbusTcpConfiguration; import org.apache.plc4x.java.spi.configuration.HasConfiguration; import org.apache.plc4x.java.spi.generation.ParseException; @@ -101,7 +101,7 @@ public CompletableFuture read(PlcReadRequest readRequest) { responseCode = getErrorCode(errorResponse); } else { try { - plcValue = toPlcValue(requestPdu, responsePdu, field.getDataType()); + plcValue = toPlcValue(requestPdu, responsePdu, field); responseCode = PlcResponseCode.OK; } catch (ParseException e) { // Add an error response code ... diff --git a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ModbusFieldTest.java b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ModbusFieldTest.java index 90231b0305f..fcf1dac7d92 100644 --- a/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ModbusFieldTest.java +++ b/plc4j/drivers/modbus/src/test/java/org/apache/plc4x/java/modbus/ModbusFieldTest.java @@ -19,10 +19,12 @@ package org.apache.plc4x.java.modbus; import org.apache.plc4x.java.modbus.base.field.ModbusFieldHoldingRegister; +import org.apache.plc4x.java.modbus.base.field.ModbusIdentificationRegister; import org.apache.plc4x.java.modbus.base.field.ModbusFieldInputRegister; import org.apache.plc4x.java.modbus.base.field.ModbusExtendedRegister; import org.apache.plc4x.java.modbus.base.field.ModbusFieldDiscreteInput; import org.apache.plc4x.java.modbus.base.field.ModbusFieldCoil; +import org.apache.plc4x.java.modbus.readwrite.ModbusDeviceInformationLevel; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -68,4 +70,21 @@ public void testDiscreteInput_INT_ARRAY_RANGE() { } } + @Test + public void testIdentificationField() { + ModbusDeviceInformationLevel level = ModbusDeviceInformationLevel.EXTENDED; + short objectId = 10; + + ModbusIdentificationRegister identification = ModbusIdentificationRegister.of("identification:EXTENDED:10"); + Assertions.assertEquals(level, identification.getLevel()); + Assertions.assertEquals(objectId, identification.getObjectId()); + + identification = ModbusIdentificationRegister.of("identification:0x03:10"); + Assertions.assertEquals(level, identification.getLevel()); + Assertions.assertEquals(objectId, identification.getObjectId()); + + identification = ModbusIdentificationRegister.of("identification:0x3:0xA"); + Assertions.assertEquals(level, identification.getLevel()); + Assertions.assertEquals(objectId, identification.getObjectId()); + } } diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcStruct.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcStruct.java index f587ced0553..099b03b0148 100644 --- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcStruct.java +++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/values/PlcStruct.java @@ -96,7 +96,7 @@ public void serialize(WriteBuffer writeBuffer) throws SerializationException { throw new PlcRuntimeException("Error serializing. List item doesn't implement XmlSerializable"); } ((Serializable) fieldValue).serialize(writeBuffer); - writeBuffer.pushContext(fieldName); + writeBuffer.popContext(fieldName); } writeBuffer.popContext("PlcStruct"); } diff --git a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec index 1136df74dd0..f19e63f9851 100644 --- a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec +++ b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec @@ -416,6 +416,15 @@ ['WCHAR' List [array uint 16 value count 'numberOfValues'] ] + ['STRING','1' STRING + [simple vstring '8' value encoding='"UTF-8"'] + ] + ['STRING' List + [simple vstring '8 * numberOfValues' value encoding='"UTF-8"'] + ] + ['WSTRING' List + [simple vstring '16 * numberOfValues' value encoding='"UTF-16"'] + ] ] ] diff --git a/protocols/modbus/src/test/resources/protocols/modbus/tcp/DriverTestsuite.xml b/protocols/modbus/src/test/resources/protocols/modbus/tcp/DriverTestsuite.xml index 309e271d638..d83e1b2fcea 100644 --- a/protocols/modbus/src/test/resources/protocols/modbus/tcp/DriverTestsuite.xml +++ b/protocols/modbus/src/test/resources/protocols/modbus/tcp/DriverTestsuite.xml @@ -532,4 +532,153 @@ + + + Identification request + + + + + + hurz +
identification:BASIC:1
+
+
+
+
+ + + MODBUS_TCP + false + + + + 1 + 0 + 5 + 1 + + + false + 43 + + 14 + + 1 + + 1 + + + + + + + + + MODBUS_TCP + true + + + + 1 + 0 + 15 + 1 + + + false + 43 + + 14 + + 1 + + false + + 3 + + + 0 + + 135 + 2 + + + 135 + 1 + 0x01 + + + 136 + 2 + 0x313d + + + + + + + + + + + + + + + 1 + 1 + + + + + + + + OK + + + 1 + + + false + + + 3 + + + false + + + 135 + + + + + + 135 + + + 0x01 + + + + + 136 + + + 0x313d + + + + + + + + + + +
+
+ diff --git a/protocols/modbus/src/test/resources/protocols/modbus/tcp/ParserSerializerTestsuite.xml b/protocols/modbus/src/test/resources/protocols/modbus/tcp/ParserSerializerTestsuite.xml index 9463e1cc673..bd5135c8ab7 100644 --- a/protocols/modbus/src/test/resources/protocols/modbus/tcp/ParserSerializerTestsuite.xml +++ b/protocols/modbus/src/test/resources/protocols/modbus/tcp/ParserSerializerTestsuite.xml @@ -87,6 +87,96 @@ + + Device identification request + + 000100000005012b0e0387 + + ModbusADU + + MODBUS_TCP + false + + + + + 1 + 0 + 5 + 1 + + + false + 43 + + 14 + + 3 + + 135 + + + + + + + + + + Device identification response + + 00010000000f012b0e03030087028701018802313d + + ModbusADU + + MODBUS_TCP + true + + + + + 1 + 0 + 15 + 1 + + + false + 43 + + 14 + + 3 + + false + + 3 + + + 0 + + 135 + 2 + + + 135 + 1 + 0x01 + + + 136 + 2 + 0x313d + + + + + + + + + + Read Extended Registers Request Split File Record 000a0000001101140e060003270e000206000400000008