mirror of
https://gitee.com/pnoker/iot-dc3.git
synced 2026-11-04 23:32:48 +08:00
fix(driver-fins): correct 32-bit read length and FLOAT write encoding
Two runtime bugs in the FINS driver: 1. read() hardcoded a 1-word (2-byte) memory-read request regardless of dataType, so INT32/UINT32/FLOAT decodes (which need 4 bytes) always threw BufferUnderflowException — 32-bit points could never be read. Derive the word count from dataType (2 words for 32-bit types). 2. encodeWriteData grouped FLOAT with INT32/UINT32, doing Integer.parseInt + putInt. Writing "3.14" threw NumberFormatException and "3" was written as int bits (read back as ~4.2E-45 via getFloat). Give FLOAT its own branch using Float.parseFloat + putFloat. Relaxed encodeWriteData/decodeValue/wordCount to package-private and added round-trip tests (FLOAT, INT32) plus wordCount coverage. Verified: dc3-driver-fins 7 tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+24
-5
@@ -164,7 +164,7 @@ public class FinsDriverCustomServiceImpl implements DriverCustomService {
|
||||
|
||||
try {
|
||||
byte areaCode = resolveMemoryAreaCode(memoryArea);
|
||||
byte[] finsFrame = buildMemoryReadFrame(driverConfig, areaCode, address, 1);
|
||||
byte[] finsFrame = buildMemoryReadFrame(driverConfig, areaCode, address, wordCount(dataType));
|
||||
byte[] response = sendFinsCommand(socket, finsFrame);
|
||||
|
||||
String value = parseReadResponse(response, dataType);
|
||||
@@ -402,7 +402,7 @@ public class FinsDriverCustomServiceImpl implements DriverCustomService {
|
||||
* @param dataType data type identifier
|
||||
* @return decoded value string
|
||||
*/
|
||||
private String decodeValue(byte[] data, String dataType) {
|
||||
String decodeValue(byte[] data, String dataType) {
|
||||
return switch (dataType.toUpperCase()) {
|
||||
case "INT16" -> String.valueOf(ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN).getShort());
|
||||
case "UINT16" ->
|
||||
@@ -417,6 +417,20 @@ public class FinsDriverCustomServiceImpl implements DriverCustomService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the FINS memory read length in words (2 bytes each) for a data type.
|
||||
* 32-bit types (INT32/UINT32/FLOAT) span 2 words; all others read a single word.
|
||||
*
|
||||
* @param dataType data type identifier
|
||||
* @return word count to request
|
||||
*/
|
||||
int wordCount(String dataType) {
|
||||
return switch (dataType.toUpperCase()) {
|
||||
case "INT32", "UINT32", "FLOAT" -> 2;
|
||||
default -> 1;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a string value to bytes for FINS write operations.
|
||||
*
|
||||
@@ -424,7 +438,7 @@ public class FinsDriverCustomServiceImpl implements DriverCustomService {
|
||||
* @param dataType data type identifier
|
||||
* @return encoded bytes
|
||||
*/
|
||||
private byte[] encodeWriteData(String value, String dataType) {
|
||||
byte[] encodeWriteData(String value, String dataType) {
|
||||
byte[] data;
|
||||
switch (dataType.toUpperCase()) {
|
||||
case "INT16":
|
||||
@@ -435,13 +449,18 @@ public class FinsDriverCustomServiceImpl implements DriverCustomService {
|
||||
break;
|
||||
}
|
||||
case "INT32":
|
||||
case "UINT32":
|
||||
case "FLOAT": {
|
||||
case "UINT32": {
|
||||
data = new byte[4];
|
||||
int i = Integer.parseInt(value);
|
||||
ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN).putInt(i);
|
||||
break;
|
||||
}
|
||||
case "FLOAT": {
|
||||
data = new byte[4];
|
||||
float f = Float.parseFloat(value);
|
||||
ByteBuffer.wrap(data).order(ByteOrder.BIG_ENDIAN).putFloat(f);
|
||||
break;
|
||||
}
|
||||
case "STRING": {
|
||||
data = value.getBytes();
|
||||
break;
|
||||
|
||||
+27
@@ -91,4 +91,31 @@ class FinsDriverCustomServiceImplTest {
|
||||
assertThatNoException().isThrownBy(() -> service.schedule());
|
||||
}
|
||||
|
||||
@Test
|
||||
void wordCountIsTwoFor32BitTypesAndOneOtherwise() {
|
||||
assertThat(service.wordCount("INT32")).isEqualTo(2);
|
||||
assertThat(service.wordCount("UINT32")).isEqualTo(2);
|
||||
assertThat(service.wordCount("FLOAT")).isEqualTo(2);
|
||||
assertThat(service.wordCount("float")).isEqualTo(2);
|
||||
assertThat(service.wordCount("INT16")).isEqualTo(1);
|
||||
assertThat(service.wordCount("UINT16")).isEqualTo(1);
|
||||
assertThat(service.wordCount("STRING")).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void floatEncodeDecodeRoundTrips() {
|
||||
byte[] encoded = service.encodeWriteData("3.14", "FLOAT");
|
||||
|
||||
assertThat(encoded).hasSize(4);
|
||||
assertThat(Float.parseFloat(service.decodeValue(encoded, "FLOAT"))).isEqualTo(3.14f);
|
||||
}
|
||||
|
||||
@Test
|
||||
void int32EncodeDecodeRoundTrips() {
|
||||
byte[] encoded = service.encodeWriteData("70000", "INT32");
|
||||
|
||||
assertThat(encoded).hasSize(4);
|
||||
assertThat(service.decodeValue(encoded, "INT32")).isEqualTo("70000");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user