[modbus] Gain-offset profile (QuantityType support) and writing of individual bits of holding registers (#9980)

* [modbus] gainOffset and bitMask profiles for working with modbus data

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] README trailing whitespaces

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] README and some final renaming

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] log error with incompatible units

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] gainOffset profile: test for incompatible unit

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] example renamed

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Remove unused fields

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] gainOffset profile: make configuration parameters optional

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] xml indentantion fix

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] static code analysis fixes

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Minor fixes for null checking

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] remove comment

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] bit profile README disclaimer with many commands

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Grammar fixes in README

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Fix bit profile UI configuration

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Bit profile: Added possibility to invert value on read/write

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] fix typo with explanation of inverted

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] bit profile: unit tests for inverted parameter

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] spotless:apply

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] static checker fixes

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] write bit feature in data thing

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* wip

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] resolve itest

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] fixes

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Remove bit profile

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Fix data thing readStart validation

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] readme fix

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Remove bit profile test

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Invalidate REFRESH data cache with cacheful writes

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] cleanup

- abort if command is not convertible to 0/1 (previously wrote the
  cached data)
- fail fast conditionals instead of deep if's

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] README Fix typo in example

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] fix data thing write when child of endpoint

Also added regression test

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* Update bundles/org.openhab.binding.modbus/src/main/resources/OH-INF/config/gainOffset.xml

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>

* [modbus] performance-optimized logging

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] README: Removing xtend syntax hint, not needed anymore

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] generics typing added

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] dead code

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] avoid supressing generic type warnings

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] unnecessary generics

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] rename type parameter name

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] QU (short for quantity output) generic type instead of Q2

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] Remove unused localization

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] profile constant visibility harmonized

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

* [modbus] spotless:apply

Signed-off-by: Sami Salonen <ssalonen@gmail.com>

Co-authored-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
Sami Salonen
2021-04-16 23:59:55 +03:00
committed by GitHub
parent b42101addc
commit 265fd30ba1
11 changed files with 1217 additions and 94 deletions

View File

@@ -53,6 +53,7 @@ Fragment-Host: org.openhab.binding.modbus
org.mockito.mockito-core;version='[3.7.0,3.7.1)',\
org.objenesis;version='[3.1.0,3.1.1)',\
org.mockito.junit-jupiter;version='[3.7.0,3.7.1)',\
junit-jupiter-params;version='[5.7.0,5.7.1)',\
org.glassfish.hk2.osgi-resource-locator;version='[1.0.3,1.0.4)',\
biz.aQute.tester.junit-platform;version='[5.3.0,5.3.1)',\
com.google.gson;version='[2.8.6,2.8.7)',\

View File

@@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -30,13 +31,20 @@ import java.util.Objects;
import java.util.concurrent.ScheduledFuture;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import org.hamcrest.Matcher;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mockito;
import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
import org.openhab.binding.modbus.handler.ModbusPollerThingHandler;
import org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal;
import org.openhab.binding.modbus.internal.handler.ModbusDataThingHandler;
import org.openhab.binding.modbus.internal.handler.ModbusTcpThingHandler;
import org.openhab.core.config.core.Configuration;
@@ -46,6 +54,7 @@ import org.openhab.core.io.transport.modbus.AsyncModbusWriteResult;
import org.openhab.core.io.transport.modbus.BitArray;
import org.openhab.core.io.transport.modbus.ModbusConstants;
import org.openhab.core.io.transport.modbus.ModbusConstants.ValueType;
import org.openhab.core.io.transport.modbus.ModbusReadCallback;
import org.openhab.core.io.transport.modbus.ModbusReadFunctionCode;
import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
import org.openhab.core.io.transport.modbus.ModbusRegisterArray;
@@ -94,6 +103,9 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
}
}
private static final String HOST = "thisishost";
private static final int PORT = 44;
private static final Map<String, String> CHANNEL_TO_ACCEPTED_TYPE = new HashMap<>();
static {
CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_SWITCH, "Switch");
@@ -109,10 +121,49 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_LAST_READ_ERROR, "DateTime");
}
private List<ModbusWriteRequestBlueprint> writeRequests = new ArrayList<>();
private Bridge realEndpointWithMockedComms;
public ModbusReadCallback getPollerCallback(ModbusPollerThingHandler handler) {
Field callbackField;
try {
callbackField = ModbusPollerThingHandler.class.getDeclaredField("callbackDelegator");
callbackField.setAccessible(true);
return (ModbusReadCallback) callbackField.get(handler);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
fail(e);
throw new RuntimeException(e);
}
}
@BeforeEach
public void beforeEach() {
mockCommsToModbusManager();
Configuration tcpConfig = new Configuration();
tcpConfig.put("host", HOST);
tcpConfig.put("port", PORT);
tcpConfig.put("id", 9);
realEndpointWithMockedComms = BridgeBuilder
.create(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_TCP,
new ThingUID(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_TCP, "mytcp"))
.withLabel("label for mytcp").withConfiguration(tcpConfig).build();
addThing(realEndpointWithMockedComms);
assertEquals(ThingStatus.ONLINE, realEndpointWithMockedComms.getStatus(),
realEndpointWithMockedComms.getStatusInfo().getDescription());
}
@AfterEach
public void tearDown() {
writeRequests.clear();
if (realEndpointWithMockedComms != null) {
thingProvider.remove(realEndpointWithMockedComms.getUID());
}
}
private static Arguments appendArg(Arguments args, Object obj) {
Object[] newArgs = Arrays.copyOf(args.get(), args.get().length + 1);
newArgs[args.get().length] = obj;
return Arguments.of(newArgs);
}
private void captureModbusWrites() {
@@ -455,10 +506,17 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
return dataHandler;
}
@SuppressWarnings({ "null" })
private ModbusDataThingHandler testWriteHandlingGeneric(String start, String transform, ValueType valueType,
String writeType, ModbusWriteFunctionCode successFC, String channel, Command command, Exception error,
BundleContext context) {
return testWriteHandlingGeneric(start, transform, valueType, writeType, successFC, channel, command, error,
context, false);
}
@SuppressWarnings({ "null" })
private ModbusDataThingHandler testWriteHandlingGeneric(String start, String transform, ValueType valueType,
String writeType, ModbusWriteFunctionCode successFC, String channel, Command command, Exception error,
BundleContext context, boolean parentIsEndpoint) {
ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502, false);
// Minimally mocked request
@@ -468,7 +526,13 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
doReturn(endpoint).when(task).getEndpoint();
doReturn(request).when(task).getRequest();
Bridge poller = createPollerMock("poller1", task);
final Bridge parent;
if (parentIsEndpoint) {
parent = createTcpMock();
addThing(parent);
} else {
parent = createPollerMock("poller1", task);
}
Configuration dataConfig = new Configuration();
dataConfig.put("readStart", "");
@@ -479,7 +543,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
String thingId = "write";
ModbusDataThingHandler dataHandler = createDataHandler(thingId, poller,
ModbusDataThingHandler dataHandler = createDataHandler(thingId, parent,
builder -> builder.withConfiguration(dataConfig), context);
assertThat(dataHandler.getThing().getStatus(), is(equalTo(ThingStatus.ONLINE)));
@@ -596,6 +660,25 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
assertSingleStateUpdate(dataHandler, CHANNEL_STRING, is(equalTo(new StringType("ON"))));
}
@Test
public void testWriteWithDataAsChildOfEndpoint() throws InvalidSyntaxException {
captureModbusWrites();
mockTransformation("MULTIPLY", new MultiplyTransformation());
ModbusDataThingHandler dataHandler = testWriteHandlingGeneric("50", "MULTIPLY(10)",
ModbusConstants.ValueType.BIT, "coil", ModbusWriteFunctionCode.WRITE_COIL, "number",
new DecimalType("2"), null, bundleContext, /* parent is endpoint */true);
assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_SUCCESS, is(notNullValue(State.class)));
assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_ERROR, is(nullValue(State.class)));
assertThat(writeRequests.size(), is(equalTo(1)));
ModbusWriteRequestBlueprint writeRequest = writeRequests.get(0);
assertThat(writeRequest.getFunctionCode(), is(equalTo(ModbusWriteFunctionCode.WRITE_COIL)));
assertThat(writeRequest.getReference(), is(equalTo(50)));
assertThat(((ModbusWriteCoilRequestBlueprint) writeRequest).getCoils().size(), is(equalTo(1)));
// Since transform output is non-zero, it is mapped as "true"
assertThat(((ModbusWriteCoilRequestBlueprint) writeRequest).getCoils().getBit(0), is(equalTo(true)));
}
@Test
public void testWriteRealTransformation() throws InvalidSyntaxException {
captureModbusWrites();
@@ -837,14 +920,94 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
waitForAssert(() -> verify((ModbusPollerThingHandler) poller.getHandler()).refresh());
}
private static Stream<Arguments> provideArgsForUpdateThenCommandFromItem()
{
return Stream.of(//
// ON/OFF commands
Arguments.of((short) 0b1011_0100_0000_1111, "1", (short) 0b1011_0100_0000_1101, OnOffType.OFF),
Arguments.of((short) 0b1011_0100_0000_1111, "4", (short) 0b1011_0100_0001_1111, OnOffType.ON),
// OPEN/CLOSED commands
Arguments.of((short) 0b1011_0100_0000_1111, "1", (short) 0b1011_0100_0000_1101, OpenClosedType.CLOSED),
Arguments.of((short) 0b1011_0100_0000_1111, "4", (short) 0b1011_0100_0001_1111, OpenClosedType.OPEN),
// DecimalType commands
Arguments.of((short) 0b1011_0100_0000_1111, "1", (short) 0b1011_0100_0000_1101, new DecimalType(0)),
Arguments.of((short) 0b1011_0100_0010_1111, "5", (short) 0b1011_0100_0000_1111, new DecimalType(0)),
Arguments.of((short) 0b1011_0100_0000_1111, "4", (short) 0b1011_0100_0001_1111, new DecimalType(5)),
Arguments.of((short) 0b1011_0100_0000_1111, "15", (short) 0b0011_0100_0000_1111, new DecimalType(0))
).flatMap(a -> {
// parametrize by channel (yes, it does not matter what channel is used, commands are interpreted all the
// same)
Stream<String> channels = Stream.of("switch", "number", "contact");
return channels.map(channel -> appendArg(a, channel));
});
}
@ParameterizedTest
@MethodSource("provideArgsForUpdateThenCommandFromItem")
public void testUpdateFromHandlerThenCommandFromItem(short stateUpdateFromHandler, String bitIndex,
short expectedWriteDataToSlave, Command commandFromItem, String channel) {
int expectedWriteDataToSlaveUnsigned = expectedWriteDataToSlave & 0xFFFF;
captureModbusWrites();
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L); // 0 -> non polling
pollerConfig.put("start", "2");
pollerConfig.put("length", "3");
pollerConfig.put("type", ModbusBindingConstantsInternal.READ_TYPE_HOLDING_REGISTER);
ThingUID pollerUID = new ThingUID(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_POLLER, "realPoller");
Bridge poller = BridgeBuilder.create(ModbusBindingConstantsInternal.THING_TYPE_MODBUS_POLLER, pollerUID)
.withLabel("label for realPoller").withConfiguration(pollerConfig)
.withBridge(realEndpointWithMockedComms.getUID()).build();
addThing(poller);
assertEquals(ThingStatus.ONLINE, poller.getStatus(), poller.getStatusInfo().getDescription());
Configuration dataConfig = new Configuration();
dataConfig.put("writeStart", "3." + bitIndex);
dataConfig.put("writeValueType", "bit");
dataConfig.put("writeType", "holding");
String thingId = "read1";
ModbusDataThingHandler dataHandler = createDataHandler(thingId, poller,
builder -> builder.withConfiguration(dataConfig), bundleContext);
assertEquals(ThingStatus.ONLINE, dataHandler.getThing().getStatus());
assertEquals(pollerUID, dataHandler.getThing().getBridgeUID());
AsyncModbusReadResult result = new AsyncModbusReadResult(Mockito.mock(ModbusReadRequestBlueprint.class),
new ModbusRegisterArray(/* register 2, dummy data */0, /* register 3 */ stateUpdateFromHandler,
/* register 4, dummy data */9));
// poller receives some data (and therefore data as well)
getPollerCallback(((ModbusPollerThingHandler) poller.getHandler())).handle(result);
dataHandler.handleCommand(new ChannelUID(dataHandler.getThing().getUID(), channel), commandFromItem);
// Assert data written
{
assertEquals(1, writeRequests.size());
ModbusWriteRequestBlueprint writeRequest = writeRequests.get(0);
assertEquals(writeRequest.getFunctionCode(), ModbusWriteFunctionCode.WRITE_SINGLE_REGISTER);
assertEquals(writeRequest.getReference(), 3);
assertEquals(((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().size(), 1);
assertEquals(expectedWriteDataToSlaveUnsigned,
((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().getRegister(0));
}
}
private void testInitGeneric(ModbusReadFunctionCode pollerFunctionCode, Configuration config,
Consumer<ThingStatusInfo> statusConsumer) {
testInitGeneric(pollerFunctionCode, 0, config, statusConsumer);
}
/**
*
* @param pollerFunctionCode poller function code. Use null if you want to have data thing direct child of endpoint
* thing
* @param pollerStart start index of poller
* @param config thing config
* @param statusConsumer assertion method for data thingstatus
*/
private void testInitGeneric(ModbusReadFunctionCode pollerFunctionCode, Configuration config,
private void testInitGeneric(ModbusReadFunctionCode pollerFunctionCode, int pollerStart, Configuration config,
Consumer<ThingStatusInfo> statusConsumer) {
int pollLength = 3;
@@ -857,6 +1020,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
// Minimally mocked request
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
doReturn(pollerStart).when(request).getReference();
doReturn(pollLength).when(request).getDataLength();
doReturn(pollerFunctionCode).when(request).getFunctionCode();
@@ -947,7 +1111,19 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
dataConfig.put("writeValueType", "int8");
dataConfig.put("writeType", "holding");
testInitGeneric(null, dataConfig, status -> {
assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
assertEquals(ThingStatus.OFFLINE, status.getStatus(), status.getDescription());
assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
});
}
@Test
public void testWriteHoldingBitDataWrongWriteType() {
Configuration dataConfig = new Configuration();
dataConfig.put("writeStart", "0.15");
dataConfig.put("writeValueType", "bit");
dataConfig.put("writeType", "coil"); // X.Y writeStart only applicable with holding
testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, dataConfig, status -> {
assertEquals(ThingStatus.OFFLINE, status.getStatus(), status.getDescription());
assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
});
}
@@ -955,11 +1131,85 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
@Test
public void testWriteHoldingBitData() {
Configuration dataConfig = new Configuration();
dataConfig.put("writeStart", "0");
dataConfig.put("writeStart", "0.15");
dataConfig.put("writeValueType", "bit");
dataConfig.put("writeType", "holding");
testInitGeneric(null, dataConfig, status -> {
assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, dataConfig, status -> {
assertEquals(status.getStatus(), ThingStatus.ONLINE, status.getDescription());
});
}
@Test
public void testWriteHoldingInt8WithSubIndexData() {
Configuration dataConfig = new Configuration();
dataConfig.put("writeStart", "1.0");
dataConfig.put("writeValueType", "int8");
dataConfig.put("writeType", "holding");
// OFFLINE since sub-register writes are not supported for other than bit
testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, dataConfig, status -> {
assertEquals(status.getStatus(), ThingStatus.OFFLINE, status.getDescription());
});
}
@Test
public void testWriteHoldingBitDataRegisterOutOfBounds() {
Configuration dataConfig = new Configuration();
// in this test poller reads from register 2. Register 1 is out of bounds
dataConfig.put("writeStart", "1.15");
dataConfig.put("writeValueType", "bit");
dataConfig.put("writeType", "holding");
testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, /* poller start */2, dataConfig, status -> {
assertEquals(ThingStatus.OFFLINE, status.getStatus(), status.getDescription());
assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
});
}
@Test
public void testWriteHoldingBitDataRegisterOutOfBounds2() {
Configuration dataConfig = new Configuration();
// register 3 is the last one polled, 4 is out of bounds
dataConfig.put("writeStart", "4.15");
dataConfig.put("writeValueType", "bit");
dataConfig.put("writeType", "holding");
testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, dataConfig, status -> {
assertEquals(ThingStatus.OFFLINE, status.getStatus(), status.getDescription());
});
}
@ParameterizedTest
@CsvSource({ "READ_COILS", "READ_INPUT_DISCRETES", "READ_INPUT_REGISTERS" })
public void testWriteHoldingBitDataWrongPoller(ModbusReadFunctionCode poller) {
Configuration dataConfig = new Configuration();
dataConfig.put("writeStart", "0.15");
dataConfig.put("writeValueType", "bit");
dataConfig.put("writeType", "holding");
testInitGeneric(poller, dataConfig, status -> {
assertEquals(ThingStatus.OFFLINE, status.getStatus(), status.getDescription());
assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
});
}
@Test
public void testWriteHoldingBitParentEndpointData() {
Configuration dataConfig = new Configuration();
dataConfig.put("writeStart", "0.15");
dataConfig.put("writeValueType", "bit");
dataConfig.put("writeType", "holding");
// OFFLINE since we require poller as parent when sub-register writes are used
testInitGeneric(/* poller not as parent */null, dataConfig, status -> {
assertEquals(ThingStatus.OFFLINE, status.getStatus(), status.getDescription());
assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
});
}
@Test
public void testWriteHoldingBitBadStartData() {
Configuration dataConfig = new Configuration();
dataConfig.put("writeStart", "0.16");
dataConfig.put("writeValueType", "int8");
dataConfig.put("writeType", "holding");
testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, dataConfig, status -> {
assertEquals(ThingStatus.OFFLINE, status.getStatus(), status.getDescription());
assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
});
}
@@ -980,7 +1230,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
dataConfig.put("writeValueType", "bit");
// missing writeType --> error
testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig, status -> {
assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
assertEquals(ThingStatus.OFFLINE, status.getStatus(), status.getDescription());
assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
assertThat(status.getDescription(), is(not(equalTo(null))));
});
@@ -995,7 +1245,7 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
dataConfig.put("writeStart", "0");
dataConfig.put("writeType", "coil");
testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig,
status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));
status -> assertEquals(ThingStatus.ONLINE, status.getStatus(), status.getDescription()));
}
@Test
@@ -1110,7 +1360,6 @@ public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
@Test
public void testWriteTransformAndNecessary() {
Configuration dataConfig = new Configuration();
// It's illegal to have start and transform. Just have transform or have all
dataConfig.put("writeStart", "3");
dataConfig.put("writeType", "holding");
dataConfig.put("writeValueType", "int16");