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.handler;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.concurrent.Future;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.openhab.core.thing.Bridge;
22 import org.openhab.core.thing.Thing;
23 import org.openhab.core.thing.ThingStatus;
24 import org.openhab.core.thing.ThingStatusDetail;
25 import org.openhab.core.thing.binding.BaseThingHandler;
26 import org.openhab.core.thing.binding.BridgeHandler;
27 import org.openhab.io.transport.modbus.ModbusCommunicationInterface;
28 import org.openhab.io.transport.modbus.ModbusFailureCallback;
29 import org.openhab.io.transport.modbus.ModbusReadCallback;
30 import org.openhab.io.transport.modbus.ModbusReadRequestBlueprint;
31 import org.openhab.io.transport.modbus.ModbusWriteCallback;
32 import org.openhab.io.transport.modbus.ModbusWriteRequestBlueprint;
33 import org.openhab.io.transport.modbus.PollTask;
34 import org.openhab.io.transport.modbus.endpoint.ModbusSlaveEndpoint;
37 * This is a convenience class to interact with the Thing's {@link ModbusCommunicationInterface}.
39 * @author Fabian Wolter - Initial contribution
43 public abstract class BaseModbusThingHandler extends BaseThingHandler {
44 private List<PollTask> periodicPollers = Collections.synchronizedList(new ArrayList<>());
45 private List<Future<?>> oneTimePollers = Collections.synchronizedList(new ArrayList<>());
46 private volatile boolean initialized;
48 public BaseModbusThingHandler(Thing thing) {
53 * This method must be invoked in the base class' initialize() method before any other initialization is done.
54 * It will throw an unchecked exception if the {@link ModbusCommunicationInterface} is not accessible (fail-fast).
55 * This prevents any further initialization of the Thing. The framework will set the ThingStatus to
56 * HANDLER_INITIALIZING_ERROR and display the exception's message.
59 public void initialize() {
66 * Register regularly polled task. The method returns immediately, and the execution of the poll task will happen in
69 * One can register only one regular poll task for triplet of (endpoint, request, callback).
71 * @param request request to send
72 * @param pollPeriodMillis poll interval, in milliseconds
73 * @param initialDelayMillis initial delay before starting polling, in milliseconds
74 * @param callback callback to call with data
75 * @param callback callback to call in case of failure
76 * @return poll task representing the regular poll
77 * @throws IllegalStateException when this communication has been closed already
79 public PollTask registerRegularPoll(ModbusReadRequestBlueprint request, long pollPeriodMillis,
80 long initialDelayMillis, ModbusReadCallback resultCallback,
81 ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback) {
84 PollTask task = getModbus().registerRegularPoll(request, pollPeriodMillis, initialDelayMillis, resultCallback,
86 periodicPollers.add(task);
92 * Unregister regularly polled task
94 * If this communication interface is closed already, the method returns immediately with false return value
96 * @param task poll task to unregister
97 * @return whether poll task was unregistered. Poll task is not unregistered in case of unexpected errors or
98 * in the case where the poll task is not registered in the first place
100 public boolean unregisterRegularPoll(PollTask task) {
101 periodicPollers.remove(task);
102 return getModbus().unregisterRegularPoll(task);
106 * Submit one-time poll task. The method returns immediately, and the execution of the poll task will happen in
109 * @param request request to send
110 * @param callback callback to call with data
111 * @param callback callback to call in case of failure
112 * @return future representing the polled task
113 * @throws IllegalStateException when this communication has been closed already
115 public Future<?> submitOneTimePoll(ModbusReadRequestBlueprint request, ModbusReadCallback resultCallback,
116 ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback) {
119 Future<?> future = getModbus().submitOneTimePoll(request, resultCallback, failureCallback);
120 oneTimePollers.add(future);
121 oneTimePollers.removeIf(Future::isDone);
127 * Submit one-time write task. The method returns immediately, and the execution of the task will happen in
130 * @param request request to send
131 * @param callback callback to call with response
132 * @param callback callback to call in case of failure
133 * @return future representing the task
134 * @throws IllegalStateException when this communication has been closed already
136 public Future<?> submitOneTimeWrite(ModbusWriteRequestBlueprint request, ModbusWriteCallback resultCallback,
137 ModbusFailureCallback<ModbusWriteRequestBlueprint> failureCallback) {
140 Future<?> future = getModbus().submitOneTimeWrite(request, resultCallback, failureCallback);
141 oneTimePollers.add(future);
142 oneTimePollers.removeIf(Future::isDone);
148 * Get endpoint associated with this communication interface
150 * @return modbus slave endpoint
152 public ModbusSlaveEndpoint getEndpoint() {
153 return getModbus().getEndpoint();
157 * Retrieves the {@link ModbusCommunicationInterface} and does some validity checking.
158 * Sets the ThingStatus to offline if it couldn't be retrieved and throws an unchecked exception.
160 * The unchecked exception should not be caught by the implementing class, as the initialization of the Thing
161 * already fails if the {@link ModbusCommunicationInterface} cannot be retrieved.
163 * @throws IllegalStateException if the {@link ModbusCommunicationInterface} couldn't be retrieved.
164 * @return the {@link ModbusCommunicationInterface}
166 private ModbusCommunicationInterface getModbus() {
168 Bridge bridge = getBridge();
169 if (bridge == null) {
170 throw new IllegalStateException("Thing has no Bridge set");
173 BridgeHandler handler = bridge.getHandler();
175 if (handler instanceof ModbusEndpointThingHandler) {
176 ModbusCommunicationInterface communicationInterface = ((ModbusEndpointThingHandler) handler)
177 .getCommunicationInterface();
179 if (communicationInterface == null) {
180 throw new IllegalStateException("Failed to retrieve Modbus communication interface");
182 return communicationInterface;
185 throw new IllegalStateException("Bridge is not a Modbus bridge: " + handler);
187 } catch (IllegalStateException e) {
188 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
189 "Modbus initialization failed: " + e.getMessage());
194 private void checkInitialized() {
196 throw new IllegalStateException(
197 getClass().getSimpleName() + " not initialized. Please call super.initialize().");
202 public void dispose() {
203 oneTimePollers.forEach(p -> p.cancel(true));
204 oneTimePollers.clear();
206 ModbusCommunicationInterface modbus = getModbus();
207 periodicPollers.forEach(p -> modbus.unregisterRegularPoll(p));
208 periodicPollers.clear();