2 * Copyright (c) 2010-2023 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.io.transport.modbus.ModbusCommunicationInterface;
22 import org.openhab.core.io.transport.modbus.ModbusFailureCallback;
23 import org.openhab.core.io.transport.modbus.ModbusReadCallback;
24 import org.openhab.core.io.transport.modbus.ModbusReadRequestBlueprint;
25 import org.openhab.core.io.transport.modbus.ModbusWriteCallback;
26 import org.openhab.core.io.transport.modbus.ModbusWriteRequestBlueprint;
27 import org.openhab.core.io.transport.modbus.PollTask;
28 import org.openhab.core.thing.Bridge;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusDetail;
32 import org.openhab.core.thing.binding.BaseThingHandler;
33 import org.openhab.core.thing.binding.BridgeHandler;
36 * This is a convenience class to interact with the Thing's {@link ModbusCommunicationInterface}.
38 * @author Fabian Wolter - Initial contribution
42 public abstract class BaseModbusThingHandler extends BaseThingHandler {
43 private List<PollTask> periodicPollers = Collections.synchronizedList(new ArrayList<>());
44 private List<Future<?>> oneTimePollers = Collections.synchronizedList(new ArrayList<>());
46 public BaseModbusThingHandler(Thing thing) {
51 * This method is called when the Thing is being initialized, but only if the Modbus Bridge is configured correctly.
52 * The code that normally goes into `BaseThingHandler.initialize()` like configuration reading and validation goes
55 public abstract void modbusInitialize();
58 public final void initialize() {
60 // check if the Bridge is configured correctly (fail-fast)
63 } catch (IllegalStateException e) {
64 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, e.getMessage());
71 * Get Slave ID, also called as unit id, represented by the thing
73 * @return slave id represented by this thing handler
75 public int getSlaveId() {
77 return getBridgeHandler().getSlaveId();
78 } catch (EndpointNotInitializedException e) {
79 throw new IllegalStateException("Bridge not initialized");
84 * Return true if auto discovery is enabled for this endpoint
86 * @return boolean true if the discovery is enabled
88 public boolean isDiscoveryEnabled() {
89 return getBridgeHandler().isDiscoveryEnabled();
93 * Register regularly polled task. The method returns immediately, and the execution of the poll task will happen in
96 * One can register only one regular poll task for triplet of (endpoint, request, callback).
98 * @param request request to send
99 * @param pollPeriodMillis poll interval, in milliseconds
100 * @param initialDelayMillis initial delay before starting polling, in milliseconds
101 * @param callback callback to call with data
102 * @param callback callback to call in case of failure
103 * @return poll task representing the regular poll
104 * @throws IllegalStateException when this communication has been closed already
106 public PollTask registerRegularPoll(ModbusReadRequestBlueprint request, long pollPeriodMillis,
107 long initialDelayMillis, ModbusReadCallback resultCallback,
108 ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback) {
109 PollTask task = getModbus().registerRegularPoll(request, pollPeriodMillis, initialDelayMillis, resultCallback,
111 periodicPollers.add(task);
117 * Unregister regularly polled task
119 * If this communication interface is closed already, the method returns immediately with false return value
121 * @param task poll task to unregister
122 * @return whether poll task was unregistered. Poll task is not unregistered in case of unexpected errors or
123 * in the case where the poll task is not registered in the first place
124 * @throws IllegalStateException when this communication has been closed already
126 public boolean unregisterRegularPoll(PollTask task) {
127 periodicPollers.remove(task);
128 return getModbus().unregisterRegularPoll(task);
132 * Submit one-time poll task. The method returns immediately, and the execution of the poll task will happen in
135 * @param request request to send
136 * @param callback callback to call with data
137 * @param callback callback to call in case of failure
138 * @return future representing the polled task
139 * @throws IllegalStateException when this communication has been closed already
141 public Future<?> submitOneTimePoll(ModbusReadRequestBlueprint request, ModbusReadCallback resultCallback,
142 ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback) {
143 Future<?> future = getModbus().submitOneTimePoll(request, resultCallback, failureCallback);
144 oneTimePollers.add(future);
145 oneTimePollers.removeIf(Future::isDone);
151 * Submit one-time write task. The method returns immediately, and the execution of the task will happen in
154 * @param request request to send
155 * @param callback callback to call with response
156 * @param callback callback to call in case of failure
157 * @return future representing the task
158 * @throws IllegalStateException when this communication has been closed already
160 public Future<?> submitOneTimeWrite(ModbusWriteRequestBlueprint request, ModbusWriteCallback resultCallback,
161 ModbusFailureCallback<ModbusWriteRequestBlueprint> failureCallback) {
162 Future<?> future = getModbus().submitOneTimeWrite(request, resultCallback, failureCallback);
163 oneTimePollers.add(future);
164 oneTimePollers.removeIf(Future::isDone);
169 private ModbusCommunicationInterface getModbus() {
170 ModbusCommunicationInterface communicationInterface = getBridgeHandler().getCommunicationInterface();
172 if (communicationInterface == null) {
173 throw new IllegalStateException("Bridge not initialized");
175 return communicationInterface;
179 private ModbusEndpointThingHandler getBridgeHandler() {
181 Bridge bridge = getBridge();
182 if (bridge == null) {
183 throw new IllegalStateException("No Bridge configured");
186 BridgeHandler handler = bridge.getHandler();
188 if (handler instanceof ModbusEndpointThingHandler) {
189 return (ModbusEndpointThingHandler) handler;
191 throw new IllegalStateException("Not a Modbus Bridge: " + handler);
193 } catch (IllegalStateException e) {
194 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, e.getMessage());
200 public void dispose() {
201 oneTimePollers.forEach(p -> p.cancel(true));
202 oneTimePollers.clear();
204 ModbusCommunicationInterface modbus = getModbus();
205 periodicPollers.forEach(p -> modbus.unregisterRegularPoll(p));
206 periodicPollers.clear();