]> git.basschouten.com Git - openhab-addons.git/blob
8de22c6254413fc0df00f7407459f9211ee7d857
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.modbus.handler;
14
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.concurrent.Future;
19
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;
34
35 /**
36  * This is a convenience class to interact with the Thing's {@link ModbusCommunicationInterface}.
37  *
38  * @author Fabian Wolter - Initial contribution
39  *
40  */
41 @NonNullByDefault
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<>());
45
46     public BaseModbusThingHandler(Thing thing) {
47         super(thing);
48     }
49
50     /**
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
53      * here.
54      */
55     public abstract void modbusInitialize();
56
57     @Override
58     public final void initialize() {
59         try {
60             // check if the Bridge is configured correctly (fail-fast)
61             getModbus();
62             getSlaveId();
63         } catch (IllegalStateException e) {
64             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, e.getMessage());
65         }
66
67         modbusInitialize();
68     }
69
70     /**
71      * Get Slave ID, also called as unit id, represented by the thing
72      *
73      * @return slave id represented by this thing handler
74      */
75     public int getSlaveId() {
76         try {
77             return getBridgeHandler().getSlaveId();
78         } catch (EndpointNotInitializedException e) {
79             throw new IllegalStateException("Bridge not initialized");
80         }
81     }
82
83     /**
84      * Return true if auto discovery is enabled for this endpoint
85      *
86      * @return boolean true if the discovery is enabled
87      */
88     public boolean isDiscoveryEnabled() {
89         return getBridgeHandler().isDiscoveryEnabled();
90     }
91
92     /**
93      * Register regularly polled task. The method returns immediately, and the execution of the poll task will happen in
94      * the background.
95      *
96      * One can register only one regular poll task for triplet of (endpoint, request, callback).
97      *
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
105      */
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,
110                 failureCallback);
111         periodicPollers.add(task);
112
113         return task;
114     }
115
116     /**
117      * Unregister regularly polled task
118      *
119      * If this communication interface is closed already, the method returns immediately with false return value
120      *
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
125      */
126     public boolean unregisterRegularPoll(PollTask task) {
127         periodicPollers.remove(task);
128         return getModbus().unregisterRegularPoll(task);
129     }
130
131     /**
132      * Submit one-time poll task. The method returns immediately, and the execution of the poll task will happen in
133      * background.
134      *
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
140      */
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);
146
147         return future;
148     }
149
150     /**
151      * Submit one-time write task. The method returns immediately, and the execution of the task will happen in
152      * background.
153      *
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
159      */
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);
165
166         return future;
167     }
168
169     private ModbusCommunicationInterface getModbus() {
170         ModbusCommunicationInterface communicationInterface = getBridgeHandler().getCommunicationInterface();
171
172         if (communicationInterface == null) {
173             throw new IllegalStateException("Bridge not initialized");
174         } else {
175             return communicationInterface;
176         }
177     }
178
179     private ModbusEndpointThingHandler getBridgeHandler() {
180         try {
181             Bridge bridge = getBridge();
182             if (bridge == null) {
183                 throw new IllegalStateException("No Bridge configured");
184             }
185
186             BridgeHandler handler = bridge.getHandler();
187
188             if (handler instanceof ModbusEndpointThingHandler) {
189                 return (ModbusEndpointThingHandler) handler;
190             } else {
191                 throw new IllegalStateException("Not a Modbus Bridge: " + handler);
192             }
193         } catch (IllegalStateException e) {
194             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, e.getMessage());
195             throw e;
196         }
197     }
198
199     @Override
200     public void dispose() {
201         oneTimePollers.forEach(p -> p.cancel(true));
202         oneTimePollers.clear();
203
204         ModbusCommunicationInterface modbus = getModbus();
205         periodicPollers.forEach(p -> modbus.unregisterRegularPoll(p));
206         periodicPollers.clear();
207
208         super.dispose();
209     }
210 }