[modbus] BaseModbusThingHandler: Add ability to retrieve slave address (#9181)

* [modbus] BaseModbusThingHandler: Add ability to retrieve slave address
* Rework error handling

Signed-off-by: Fabian Wolter <github@fabian-wolter.de>
This commit is contained in:
Fabian Wolter 2020-12-05 06:24:55 +01:00 committed by GitHub
parent 86bd264cb9
commit 568da33684
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 58 deletions

View File

@ -107,7 +107,7 @@ public class MyHandler extends BaseModbusThingHandler {
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
ModbusReadRequestBlueprint blueprint = new ModbusReadRequestBlueprint(42,
ModbusReadRequestBlueprint blueprint = new ModbusReadRequestBlueprint(getSlaveId(),
ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, 0, 1, 2);
submitOneTimePoll(blueprint, this::readSuccessful, this::readError);
@ -115,12 +115,10 @@ public class MyHandler extends BaseModbusThingHandler {
}
@Override
public void initialize() {
super.initialize();
public void modbusInitialize() {
// do other Thing initialization
ModbusReadRequestBlueprint blueprint = new ModbusReadRequestBlueprint(42,
ModbusReadRequestBlueprint blueprint = new ModbusReadRequestBlueprint(getSlaveId(),
ModbusReadFunctionCode.READ_MULTIPLE_REGISTERS, 0, 1, 2);
registerRegularPoll(blueprint, 1000, 0, this::readSuccessful, this::readError);

View File

@ -31,7 +31,6 @@ import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
import org.openhab.io.transport.modbus.ModbusWriteCallback;
import org.openhab.io.transport.modbus.ModbusWriteRequestBlueprint;
import org.openhab.io.transport.modbus.PollTask;
import org.openhab.io.transport.modbus.endpoint.ModbusSlaveEndpoint;
/**
* This is a convenience class to interact with the Thing's {@link ModbusCommunicationInterface}.
@ -43,23 +42,51 @@ import org.openhab.io.transport.modbus.endpoint.ModbusSlaveEndpoint;
public abstract class BaseModbusThingHandler extends BaseThingHandler {
private List<PollTask> periodicPollers = Collections.synchronizedList(new ArrayList<>());
private List<Future<?>> oneTimePollers = Collections.synchronizedList(new ArrayList<>());
private volatile boolean initialized;
public BaseModbusThingHandler(Thing thing) {
super(thing);
}
/**
* This method must be invoked in the base class' initialize() method before any other initialization is done.
* It will throw an unchecked exception if the {@link ModbusCommunicationInterface} is not accessible (fail-fast).
* This prevents any further initialization of the Thing. The framework will set the ThingStatus to
* HANDLER_INITIALIZING_ERROR and display the exception's message.
* This method is called when the Thing is being initialized, but only if the Modbus Bridge is configured correctly.
* The code that normally goes into `BaseThingHandler.initialize()` like configuration reading and validation goes
* here.
*/
@Override
public void initialize() {
getModbus();
public abstract void modbusInitialize();
initialized = true;
@Override
public final void initialize() {
try {
// check if the Bridge is configured correctly (fail-fast)
getModbus();
getSlaveId();
} catch (IllegalStateException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, e.getMessage());
}
modbusInitialize();
}
/**
* Get Slave ID, also called as unit id, represented by the thing
*
* @return slave id represented by this thing handler
*/
public int getSlaveId() {
try {
return getBridgeHandler().getSlaveId();
} catch (EndpointNotInitializedException e) {
throw new IllegalStateException("Bridge not initialized");
}
}
/**
* Return true if auto discovery is enabled for this endpoint
*
* @return boolean true if the discovery is enabled
*/
public boolean isDiscoveryEnabled() {
return getBridgeHandler().isDiscoveryEnabled();
}
/**
@ -79,8 +106,6 @@ public abstract class BaseModbusThingHandler extends BaseThingHandler {
public PollTask registerRegularPoll(ModbusReadRequestBlueprint request, long pollPeriodMillis,
long initialDelayMillis, ModbusReadCallback resultCallback,
ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback) {
checkInitialized();
PollTask task = getModbus().registerRegularPoll(request, pollPeriodMillis, initialDelayMillis, resultCallback,
failureCallback);
periodicPollers.add(task);
@ -96,6 +121,7 @@ public abstract class BaseModbusThingHandler extends BaseThingHandler {
* @param task poll task to unregister
* @return whether poll task was unregistered. Poll task is not unregistered in case of unexpected errors or
* in the case where the poll task is not registered in the first place
* @throws IllegalStateException when this communication has been closed already
*/
public boolean unregisterRegularPoll(PollTask task) {
periodicPollers.remove(task);
@ -114,8 +140,6 @@ public abstract class BaseModbusThingHandler extends BaseThingHandler {
*/
public Future<?> submitOneTimePoll(ModbusReadRequestBlueprint request, ModbusReadCallback resultCallback,
ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback) {
checkInitialized();
Future<?> future = getModbus().submitOneTimePoll(request, resultCallback, failureCallback);
oneTimePollers.add(future);
oneTimePollers.removeIf(Future::isDone);
@ -135,8 +159,6 @@ public abstract class BaseModbusThingHandler extends BaseThingHandler {
*/
public Future<?> submitOneTimeWrite(ModbusWriteRequestBlueprint request, ModbusWriteCallback resultCallback,
ModbusFailureCallback<ModbusWriteRequestBlueprint> failureCallback) {
checkInitialized();
Future<?> future = getModbus().submitOneTimeWrite(request, resultCallback, failureCallback);
oneTimePollers.add(future);
oneTimePollers.removeIf(Future::isDone);
@ -144,60 +166,36 @@ public abstract class BaseModbusThingHandler extends BaseThingHandler {
return future;
}
/**
* Get endpoint associated with this communication interface
*
* @return modbus slave endpoint
*/
public ModbusSlaveEndpoint getEndpoint() {
return getModbus().getEndpoint();
private ModbusCommunicationInterface getModbus() {
ModbusCommunicationInterface communicationInterface = getBridgeHandler().getCommunicationInterface();
if (communicationInterface == null) {
throw new IllegalStateException("Bridge not initialized");
} else {
return communicationInterface;
}
}
/**
* Retrieves the {@link ModbusCommunicationInterface} and does some validity checking.
* Sets the ThingStatus to offline if it couldn't be retrieved and throws an unchecked exception.
*
* The unchecked exception should not be caught by the implementing class, as the initialization of the Thing
* already fails if the {@link ModbusCommunicationInterface} cannot be retrieved.
*
* @throws IllegalStateException if the {@link ModbusCommunicationInterface} couldn't be retrieved.
* @return the {@link ModbusCommunicationInterface}
*/
private ModbusCommunicationInterface getModbus() {
private ModbusEndpointThingHandler getBridgeHandler() {
try {
Bridge bridge = getBridge();
if (bridge == null) {
throw new IllegalStateException("Thing has no Bridge set");
throw new IllegalStateException("No Bridge configured");
}
BridgeHandler handler = bridge.getHandler();
if (handler instanceof ModbusEndpointThingHandler) {
ModbusCommunicationInterface communicationInterface = ((ModbusEndpointThingHandler) handler)
.getCommunicationInterface();
if (communicationInterface == null) {
throw new IllegalStateException("Failed to retrieve Modbus communication interface");
} else {
return communicationInterface;
}
return (ModbusEndpointThingHandler) handler;
} else {
throw new IllegalStateException("Bridge is not a Modbus bridge: " + handler);
throw new IllegalStateException("Not a Modbus Bridge: " + handler);
}
} catch (IllegalStateException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"Modbus initialization failed: " + e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, e.getMessage());
throw e;
}
}
private void checkInitialized() {
if (!initialized) {
throw new IllegalStateException(
getClass().getSimpleName() + " not initialized. Please call super.initialize().");
}
}
@Override
public void dispose() {
oneTimePollers.forEach(p -> p.cancel(true));