use of org.openhab.binding.modbus.handler.ModbusPollerThingHandler in project openhab-addons by openhab.
the class ModbusPollerThingHandlerTest method testRegistersPassedToChildDataThings.
@Test
public void testRegistersPassedToChildDataThings() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
PollTask pollTask = Mockito.mock(PollTask.class);
doReturn(pollTask).when(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), notNull(), notNull());
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 150L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID()).build();
addThing(poller);
verifyEndpointBasicInitInteraction();
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
ArgumentCaptor<ModbusReadCallback> callbackCapturer = ArgumentCaptor.forClass(ModbusReadCallback.class);
verify(comms).registerRegularPoll(notNull(), eq(150l), eq(0L), callbackCapturer.capture(), notNull());
ModbusReadCallback readCallback = callbackCapturer.getValue();
assertNotNull(readCallback);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
ModbusDataThingHandler child2 = Mockito.mock(ModbusDataThingHandler.class);
AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
// has one data child
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
reset(child1);
// two children (one child initialized)
thingHandler.childHandlerInitialized(child2, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child1).onReadResult(result);
verify(child2).onReadResult(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
reset(child1);
reset(child2);
// one child disposed
thingHandler.childHandlerDisposed(child1, Mockito.mock(Thing.class));
readCallback.handle(result);
verify(child2).onReadResult(result);
verifyNoMoreInteractions(child1);
verifyNoMoreInteractions(child2);
}
use of org.openhab.binding.modbus.handler.ModbusPollerThingHandler in project openhab-addons by openhab.
the class ModbusPollerThingHandlerTest method testRefreshWithPreviousData.
/**
* When there's no recently received data, refresh() will re-use that instead
*
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws NoSuchFieldException
* @throws SecurityException
*/
@Test
public void testRefreshWithPreviousData() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
pollerConfig.put("cacheMillis", 10000L);
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID()).build();
addThing(poller);
verifyEndpointBasicInitInteraction();
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
verify(comms, never()).submitOneTimePoll(any(), any(), any());
// data is received
ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
pollerReadCallback.handle(result);
// data child receives the data
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
reset(child1);
// call refresh
// cache is still valid, we should not have real data poll this time
thingHandler.refresh();
verify(comms, never()).submitOneTimePoll(any(), any(), any());
// data child receives the cached data
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
}
use of org.openhab.binding.modbus.handler.ModbusPollerThingHandler in project openhab-addons by openhab.
the class ModbusPollerThingHandlerTest method testRefreshWithOldPreviousData.
@Test
public void testRefreshWithOldPreviousData() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, InterruptedException {
Configuration pollerConfig = new Configuration();
pollerConfig.put("refresh", 0L);
pollerConfig.put("start", 5);
pollerConfig.put("length", 13);
pollerConfig.put("type", "coil");
pollerConfig.put("cacheMillis", 10L);
poller = createPollerThingBuilder("poller").withConfiguration(pollerConfig).withBridge(endpoint.getUID()).build();
addThing(poller);
verifyEndpointBasicInitInteraction();
ModbusPollerThingHandler thingHandler = (ModbusPollerThingHandler) poller.getHandler();
assertNotNull(thingHandler);
ModbusDataThingHandler child1 = Mockito.mock(ModbusDataThingHandler.class);
thingHandler.childHandlerInitialized(child1, Mockito.mock(Thing.class));
assertThat(poller.getStatus(), is(equalTo(ThingStatus.ONLINE)));
verify(comms, never()).submitOneTimePoll(any(), any(), any());
// data is received
ModbusReadCallback pollerReadCallback = getPollerCallback(thingHandler);
ModbusReadRequestBlueprint request = Mockito.mock(ModbusReadRequestBlueprint.class);
ModbusRegisterArray registers = Mockito.mock(ModbusRegisterArray.class);
AsyncModbusReadResult result = new AsyncModbusReadResult(request, registers);
pollerReadCallback.handle(result);
// data child should receive the data
verify(child1).onReadResult(result);
verifyNoMoreInteractions(child1);
reset(child1);
// Sleep to ensure cache expiry
Thread.sleep(15L);
// call refresh. Since cache expired, will poll for more
verify(comms, never()).submitOneTimePoll(any(), any(), any());
thingHandler.refresh();
verify(comms).submitOneTimePoll(any(), any(), any());
}
use of org.openhab.binding.modbus.handler.ModbusPollerThingHandler in project openhab-addons by openhab.
the class ModbusDataThingHandler method requestFromCommand.
@Nullable
private ModbusWriteRequestBlueprint requestFromCommand(ChannelUID channelUID, Command origCommand, ModbusDataConfiguration config, Command transformedCommand, Integer writeStart) {
ModbusWriteRequestBlueprint request;
boolean writeMultiple = config.isWriteMultipleEvenWithSingleRegisterOrCoil();
String writeType = config.getWriteType();
ModbusPollerThingHandler pollerHandler = this.pollerHandler;
if (writeType == null) {
// disposed thing
return null;
}
if (writeType.equals(WRITE_TYPE_COIL)) {
Optional<Boolean> commandAsBoolean = ModbusBitUtilities.translateCommand2Boolean(transformedCommand);
if (!commandAsBoolean.isPresent()) {
logger.warn("Cannot process command {} with channel {} since command is not OnOffType, OpenClosedType or Decimal trying to write to coil. Do not know how to convert to 0/1. Transformed command was '{}'", origCommand, channelUID, transformedCommand);
return null;
}
boolean data = commandAsBoolean.get();
request = new ModbusWriteCoilRequestBlueprint(slaveId, writeStart, data, writeMultiple, config.getWriteMaxTries());
} else if (writeType.equals(WRITE_TYPE_HOLDING)) {
ValueType writeValueType = this.writeValueType;
if (writeValueType == null) {
// Should not happen in practice, since we are not in configuration error (checked above)
// This will make compiler happy anyways with the null checks
logger.warn("Received command but write value type not set! Ignoring command");
return null;
}
final ModbusRegisterArray data;
if (writeValueType.equals(ValueType.BIT)) {
if (writeSubIndex.isEmpty()) {
// Should not happen! should be in configuration error
logger.error("Bug: sub index not present but writeValueType=BIT. Should be in configuration error");
return null;
}
Optional<Boolean> commandBool = ModbusBitUtilities.translateCommand2Boolean(transformedCommand);
if (commandBool.isEmpty()) {
logger.warn("Data thing is configured to write individual bit but we received command that is not convertible to 0/1 bit. Ignoring.");
return null;
} else if (pollerHandler == null) {
logger.warn("Bug: sub index present but not child of poller. Should be in configuration erro");
return null;
}
// writing bit of an individual register. Using cache from poller
AtomicReference<@Nullable ModbusRegisterArray> cachedRegistersRef = pollerHandler.getLastPolledDataCache();
ModbusRegisterArray mutatedRegisters = cachedRegistersRef.updateAndGet(cachedRegisters -> cachedRegisters == null ? null : combineCommandWithRegisters(cachedRegisters, writeStart, writeSubIndex.get(), commandBool.get()));
if (mutatedRegisters == null) {
logger.warn("Received command to thing with writeValueType=bit (pointing to individual bit of a holding register) but internal cache not yet populated. Ignoring command");
return null;
}
// extract register (first byte index = register index * 2)
byte[] allMutatedBytes = mutatedRegisters.getBytes();
int writeStartRelative = writeStart - pollStart;
data = new ModbusRegisterArray(allMutatedBytes[writeStartRelative * 2], allMutatedBytes[writeStartRelative * 2 + 1]);
} else {
data = ModbusBitUtilities.commandToRegisters(transformedCommand, writeValueType);
}
writeMultiple = writeMultiple || data.size() > 1;
request = new ModbusWriteRegisterRequestBlueprint(slaveId, writeStart, data, writeMultiple, config.getWriteMaxTries());
} else {
// We keep this here for future-proofing the code (new writeType values)
throw new IllegalStateException(String.format("writeType does not equal %s or %s and thus configuration is invalid. Should not end up this far with configuration error.", WRITE_TYPE_COIL, WRITE_TYPE_HOLDING));
}
return request;
}
use of org.openhab.binding.modbus.handler.ModbusPollerThingHandler in project openhab-addons by openhab.
the class ModbusDataThingHandler method handleCommand.
@Override
public synchronized void handleCommand(ChannelUID channelUID, Command command) {
logger.trace("Thing {} '{}' received command '{}' to channel '{}'", getThing().getUID(), getThing().getLabel(), command, channelUID);
ModbusDataConfiguration config = this.config;
if (config == null) {
return;
}
if (RefreshType.REFRESH == command) {
ModbusPollerThingHandler poller = pollerHandler;
if (poller == null) {
// There is no data to update
return;
}
// We *schedule* the REFRESH to avoid dead-lock situation where poller is trying update this
// data thing with cached data (resulting in deadlock in two synchronized methods: this (handleCommand) and
// onRegisters.
scheduler.schedule(() -> poller.refresh(), 0, TimeUnit.SECONDS);
return;
} else if (hasConfigurationError()) {
logger.debug("Thing {} '{}' command '{}' to channel '{}': Thing has configuration error so ignoring the command", getThing().getUID(), getThing().getLabel(), command, channelUID);
return;
} else if (!isWriteEnabled) {
logger.debug("Thing {} '{}' command '{}' to channel '{}': no writing configured -> aborting processing command", getThing().getUID(), getThing().getLabel(), command, channelUID);
return;
}
Optional<Command> transformedCommand = transformCommandAndProcessJSON(channelUID, command);
if (transformedCommand == null) {
// transformCommandAndProcessJSON javadoc
return;
}
// We did not have JSON output from the transformation, so writeStart is absolute required. Abort if it is
// missing
Optional<Integer> writeStart = this.writeStart;
if (writeStart.isEmpty()) {
logger.debug("Thing {} '{}': not processing command {} since writeStart is missing and transformation output is not a JSON", getThing().getUID(), getThing().getLabel(), command);
return;
}
if (!transformedCommand.isPresent()) {
// transformation failed, return
logger.warn("Cannot process command {} (of type {}) with channel {} since transformation was unsuccessful", command, command.getClass().getSimpleName(), channelUID);
return;
}
ModbusWriteRequestBlueprint request = requestFromCommand(channelUID, command, config, transformedCommand.get(), writeStart.get());
if (request == null) {
return;
}
logger.trace("Submitting write task {} to endpoint {}", request, comms.getEndpoint());
comms.submitOneTimeWrite(request, this::onWriteResponse, this::handleWriteError);
}
Aggregations