]> git.basschouten.com Git - openhab-addons.git/blob
52719cd72037c4de01f274cc7283e016b42f54b7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.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;
35
36 /**
37  * This is a convenience class to interact with the Thing's {@link ModbusCommunicationInterface}.
38  *
39  * @author Fabian Wolter - Initial contribution
40  *
41  */
42 @NonNullByDefault
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;
47
48     public BaseModbusThingHandler(Thing thing) {
49         super(thing);
50     }
51
52     /**
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.
57      */
58     @Override
59     public void initialize() {
60         getModbus();
61
62         initialized = true;
63     }
64
65     /**
66      * Register regularly polled task. The method returns immediately, and the execution of the poll task will happen in
67      * the background.
68      *
69      * One can register only one regular poll task for triplet of (endpoint, request, callback).
70      *
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
78      */
79     public PollTask registerRegularPoll(ModbusReadRequestBlueprint request, long pollPeriodMillis,
80             long initialDelayMillis, ModbusReadCallback resultCallback,
81             ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback) {
82         checkInitialized();
83
84         PollTask task = getModbus().registerRegularPoll(request, pollPeriodMillis, initialDelayMillis, resultCallback,
85                 failureCallback);
86         periodicPollers.add(task);
87
88         return task;
89     }
90
91     /**
92      * Unregister regularly polled task
93      *
94      * If this communication interface is closed already, the method returns immediately with false return value
95      *
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
99      */
100     public boolean unregisterRegularPoll(PollTask task) {
101         periodicPollers.remove(task);
102         return getModbus().unregisterRegularPoll(task);
103     }
104
105     /**
106      * Submit one-time poll task. The method returns immediately, and the execution of the poll task will happen in
107      * background.
108      *
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
114      */
115     public Future<?> submitOneTimePoll(ModbusReadRequestBlueprint request, ModbusReadCallback resultCallback,
116             ModbusFailureCallback<ModbusReadRequestBlueprint> failureCallback) {
117         checkInitialized();
118
119         Future<?> future = getModbus().submitOneTimePoll(request, resultCallback, failureCallback);
120         oneTimePollers.add(future);
121         oneTimePollers.removeIf(Future::isDone);
122
123         return future;
124     }
125
126     /**
127      * Submit one-time write task. The method returns immediately, and the execution of the task will happen in
128      * background.
129      *
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
135      */
136     public Future<?> submitOneTimeWrite(ModbusWriteRequestBlueprint request, ModbusWriteCallback resultCallback,
137             ModbusFailureCallback<ModbusWriteRequestBlueprint> failureCallback) {
138         checkInitialized();
139
140         Future<?> future = getModbus().submitOneTimeWrite(request, resultCallback, failureCallback);
141         oneTimePollers.add(future);
142         oneTimePollers.removeIf(Future::isDone);
143
144         return future;
145     }
146
147     /**
148      * Get endpoint associated with this communication interface
149      *
150      * @return modbus slave endpoint
151      */
152     public ModbusSlaveEndpoint getEndpoint() {
153         return getModbus().getEndpoint();
154     }
155
156     /**
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.
159      *
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.
162      *
163      * @throws IllegalStateException if the {@link ModbusCommunicationInterface} couldn't be retrieved.
164      * @return the {@link ModbusCommunicationInterface}
165      */
166     private ModbusCommunicationInterface getModbus() {
167         try {
168             Bridge bridge = getBridge();
169             if (bridge == null) {
170                 throw new IllegalStateException("Thing has no Bridge set");
171             }
172
173             BridgeHandler handler = bridge.getHandler();
174
175             if (handler instanceof ModbusEndpointThingHandler) {
176                 ModbusCommunicationInterface communicationInterface = ((ModbusEndpointThingHandler) handler)
177                         .getCommunicationInterface();
178
179                 if (communicationInterface == null) {
180                     throw new IllegalStateException("Failed to retrieve Modbus communication interface");
181                 } else {
182                     return communicationInterface;
183                 }
184             } else {
185                 throw new IllegalStateException("Bridge is not a Modbus bridge: " + handler);
186             }
187         } catch (IllegalStateException e) {
188             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
189                     "Modbus initialization failed: " + e.getMessage());
190             throw e;
191         }
192     }
193
194     private void checkInitialized() {
195         if (!initialized) {
196             throw new IllegalStateException(
197                     getClass().getSimpleName() + " not initialized. Please call super.initialize().");
198         }
199     }
200
201     @Override
202     public void dispose() {
203         oneTimePollers.forEach(p -> p.cancel(true));
204         oneTimePollers.clear();
205
206         ModbusCommunicationInterface modbus = getModbus();
207         periodicPollers.forEach(p -> modbus.unregisterRegularPoll(p));
208         periodicPollers.clear();
209
210         super.dispose();
211     }
212 }