2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.modbus.tests;
15 import static org.hamcrest.CoreMatchers.*;
16 import static org.hamcrest.MatcherAssert.assertThat;
17 import static org.junit.jupiter.api.Assertions.*;
18 import static org.mockito.ArgumentMatchers.*;
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.Mockito.*;
21 import static org.openhab.binding.modbus.internal.ModbusBindingConstantsInternal.*;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Map.Entry;
29 import java.util.concurrent.ScheduledFuture;
30 import java.util.function.Consumer;
31 import java.util.function.Function;
33 import org.apache.commons.lang.StringUtils;
34 import org.hamcrest.Matcher;
35 import org.junit.jupiter.api.AfterEach;
36 import org.junit.jupiter.api.Test;
37 import org.mockito.Mockito;
38 import org.openhab.binding.modbus.handler.EndpointNotInitializedException;
39 import org.openhab.binding.modbus.handler.ModbusPollerThingHandler;
40 import org.openhab.binding.modbus.internal.handler.ModbusDataThingHandler;
41 import org.openhab.binding.modbus.internal.handler.ModbusTcpThingHandler;
42 import org.openhab.core.config.core.Configuration;
43 import org.openhab.core.items.GenericItem;
44 import org.openhab.core.items.Item;
45 import org.openhab.core.library.items.DateTimeItem;
46 import org.openhab.core.library.types.DecimalType;
47 import org.openhab.core.library.types.OnOffType;
48 import org.openhab.core.library.types.OpenClosedType;
49 import org.openhab.core.library.types.StringType;
50 import org.openhab.core.thing.Bridge;
51 import org.openhab.core.thing.ChannelUID;
52 import org.openhab.core.thing.Thing;
53 import org.openhab.core.thing.ThingStatus;
54 import org.openhab.core.thing.ThingStatusDetail;
55 import org.openhab.core.thing.ThingStatusInfo;
56 import org.openhab.core.thing.ThingUID;
57 import org.openhab.core.thing.binding.ThingHandler;
58 import org.openhab.core.thing.binding.builder.BridgeBuilder;
59 import org.openhab.core.thing.binding.builder.ChannelBuilder;
60 import org.openhab.core.thing.binding.builder.ThingBuilder;
61 import org.openhab.core.transform.TransformationException;
62 import org.openhab.core.transform.TransformationService;
63 import org.openhab.core.types.Command;
64 import org.openhab.core.types.RefreshType;
65 import org.openhab.core.types.State;
66 import org.openhab.core.types.UnDefType;
67 import org.openhab.io.transport.modbus.AsyncModbusFailure;
68 import org.openhab.io.transport.modbus.AsyncModbusReadResult;
69 import org.openhab.io.transport.modbus.AsyncModbusWriteResult;
70 import org.openhab.io.transport.modbus.BitArray;
71 import org.openhab.io.transport.modbus.ModbusConstants;
72 import org.openhab.io.transport.modbus.ModbusConstants.ValueType;
73 import org.openhab.io.transport.modbus.ModbusReadFunctionCode;
74 import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
75 import org.openhab.io.transport.modbus.ModbusRegister;
76 import org.openhab.io.transport.modbus.ModbusRegisterArray;
77 import org.openhab.io.transport.modbus.ModbusResponse;
78 import org.openhab.io.transport.modbus.ModbusWriteCoilRequestBlueprint;
79 import org.openhab.io.transport.modbus.ModbusWriteFunctionCode;
80 import org.openhab.io.transport.modbus.ModbusWriteRegisterRequestBlueprint;
81 import org.openhab.io.transport.modbus.ModbusWriteRequestBlueprint;
82 import org.openhab.io.transport.modbus.PollTask;
83 import org.openhab.io.transport.modbus.endpoint.ModbusSlaveEndpoint;
84 import org.openhab.io.transport.modbus.endpoint.ModbusTCPSlaveEndpoint;
85 import org.osgi.framework.BundleContext;
86 import org.osgi.framework.InvalidSyntaxException;
89 * @author Sami Salonen - Initial contribution
91 public class ModbusDataHandlerTest extends AbstractModbusOSGiTest {
93 private final class MultiplyTransformation implements TransformationService {
95 public String transform(String function, String source) throws TransformationException {
96 return String.valueOf(Integer.parseInt(function) * Integer.parseInt(source));
100 private static final Map<String, String> CHANNEL_TO_ACCEPTED_TYPE = new HashMap<>();
102 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_SWITCH, "Switch");
103 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_CONTACT, "Contact");
104 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_DATETIME, "DateTime");
105 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_DIMMER, "Dimmer");
106 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_NUMBER, "Number");
107 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_STRING, "String");
108 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_ROLLERSHUTTER, "Rollershutter");
109 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_LAST_READ_SUCCESS, "DateTime");
110 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_LAST_WRITE_SUCCESS, "DateTime");
111 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_LAST_WRITE_ERROR, "DateTime");
112 CHANNEL_TO_ACCEPTED_TYPE.put(CHANNEL_LAST_READ_ERROR, "DateTime");
114 private List<ModbusWriteRequestBlueprint> writeRequests = new ArrayList<>();
117 public void tearDown() {
118 writeRequests.clear();
121 private void captureModbusWrites() {
122 Mockito.when(comms.submitOneTimeWrite(any(), any(), any())).then(invocation -> {
123 ModbusWriteRequestBlueprint task = (ModbusWriteRequestBlueprint) invocation.getArgument(0);
124 writeRequests.add(task);
125 return Mockito.mock(ScheduledFuture.class);
129 private Bridge createPollerMock(String pollerId, PollTask task) {
131 ThingUID thingUID = new ThingUID(THING_TYPE_MODBUS_POLLER, pollerId);
132 BridgeBuilder builder = BridgeBuilder.create(THING_TYPE_MODBUS_POLLER, thingUID)
133 .withLabel("label for " + pollerId);
134 for (Entry<String, String> entry : CHANNEL_TO_ACCEPTED_TYPE.entrySet()) {
135 String channelId = entry.getKey();
136 String channelAcceptedType = entry.getValue();
137 builder = builder.withChannel(
138 ChannelBuilder.create(new ChannelUID(thingUID, channelId), channelAcceptedType).build());
140 poller = builder.build();
141 poller.setStatusInfo(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, ""));
143 ModbusPollerThingHandler mockHandler = Mockito.mock(ModbusPollerThingHandler.class);
144 doReturn(task.getRequest()).when(mockHandler).getRequest();
145 assert comms != null;
146 doReturn(comms).when(mockHandler).getCommunicationInterface();
147 doReturn(task.getEndpoint()).when(comms).getEndpoint();
148 poller.setHandler(mockHandler);
149 assertSame(poller.getHandler(), mockHandler);
150 assertSame(((ModbusPollerThingHandler) poller.getHandler()).getCommunicationInterface().getEndpoint(),
152 assertSame(((ModbusPollerThingHandler) poller.getHandler()).getRequest(), task.getRequest());
158 private Bridge createTcpMock() {
159 ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502);
160 Bridge tcpBridge = ModbusPollerThingHandlerTest.createTcpThingBuilder("tcp1").build();
161 ModbusTcpThingHandler tcpThingHandler = Mockito.mock(ModbusTcpThingHandler.class);
162 tcpBridge.setStatusInfo(new ThingStatusInfo(ThingStatus.ONLINE, ThingStatusDetail.NONE, ""));
163 tcpBridge.setHandler(tcpThingHandler);
164 doReturn(comms).when(tcpThingHandler).getCommunicationInterface();
166 doReturn(0).when(tcpThingHandler).getSlaveId();
167 } catch (EndpointNotInitializedException e) {
168 // not raised -- we are mocking return value only, not actually calling the method
169 throw new IllegalStateException();
171 tcpThingHandler.initialize();
172 assertThat(tcpBridge.getStatus(), is(equalTo(ThingStatus.ONLINE)));
176 private ModbusDataThingHandler createDataHandler(String id, Bridge bridge,
177 Function<ThingBuilder, ThingBuilder> builderConfigurator) {
178 return createDataHandler(id, bridge, builderConfigurator, null);
181 private ModbusDataThingHandler createDataHandler(String id, Bridge bridge,
182 Function<ThingBuilder, ThingBuilder> builderConfigurator, BundleContext context) {
183 return createDataHandler(id, bridge, builderConfigurator, context, true);
186 private ModbusDataThingHandler createDataHandler(String id, Bridge bridge,
187 Function<ThingBuilder, ThingBuilder> builderConfigurator, BundleContext context,
188 boolean autoCreateItemsAndLinkToChannels) {
189 ThingUID thingUID = new ThingUID(THING_TYPE_MODBUS_DATA, id);
190 ThingBuilder builder = ThingBuilder.create(THING_TYPE_MODBUS_DATA, thingUID).withLabel("label for " + id);
191 Map<String, ChannelUID> toBeLinked = new HashMap<>();
192 for (Entry<String, String> entry : CHANNEL_TO_ACCEPTED_TYPE.entrySet()) {
193 String channelId = entry.getKey();
194 String channelAcceptedType = entry.getValue();
195 ChannelUID channelUID = new ChannelUID(thingUID, channelId);
196 builder = builder.withChannel(ChannelBuilder.create(channelUID, channelAcceptedType).build());
198 if (autoCreateItemsAndLinkToChannels) {
199 // Create item of correct type and link it to channel
200 String itemName = getItemName(channelUID);
201 final GenericItem item;
202 if (channelId.startsWith("last") || channelId.equals("datetime")) {
203 item = new DateTimeItem(itemName);
205 item = coreItemFactory.createItem(StringUtils.capitalize(channelId), itemName);
207 assertThat(String.format("Could not determine correct item type for %s", channelId), item,
211 toBeLinked.put(itemName, channelUID);
214 if (builderConfigurator != null) {
215 builder = builderConfigurator.apply(builder);
218 Thing dataThing = builder.withBridge(bridge.getUID()).build();
221 // Link after the things and items have been created
222 for (Entry<String, ChannelUID> entry : toBeLinked.entrySet()) {
223 linkItem(entry.getKey(), entry.getValue());
225 return (ModbusDataThingHandler) dataThing.getHandler();
228 private String getItemName(ChannelUID channelUID) {
229 return channelUID.toString().replace(':', '_') + "_item";
232 private void assertSingleStateUpdate(ModbusDataThingHandler handler, String channel, Matcher<State> matcher) {
233 waitForAssert(() -> {
234 ChannelUID channelUID = new ChannelUID(handler.getThing().getUID(), channel);
235 String itemName = getItemName(channelUID);
236 Item item = itemRegistry.get(itemName);
237 assertThat(String.format("Item %s is not available from item registry", itemName), item,
240 List<State> updates = getStateUpdates(itemName);
241 if (updates != null) {
243 String.format("Many updates found, expected one: %s", Arrays.deepToString(updates.toArray())),
244 updates.size(), is(equalTo(1)));
246 State state = updates == null ? null : updates.get(0);
247 assertThat(String.format("%s %s, state %s of type %s", item.getClass().getSimpleName(), itemName, state,
248 state == null ? null : state.getClass().getSimpleName()), state, is(matcher));
252 private void assertSingleStateUpdate(ModbusDataThingHandler handler, String channel, State state) {
253 assertSingleStateUpdate(handler, channel, is(equalTo(state)));
256 private void testOutOfBoundsGeneric(int pollStart, int pollLength, String start,
257 ModbusReadFunctionCode functionCode, ValueType valueType, ThingStatus expectedStatus) {
258 testOutOfBoundsGeneric(pollStart, pollLength, start, functionCode, valueType, expectedStatus, null);
261 private void testOutOfBoundsGeneric(int pollStart, int pollLength, String start,
262 ModbusReadFunctionCode functionCode, ValueType valueType, ThingStatus expectedStatus,
263 BundleContext context) {
264 ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502);
266 // Minimally mocked request
267 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
268 doReturn(pollStart).when(request).getReference();
269 doReturn(pollLength).when(request).getDataLength();
270 doReturn(functionCode).when(request).getFunctionCode();
272 PollTask task = Mockito.mock(PollTask.class);
273 doReturn(endpoint).when(task).getEndpoint();
274 doReturn(request).when(task).getRequest();
276 Bridge pollerThing = createPollerMock("poller1", task);
278 Configuration dataConfig = new Configuration();
279 dataConfig.put("readStart", start);
280 dataConfig.put("readTransform", "default");
281 dataConfig.put("readValueType", valueType.getConfigValue());
282 ModbusDataThingHandler dataHandler = createDataHandler("data1", pollerThing,
283 builder -> builder.withConfiguration(dataConfig), context);
284 assertThat(dataHandler.getThing().getStatusInfo().getDescription(), dataHandler.getThing().getStatus(),
285 is(equalTo(expectedStatus)));
289 public void testInitCoilsOutOfIndex() {
290 testOutOfBoundsGeneric(4, 3, "8", ModbusReadFunctionCode.READ_COILS, ModbusConstants.ValueType.BIT,
291 ThingStatus.OFFLINE);
295 public void testInitCoilsOutOfIndex2() {
296 // Reading coils 4, 5, 6. Coil 7 is out of bounds
297 testOutOfBoundsGeneric(4, 3, "7", ModbusReadFunctionCode.READ_COILS, ModbusConstants.ValueType.BIT,
298 ThingStatus.OFFLINE);
302 public void testInitCoilsOK() {
303 // Reading coils 4, 5, 6. Coil 6 is OK
304 testOutOfBoundsGeneric(4, 3, "6", ModbusReadFunctionCode.READ_COILS, ModbusConstants.ValueType.BIT,
309 public void testInitRegistersWithBitOutOfIndex() {
310 testOutOfBoundsGeneric(4, 3, "8.0", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
311 ModbusConstants.ValueType.BIT, ThingStatus.OFFLINE);
315 public void testInitRegistersWithBitOutOfIndex2() {
316 testOutOfBoundsGeneric(4, 3, "7.16", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
317 ModbusConstants.ValueType.BIT, ThingStatus.OFFLINE);
321 public void testInitRegistersWithBitOK() {
322 testOutOfBoundsGeneric(4, 3, "6.0", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
323 ModbusConstants.ValueType.BIT, ThingStatus.ONLINE);
327 public void testInitRegistersWithBitOK2() {
328 testOutOfBoundsGeneric(4, 3, "6.15", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
329 ModbusConstants.ValueType.BIT, ThingStatus.ONLINE);
333 public void testInitRegistersWithInt8OutOfIndex() {
334 testOutOfBoundsGeneric(4, 3, "8.0", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
335 ModbusConstants.ValueType.INT8, ThingStatus.OFFLINE);
339 public void testInitRegistersWithInt8OutOfIndex2() {
340 testOutOfBoundsGeneric(4, 3, "7.2", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
341 ModbusConstants.ValueType.INT8, ThingStatus.OFFLINE);
345 public void testInitRegistersWithInt8OK() {
346 testOutOfBoundsGeneric(4, 3, "6.0", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
347 ModbusConstants.ValueType.INT8, ThingStatus.ONLINE);
351 public void testInitRegistersWithInt8OK2() {
352 testOutOfBoundsGeneric(4, 3, "6.1", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
353 ModbusConstants.ValueType.INT8, ThingStatus.ONLINE);
357 public void testInitRegistersWithInt16OK() {
358 // Poller reading registers 4, 5, 6. Register 6 is OK
359 testOutOfBoundsGeneric(4, 3, "6", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
360 ModbusConstants.ValueType.INT16, ThingStatus.ONLINE);
364 public void testInitRegistersWithInt16OutOfBounds() {
365 // Poller reading registers 4, 5, 6. Register 7 is out-of-bounds
366 testOutOfBoundsGeneric(4, 3, "7", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
367 ModbusConstants.ValueType.INT16, ThingStatus.OFFLINE);
371 public void testInitRegistersWithInt16OutOfBounds2() {
372 testOutOfBoundsGeneric(4, 3, "8", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
373 ModbusConstants.ValueType.INT16, ThingStatus.OFFLINE);
377 public void testInitRegistersWithInt16NoDecimalFormatAllowed() {
378 testOutOfBoundsGeneric(4, 3, "7.0", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
379 ModbusConstants.ValueType.INT16, ThingStatus.OFFLINE);
383 public void testInitRegistersWithInt32OK() {
384 testOutOfBoundsGeneric(4, 3, "5", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
385 ModbusConstants.ValueType.INT32, ThingStatus.ONLINE);
389 public void testInitRegistersWithInt32OutOfBounds() {
390 testOutOfBoundsGeneric(4, 3, "6", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
391 ModbusConstants.ValueType.INT32, ThingStatus.OFFLINE);
395 public void testInitRegistersWithInt32AtTheEdge() {
396 testOutOfBoundsGeneric(4, 3, "5", ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
397 ModbusConstants.ValueType.INT32, ThingStatus.ONLINE);
400 private ModbusDataThingHandler testReadHandlingGeneric(ModbusReadFunctionCode functionCode, String start,
401 String transform, ValueType valueType, BitArray bits, ModbusRegisterArray registers, Exception error) {
402 return testReadHandlingGeneric(functionCode, start, transform, valueType, bits, registers, error, null);
405 private ModbusDataThingHandler testReadHandlingGeneric(ModbusReadFunctionCode functionCode, String start,
406 String transform, ValueType valueType, BitArray bits, ModbusRegisterArray registers, Exception error,
407 BundleContext context) {
408 return testReadHandlingGeneric(functionCode, start, transform, valueType, bits, registers, error, context,
412 @SuppressWarnings({ "null" })
413 private ModbusDataThingHandler testReadHandlingGeneric(ModbusReadFunctionCode functionCode, String start,
414 String transform, ValueType valueType, BitArray bits, ModbusRegisterArray registers, Exception error,
415 BundleContext context, boolean autoCreateItemsAndLinkToChannels) {
416 ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502);
420 // Minimally mocked request
421 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
422 doReturn(pollLength).when(request).getDataLength();
423 doReturn(functionCode).when(request).getFunctionCode();
425 PollTask task = Mockito.mock(PollTask.class);
426 doReturn(endpoint).when(task).getEndpoint();
427 doReturn(request).when(task).getRequest();
429 Bridge poller = createPollerMock("poller1", task);
431 Configuration dataConfig = new Configuration();
432 dataConfig.put("readStart", start);
433 dataConfig.put("readTransform", transform);
434 dataConfig.put("readValueType", valueType.getConfigValue());
436 String thingId = "read1";
437 ModbusDataThingHandler dataHandler = createDataHandler(thingId, poller,
438 builder -> builder.withConfiguration(dataConfig), context, autoCreateItemsAndLinkToChannels);
440 assertThat(dataHandler.getThing().getStatus(), is(equalTo(ThingStatus.ONLINE)));
444 assertNull(registers);
446 AsyncModbusReadResult result = new AsyncModbusReadResult(request, bits);
447 dataHandler.onReadResult(result);
448 } else if (registers != null) {
451 AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
452 dataHandler.onReadResult(result);
455 assertNull(registers);
456 assertNotNull(error);
457 AsyncModbusFailure<ModbusReadRequestBlueprint> result = new AsyncModbusFailure<ModbusReadRequestBlueprint>(
459 dataHandler.handleReadError(result);
464 @SuppressWarnings({ "null" })
465 private ModbusDataThingHandler testWriteHandlingGeneric(String start, String transform, ValueType valueType,
466 String writeType, ModbusWriteFunctionCode successFC, String channel, Command command, Exception error,
467 BundleContext context) {
468 ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502);
470 // Minimally mocked request
471 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
473 PollTask task = Mockito.mock(PollTask.class);
474 doReturn(endpoint).when(task).getEndpoint();
475 doReturn(request).when(task).getRequest();
477 Bridge poller = createPollerMock("poller1", task);
479 Configuration dataConfig = new Configuration();
480 dataConfig.put("readStart", "");
481 dataConfig.put("writeStart", start);
482 dataConfig.put("writeTransform", transform);
483 dataConfig.put("writeValueType", valueType.getConfigValue());
484 dataConfig.put("writeType", writeType);
486 String thingId = "write";
488 ModbusDataThingHandler dataHandler = createDataHandler(thingId, poller,
489 builder -> builder.withConfiguration(dataConfig), context);
491 assertThat(dataHandler.getThing().getStatus(), is(equalTo(ThingStatus.ONLINE)));
493 dataHandler.handleCommand(new ChannelUID(dataHandler.getThing().getUID(), channel), command);
496 dataHandler.handleReadError(new AsyncModbusFailure<ModbusReadRequestBlueprint>(request, error));
498 ModbusResponse resp = new ModbusResponse() {
501 public int getFunctionCode() {
502 return successFC.getFunctionCode();
506 .onWriteResponse(new AsyncModbusWriteResult(Mockito.mock(ModbusWriteRequestBlueprint.class), resp));
512 public void testOnError() {
513 ModbusDataThingHandler dataHandler = testReadHandlingGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
514 "0.0", "default", ModbusConstants.ValueType.BIT, null, null, new Exception("fooerror"));
516 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_READ_ERROR, is(notNullValue(State.class)));
520 public void testOnRegistersInt16StaticTransformation() {
521 ModbusDataThingHandler dataHandler = testReadHandlingGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
522 "0", "-3", ModbusConstants.ValueType.INT16, null,
523 new ModbusRegisterArray(new ModbusRegister[] { new ModbusRegister((byte) 0xff, (byte) 0xfd) }), null);
525 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_READ_SUCCESS, is(notNullValue(State.class)));
526 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_READ_ERROR, is(nullValue(State.class)));
528 // -3 converts to "true"
529 assertSingleStateUpdate(dataHandler, CHANNEL_CONTACT, is(nullValue(State.class)));
530 assertSingleStateUpdate(dataHandler, CHANNEL_SWITCH, is(nullValue(State.class)));
531 assertSingleStateUpdate(dataHandler, CHANNEL_DIMMER, is(nullValue(State.class)));
532 assertSingleStateUpdate(dataHandler, CHANNEL_NUMBER, new DecimalType(-3));
533 // roller shutter fails since -3 is invalid value (not between 0...100)
534 // assertThatStateContains(state, CHANNEL_ROLLERSHUTTER, new PercentType(1));
535 assertSingleStateUpdate(dataHandler, CHANNEL_STRING, new StringType("-3"));
536 // no datetime, conversion not possible without transformation
540 public void testOnRegistersRealTransformation() {
541 mockTransformation("MULTIPLY", new MultiplyTransformation());
542 ModbusDataThingHandler dataHandler = testReadHandlingGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
543 "0", "MULTIPLY(10)", ModbusConstants.ValueType.INT16, null,
544 new ModbusRegisterArray(new ModbusRegister[] { new ModbusRegister((byte) 0xff, (byte) 0xfd) }), null,
547 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_READ_SUCCESS, is(notNullValue(State.class)));
548 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_READ_ERROR, is(nullValue(State.class)));
550 // transformation output (-30) is not valid for contact or switch
551 assertSingleStateUpdate(dataHandler, CHANNEL_CONTACT, is(nullValue(State.class)));
552 assertSingleStateUpdate(dataHandler, CHANNEL_SWITCH, is(nullValue(State.class)));
553 // -30 is not valid value for Dimmer (PercentType) (not between 0...100)
554 assertSingleStateUpdate(dataHandler, CHANNEL_DIMMER, is(nullValue(State.class)));
555 assertSingleStateUpdate(dataHandler, CHANNEL_NUMBER, new DecimalType(-30));
556 // roller shutter fails since -3 is invalid value (not between 0...100)
557 assertSingleStateUpdate(dataHandler, CHANNEL_ROLLERSHUTTER, is(nullValue(State.class)));
558 assertSingleStateUpdate(dataHandler, CHANNEL_STRING, new StringType("-30"));
559 // no datetime, conversion not possible without transformation
563 public void testOnRegistersNaNFloatInRegisters() throws InvalidSyntaxException {
564 ModbusDataThingHandler dataHandler = testReadHandlingGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
565 "0", "default", ModbusConstants.ValueType.FLOAT32, null, new ModbusRegisterArray(
566 // equivalent of floating point NaN
567 new ModbusRegister[] { new ModbusRegister((byte) 0x7f, (byte) 0xc0),
568 new ModbusRegister((byte) 0x00, (byte) 0x00) }),
569 null, bundleContext);
571 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_READ_SUCCESS, is(notNullValue(State.class)));
572 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_READ_ERROR, is(nullValue(State.class)));
574 // UNDEF is treated as "boolean true" (OPEN/ON) since it is != 0.
575 assertSingleStateUpdate(dataHandler, CHANNEL_CONTACT, OpenClosedType.OPEN);
576 assertSingleStateUpdate(dataHandler, CHANNEL_SWITCH, OnOffType.ON);
577 assertSingleStateUpdate(dataHandler, CHANNEL_DIMMER, OnOffType.ON);
578 assertSingleStateUpdate(dataHandler, CHANNEL_NUMBER, UnDefType.UNDEF);
579 assertSingleStateUpdate(dataHandler, CHANNEL_ROLLERSHUTTER, UnDefType.UNDEF);
580 assertSingleStateUpdate(dataHandler, CHANNEL_STRING, UnDefType.UNDEF);
584 public void testOnRegistersRealTransformation2() throws InvalidSyntaxException {
585 mockTransformation("ONOFF", new TransformationService() {
588 public String transform(String function, String source) throws TransformationException {
589 return Integer.parseInt(source) != 0 ? "ON" : "OFF";
592 ModbusDataThingHandler dataHandler = testReadHandlingGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS,
593 "0", "ONOFF(10)", ModbusConstants.ValueType.INT16, null,
594 new ModbusRegisterArray(new ModbusRegister[] { new ModbusRegister((byte) 0xff, (byte) 0xfd) }), null,
597 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_READ_SUCCESS, is(notNullValue(State.class)));
598 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_READ_ERROR, is(nullValue(State.class)));
600 assertSingleStateUpdate(dataHandler, CHANNEL_CONTACT, is(nullValue(State.class)));
601 assertSingleStateUpdate(dataHandler, CHANNEL_SWITCH, is(equalTo(OnOffType.ON)));
602 assertSingleStateUpdate(dataHandler, CHANNEL_DIMMER, is(equalTo(OnOffType.ON)));
603 assertSingleStateUpdate(dataHandler, CHANNEL_NUMBER, is(nullValue(State.class)));
604 assertSingleStateUpdate(dataHandler, CHANNEL_ROLLERSHUTTER, is(nullValue(State.class)));
605 assertSingleStateUpdate(dataHandler, CHANNEL_STRING, is(equalTo(new StringType("ON"))));
609 public void testWriteRealTransformation() throws InvalidSyntaxException {
610 captureModbusWrites();
611 mockTransformation("MULTIPLY", new MultiplyTransformation());
612 ModbusDataThingHandler dataHandler = testWriteHandlingGeneric("50", "MULTIPLY(10)",
613 ModbusConstants.ValueType.BIT, "coil", ModbusWriteFunctionCode.WRITE_COIL, "number",
614 new DecimalType("2"), null, bundleContext);
616 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_SUCCESS, is(notNullValue(State.class)));
617 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_ERROR, is(nullValue(State.class)));
618 assertThat(writeRequests.size(), is(equalTo(1)));
619 ModbusWriteRequestBlueprint writeRequest = writeRequests.get(0);
620 assertThat(writeRequest.getFunctionCode(), is(equalTo(ModbusWriteFunctionCode.WRITE_COIL)));
621 assertThat(writeRequest.getReference(), is(equalTo(50)));
622 assertThat(((ModbusWriteCoilRequestBlueprint) writeRequest).getCoils().size(), is(equalTo(1)));
623 // Since transform output is non-zero, it is mapped as "true"
624 assertThat(((ModbusWriteCoilRequestBlueprint) writeRequest).getCoils().getBit(0), is(equalTo(true)));
628 public void testWriteRealTransformation2() throws InvalidSyntaxException {
629 captureModbusWrites();
630 mockTransformation("ZERO", new TransformationService() {
633 public String transform(String function, String source) throws TransformationException {
637 ModbusDataThingHandler dataHandler = testWriteHandlingGeneric("50", "ZERO(foobar)",
638 ModbusConstants.ValueType.BIT, "coil", ModbusWriteFunctionCode.WRITE_COIL, "number",
639 new DecimalType("2"), null, bundleContext);
641 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_SUCCESS, is(notNullValue(State.class)));
642 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_ERROR, is(nullValue(State.class)));
643 assertThat(writeRequests.size(), is(equalTo(1)));
644 ModbusWriteRequestBlueprint writeRequest = writeRequests.get(0);
645 assertThat(writeRequest.getFunctionCode(), is(equalTo(ModbusWriteFunctionCode.WRITE_COIL)));
646 assertThat(writeRequest.getReference(), is(equalTo(50)));
647 assertThat(((ModbusWriteCoilRequestBlueprint) writeRequest).getCoils().size(), is(equalTo(1)));
648 // Since transform output is zero, it is mapped as "false"
649 assertThat(((ModbusWriteCoilRequestBlueprint) writeRequest).getCoils().getBit(0), is(equalTo(false)));
653 public void testWriteRealTransformation3() throws InvalidSyntaxException {
654 captureModbusWrites();
655 mockTransformation("RANDOM", new TransformationService() {
658 public String transform(String function, String source) throws TransformationException {
662 ModbusDataThingHandler dataHandler = testWriteHandlingGeneric("50", "RANDOM(foobar)",
663 ModbusConstants.ValueType.INT16, "holding", ModbusWriteFunctionCode.WRITE_SINGLE_REGISTER, "number",
664 new DecimalType("2"), null, bundleContext);
666 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_SUCCESS, is(notNullValue(State.class)));
667 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_ERROR, is(nullValue(State.class)));
668 assertThat(writeRequests.size(), is(equalTo(1)));
669 ModbusWriteRequestBlueprint writeRequest = writeRequests.get(0);
670 assertThat(writeRequest.getFunctionCode(), is(equalTo(ModbusWriteFunctionCode.WRITE_SINGLE_REGISTER)));
671 assertThat(writeRequest.getReference(), is(equalTo(50)));
672 assertThat(((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().size(), is(equalTo(1)));
673 assertThat(((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().getRegister(0).getValue(),
678 public void testWriteRealTransformation4() throws InvalidSyntaxException {
679 captureModbusWrites();
680 mockTransformation("JSON", new TransformationService() {
683 public String transform(String function, String source) throws TransformationException {
685 + "\"functionCode\": 16,"//
686 + "\"address\": 5412,"//
687 + "\"value\": [1, 0, 5]"//
690 + "\"functionCode\": 6,"//
691 + "\"address\": 555,"//
696 ModbusDataThingHandler dataHandler = testWriteHandlingGeneric("50", "JSON(foobar)",
697 ModbusConstants.ValueType.INT16, "holding", ModbusWriteFunctionCode.WRITE_MULTIPLE_REGISTERS, "number",
698 new DecimalType("2"), null, bundleContext);
700 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_SUCCESS, is(notNullValue(State.class)));
701 assertSingleStateUpdate(dataHandler, CHANNEL_LAST_WRITE_ERROR, is(nullValue(State.class)));
702 assertThat(writeRequests.size(), is(equalTo(2)));
704 ModbusWriteRequestBlueprint writeRequest = writeRequests.get(0);
705 assertThat(writeRequest.getFunctionCode(), is(equalTo(ModbusWriteFunctionCode.WRITE_MULTIPLE_REGISTERS)));
706 assertThat(writeRequest.getReference(), is(equalTo(5412)));
707 assertThat(((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().size(), is(equalTo(3)));
708 assertThat(((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().getRegister(0).getValue(),
710 assertThat(((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().getRegister(1).getValue(),
712 assertThat(((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().getRegister(2).getValue(),
716 ModbusWriteRequestBlueprint writeRequest = writeRequests.get(1);
717 assertThat(writeRequest.getFunctionCode(), is(equalTo(ModbusWriteFunctionCode.WRITE_SINGLE_REGISTER)));
718 assertThat(writeRequest.getReference(), is(equalTo(555)));
719 assertThat(((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().size(), is(equalTo(1)));
720 assertThat(((ModbusWriteRegisterRequestBlueprint) writeRequest).getRegisters().getRegister(0).getValue(),
725 private void testValueTypeGeneric(ModbusReadFunctionCode functionCode, ValueType valueType,
726 ThingStatus expectedStatus) {
727 ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502);
729 // Minimally mocked request
730 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
731 doReturn(3).when(request).getDataLength();
732 doReturn(functionCode).when(request).getFunctionCode();
734 PollTask task = Mockito.mock(PollTask.class);
735 doReturn(endpoint).when(task).getEndpoint();
736 doReturn(request).when(task).getRequest();
738 Bridge poller = createPollerMock("poller1", task);
740 Configuration dataConfig = new Configuration();
741 dataConfig.put("readStart", "1");
742 dataConfig.put("readTransform", "default");
743 dataConfig.put("readValueType", valueType.getConfigValue());
744 ModbusDataThingHandler dataHandler = createDataHandler("data1", poller,
745 builder -> builder.withConfiguration(dataConfig));
746 assertThat(dataHandler.getThing().getStatus(), is(equalTo(expectedStatus)));
750 public void testCoilDoesNotAcceptFloat32ValueType() {
751 testValueTypeGeneric(ModbusReadFunctionCode.READ_COILS, ModbusConstants.ValueType.FLOAT32, ThingStatus.OFFLINE);
755 public void testCoilAcceptsBitValueType() {
756 testValueTypeGeneric(ModbusReadFunctionCode.READ_COILS, ModbusConstants.ValueType.BIT, ThingStatus.ONLINE);
760 public void testDiscreteInputDoesNotAcceptFloat32ValueType() {
761 testValueTypeGeneric(ModbusReadFunctionCode.READ_INPUT_DISCRETES, ModbusConstants.ValueType.FLOAT32,
762 ThingStatus.OFFLINE);
766 public void testDiscreteInputAcceptsBitValueType() {
767 testValueTypeGeneric(ModbusReadFunctionCode.READ_INPUT_DISCRETES, ModbusConstants.ValueType.BIT,
772 public void testRefreshOnData() throws InterruptedException {
773 ModbusReadFunctionCode functionCode = ModbusReadFunctionCode.READ_COILS;
775 ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502);
779 // Minimally mocked request
780 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
781 doReturn(pollLength).when(request).getDataLength();
782 doReturn(functionCode).when(request).getFunctionCode();
784 PollTask task = Mockito.mock(PollTask.class);
785 doReturn(endpoint).when(task).getEndpoint();
786 doReturn(request).when(task).getRequest();
788 Bridge poller = createPollerMock("poller1", task);
790 Configuration dataConfig = new Configuration();
791 dataConfig.put("readStart", "0");
792 dataConfig.put("readTransform", "default");
793 dataConfig.put("readValueType", "bit");
795 String thingId = "read1";
797 ModbusDataThingHandler dataHandler = createDataHandler(thingId, poller,
798 builder -> builder.withConfiguration(dataConfig), bundleContext);
799 assertThat(dataHandler.getThing().getStatus(), is(equalTo(ThingStatus.ONLINE)));
801 verify(comms, never()).submitOneTimePoll(eq(request), notNull(), notNull());
802 // Reset initial REFRESH commands to data thing channels from the Core
803 reset(poller.getHandler());
804 dataHandler.handleCommand(Mockito.mock(ChannelUID.class), RefreshType.REFRESH);
806 // data handler asynchronously calls the poller.refresh() -- it might take some time
807 // We check that refresh is finally called
808 waitForAssert(() -> verify((ModbusPollerThingHandler) poller.getHandler()).refresh(), 2500, 50);
813 * @param pollerFunctionCode poller function code. Use null if you want to have data thing direct child of endpoint
815 * @param config thing config
816 * @param statusConsumer assertion method for data thingstatus
818 private void testInitGeneric(ModbusReadFunctionCode pollerFunctionCode, Configuration config,
819 Consumer<ThingStatusInfo> statusConsumer) {
823 if (pollerFunctionCode == null) {
824 parent = createTcpMock();
825 ThingHandler foo = parent.getHandler();
828 ModbusSlaveEndpoint endpoint = new ModbusTCPSlaveEndpoint("thisishost", 502);
830 // Minimally mocked request
831 ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
832 doReturn(pollLength).when(request).getDataLength();
833 doReturn(pollerFunctionCode).when(request).getFunctionCode();
835 PollTask task = Mockito.mock(PollTask.class);
836 doReturn(endpoint).when(task).getEndpoint();
837 doReturn(request).when(task).getRequest();
839 parent = createPollerMock("poller1", task);
842 String thingId = "read1";
844 ModbusDataThingHandler dataHandler = createDataHandler(thingId, parent,
845 builder -> builder.withConfiguration(config), bundleContext);
847 statusConsumer.accept(dataHandler.getThing().getStatusInfo());
851 public void testReadOnlyData() {
852 Configuration dataConfig = new Configuration();
853 dataConfig.put("readStart", "0");
854 dataConfig.put("readValueType", "bit");
855 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig,
856 status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));
860 * readValueType=bit should be assumed with coils, so it's ok to skip it
863 public void testReadOnlyDataMissingValueTypeWithCoils() {
864 Configuration dataConfig = new Configuration();
865 dataConfig.put("readStart", "0");
866 // missing value type
867 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig,
868 status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));
872 public void testReadOnlyDataInvalidValueType() {
873 Configuration dataConfig = new Configuration();
874 dataConfig.put("readStart", "0");
875 dataConfig.put("readValueType", "foobar");
876 testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, dataConfig, status -> {
877 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
878 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
883 * We do not assume value type with registers, not ok to skip it
886 public void testReadOnlyDataMissingValueTypeWithRegisters() {
887 Configuration dataConfig = new Configuration();
888 dataConfig.put("readStart", "0");
889 testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, dataConfig, status -> {
890 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
891 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
896 public void testWriteOnlyData() {
897 Configuration dataConfig = new Configuration();
898 dataConfig.put("writeStart", "0");
899 dataConfig.put("writeValueType", "bit");
900 dataConfig.put("writeType", "coil");
901 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig,
902 status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));
906 public void testWriteHoldingInt16Data() {
907 Configuration dataConfig = new Configuration();
908 dataConfig.put("writeStart", "0");
909 dataConfig.put("writeValueType", "int16");
910 dataConfig.put("writeType", "holding");
911 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig,
912 status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));
916 public void testWriteHoldingInt8Data() {
917 Configuration dataConfig = new Configuration();
918 dataConfig.put("writeStart", "0");
919 dataConfig.put("writeValueType", "int8");
920 dataConfig.put("writeType", "holding");
921 testInitGeneric(null, dataConfig, status -> {
922 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
923 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
928 public void testWriteHoldingBitData() {
929 Configuration dataConfig = new Configuration();
930 dataConfig.put("writeStart", "0");
931 dataConfig.put("writeValueType", "bit");
932 dataConfig.put("writeType", "holding");
933 testInitGeneric(null, dataConfig, status -> {
934 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
935 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
940 public void testWriteOnlyDataChildOfEndpoint() {
941 Configuration dataConfig = new Configuration();
942 dataConfig.put("writeStart", "0");
943 dataConfig.put("writeValueType", "bit");
944 dataConfig.put("writeType", "coil");
945 testInitGeneric(null, dataConfig, status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));
949 public void testWriteOnlyDataMissingOneParameter() {
950 Configuration dataConfig = new Configuration();
951 dataConfig.put("writeStart", "0");
952 dataConfig.put("writeValueType", "bit");
953 // missing writeType --> error
954 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig, status -> {
955 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
956 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
957 assertThat(status.getDescription(), is(not(equalTo(null))));
962 * OK to omit writeValueType with coils since bit is assumed
965 public void testWriteOnlyDataMissingValueTypeWithCoilParameter() {
966 Configuration dataConfig = new Configuration();
967 dataConfig.put("writeStart", "0");
968 dataConfig.put("writeType", "coil");
969 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig,
970 status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));
974 public void testWriteOnlyIllegalValueType() {
975 Configuration dataConfig = new Configuration();
976 dataConfig.put("writeStart", "0");
977 dataConfig.put("writeType", "coil");
978 dataConfig.put("writeValueType", "foobar");
979 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig, status -> {
980 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
981 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
986 public void testWriteInvalidType() {
987 Configuration dataConfig = new Configuration();
988 dataConfig.put("writeStart", "0");
989 dataConfig.put("writeType", "foobar");
990 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig, status -> {
991 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
992 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
997 public void testWriteCoilBadStart() {
998 Configuration dataConfig = new Configuration();
999 dataConfig.put("writeStart", "0.4");
1000 dataConfig.put("writeType", "coil");
1001 testInitGeneric(null, dataConfig, status -> {
1002 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
1003 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
1008 public void testWriteHoldingBadStart() {
1009 Configuration dataConfig = new Configuration();
1010 dataConfig.put("writeStart", "0.4");
1011 dataConfig.put("writeType", "holding");
1012 testInitGeneric(null, dataConfig, status -> {
1013 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
1014 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
1019 public void testReadHoldingBadStart() {
1020 Configuration dataConfig = new Configuration();
1021 dataConfig.put("readStart", "0.0");
1022 dataConfig.put("readValueType", "int16");
1023 testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, dataConfig, status -> {
1024 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
1025 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
1030 public void testReadHoldingBadStart2() {
1031 Configuration dataConfig = new Configuration();
1032 dataConfig.put("readStart", "0.0");
1033 dataConfig.put("readValueType", "bit");
1034 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig, status -> {
1035 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
1036 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
1041 public void testReadHoldingOKStart() {
1042 Configuration dataConfig = new Configuration();
1043 dataConfig.put("readStart", "0.0");
1044 dataConfig.put("readType", "holding");
1045 dataConfig.put("readValueType", "bit");
1046 testInitGeneric(ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, dataConfig,
1047 status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));
1051 public void testReadValueTypeIllegal() {
1052 Configuration dataConfig = new Configuration();
1053 dataConfig.put("readStart", "0.0");
1054 dataConfig.put("readType", "holding");
1055 dataConfig.put("readValueType", "foobar");
1056 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig, status -> {
1057 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
1058 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
1063 public void testWriteOnlyTransform() {
1064 Configuration dataConfig = new Configuration();
1065 // no need to have start, JSON output of transformation defines everything
1066 dataConfig.put("writeTransform", "JS(myJsonTransform.js)");
1067 testInitGeneric(null, dataConfig, status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));
1071 public void testWriteTransformAndStart() {
1072 Configuration dataConfig = new Configuration();
1073 // It's illegal to have start and transform. Just have transform or have all
1074 dataConfig.put("writeStart", "3");
1075 dataConfig.put("writeTransform", "JS(myJsonTransform.js)");
1076 testInitGeneric(ModbusReadFunctionCode.READ_COILS, dataConfig, status -> {
1077 assertThat(status.getStatus(), is(equalTo(ThingStatus.OFFLINE)));
1078 assertThat(status.getStatusDetail(), is(equalTo(ThingStatusDetail.CONFIGURATION_ERROR)));
1083 public void testWriteTransformAndNecessary() {
1084 Configuration dataConfig = new Configuration();
1085 // It's illegal to have start and transform. Just have transform or have all
1086 dataConfig.put("writeStart", "3");
1087 dataConfig.put("writeType", "holding");
1088 dataConfig.put("writeValueType", "int16");
1089 dataConfig.put("writeTransform", "JS(myJsonTransform.js)");
1090 testInitGeneric(null, dataConfig, status -> assertThat(status.getStatus(), is(equalTo(ThingStatus.ONLINE))));