]> git.basschouten.com Git - openhab-addons.git/blob
a6d9bb149382b2e7426d05630f23377f6c056c74
[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.lcn.internal.connection;
14
15 import org.eclipse.jdt.annotation.NonNullByDefault;
16 import org.openhab.binding.lcn.internal.common.LcnException;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 /**
21  * Manages timeout and retry logic for an LCN request.
22  *
23  * @author Tobias Jüttner - Initial Contribution
24  * @author Fabian Wolter - Migration to OH2
25  */
26 @NonNullByDefault
27 public class RequestStatus {
28     private final Logger logger = LoggerFactory.getLogger(RequestStatus.class);
29     /** Interval for forced updates. -1 if not used. */
30     private volatile long maxAgeMSec;
31
32     /** Tells how often a request will be sent if no response was received. */
33     private final int numTries;
34
35     /** true if request logic is activated. */
36     private volatile boolean isActive;
37
38     /** The time the current request was sent out or 0. */
39     private volatile long currRequestTimeStamp;
40
41     /** The time stamp of the next scheduled request or 0. */
42     private volatile long nextRequestTimeStamp;
43
44     /** Number of retries left until the request is marked as failed. */
45     private volatile int numRetriesLeft;
46     private final String label;
47
48     /**
49      * Constructor.
50      *
51      * @param maxAgeMSec the forced-updates interval (-1 if not used)
52      * @param numTries the maximum number of tries until the request is marked as failed
53      */
54     RequestStatus(long maxAgeMSec, int numTries, String label) {
55         this.maxAgeMSec = maxAgeMSec;
56         this.numTries = numTries;
57         this.label = label;
58         this.reset();
59     }
60
61     /** Resets the runtime data to the initial states. */
62     public synchronized void reset() {
63         this.isActive = false;
64         this.currRequestTimeStamp = 0;
65         this.nextRequestTimeStamp = 0;
66         this.numRetriesLeft = 0;
67     }
68
69     /**
70      * Checks whether the request logic is active.
71      *
72      * @return true if active
73      */
74     public boolean isActive() {
75         return this.isActive;
76     }
77
78     /**
79      * Checks whether a request is waiting for a response.
80      *
81      * @return true if waiting for a response
82      */
83     boolean isPending() {
84         return this.currRequestTimeStamp != 0;
85     }
86
87     /**
88      * Checks whether the request is active and ran into timeout while waiting for a response.
89      *
90      * @param timeoutMSec the timeout in milliseconds
91      * @param currTime the current time stamp
92      * @return true if request timed out
93      */
94     synchronized boolean isTimeout(long timeoutMSec, long currTime) {
95         return this.isPending() && currTime - this.currRequestTimeStamp >= timeoutMSec;
96     }
97
98     /**
99      * Checks for failed requests (active and out of retries).
100      *
101      * @param timeoutMSec the timeout in milliseconds
102      * @param currTime the current time stamp
103      * @return true if no response was received and no retries are left
104      */
105     synchronized boolean isFailed(long timeoutMSec, long currTime) {
106         return this.isTimeout(timeoutMSec, currTime) && this.numRetriesLeft == 0;
107     }
108
109     /**
110      * Schedules the next request.
111      *
112      * @param delayMSec the delay in milliseconds
113      * @param currTime the current time stamp
114      */
115     public synchronized void nextRequestIn(long delayMSec, long currTime) {
116         this.isActive = true;
117         this.nextRequestTimeStamp = currTime + delayMSec;
118     }
119
120     /**
121      * Schedules a request to retrieve the current value.
122      */
123     public synchronized void refresh() {
124         nextRequestIn(0, System.currentTimeMillis());
125         this.numRetriesLeft = this.numTries;
126     }
127
128     /**
129      * Checks whether sending a new request is required (should be called periodically).
130      *
131      * @param timeoutMSec the time to wait for a response before retrying the request
132      * @param currTime the current time stamp
133      * @return true to indicate a new request should be sent
134      * @throws LcnException when a status request timed out
135      */
136     synchronized boolean shouldSendNextRequest(long timeoutMSec, long currTime) throws LcnException {
137         if (this.isActive) {
138             if (this.nextRequestTimeStamp != 0 && currTime >= this.nextRequestTimeStamp) {
139                 return true;
140             }
141             // Retry of current request (after no response was received)
142             if (this.isTimeout(timeoutMSec, currTime)) {
143                 if (this.numRetriesLeft > 0) {
144                     return true;
145                 } else if (isPending()) {
146                     currRequestTimeStamp = 0;
147                     throw new LcnException(label + ": Failed finally after " + numTries + " tries");
148                 }
149             }
150         }
151         return false;
152     }
153
154     /**
155      * Must be called right after a new request has been sent.
156      * Must be activated first.
157      *
158      * @param currTime the current time stamp
159      */
160     public synchronized void onRequestSent(long currTime) {
161         if (!this.isActive) {
162             logger.warn("Tried to send a request which is not active");
163         }
164         // Updates retry counter
165         if (this.currRequestTimeStamp == 0) {
166             this.numRetriesLeft = this.numTries - 1;
167         } else if (this.numRetriesLeft > 0) { // Should not happen if used correctly
168             --this.numRetriesLeft;
169         }
170         // Mark request as pending
171         this.currRequestTimeStamp = currTime;
172         // Schedule next request
173         if (this.maxAgeMSec != -1) {
174             this.nextRequestIn(this.maxAgeMSec, currTime);
175         } else {
176             this.nextRequestTimeStamp = 0;
177         }
178     }
179
180     /** Must be called when a response (requested or not) has been received. */
181     public synchronized void onResponseReceived() {
182         if (this.isActive) {
183             this.currRequestTimeStamp = 0; // Mark request (if any) as successful
184
185             // Reset timer for next transmission
186             if (this.maxAgeMSec != -1) {
187                 this.nextRequestIn(this.maxAgeMSec, System.currentTimeMillis());
188             }
189         }
190     }
191
192     /**
193      * Sets the timeout of this RequestStatus.
194      *
195      * @param maxAgeMSec the timeout in ms
196      */
197     public void setMaxAgeMSec(long maxAgeMSec) {
198         this.maxAgeMSec = maxAgeMSec;
199     }
200 }