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.lcn.internal.connection;
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;
21 * Manages timeout and retry logic for an LCN request.
23 * @author Tobias Jüttner - Initial Contribution
24 * @author Fabian Wolter - Migration to OH2
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;
32 /** Tells how often a request will be sent if no response was received. */
33 private final int numTries;
35 /** true if request logic is activated. */
36 private volatile boolean isActive;
38 /** The time the current request was sent out or 0. */
39 private volatile long currRequestTimeStamp;
41 /** The time stamp of the next scheduled request or 0. */
42 private volatile long nextRequestTimeStamp;
44 /** Number of retries left until the request is marked as failed. */
45 private volatile int numRetriesLeft;
46 private final String label;
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
54 RequestStatus(long maxAgeMSec, int numTries, String label) {
55 this.maxAgeMSec = maxAgeMSec;
56 this.numTries = numTries;
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;
70 * Checks whether the request logic is active.
72 * @return true if active
74 public boolean isActive() {
79 * Checks whether a request is waiting for a response.
81 * @return true if waiting for a response
84 return this.currRequestTimeStamp != 0;
88 * Checks whether the request is active and ran into timeout while waiting for a response.
90 * @param timeoutMSec the timeout in milliseconds
91 * @param currTime the current time stamp
92 * @return true if request timed out
94 synchronized boolean isTimeout(long timeoutMSec, long currTime) {
95 return this.isPending() && currTime - this.currRequestTimeStamp >= timeoutMSec;
99 * Checks for failed requests (active and out of retries).
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
105 synchronized boolean isFailed(long timeoutMSec, long currTime) {
106 return this.isTimeout(timeoutMSec, currTime) && this.numRetriesLeft == 0;
110 * Schedules the next request.
112 * @param delayMSec the delay in milliseconds
113 * @param currTime the current time stamp
115 public synchronized void nextRequestIn(long delayMSec, long currTime) {
116 this.isActive = true;
117 this.nextRequestTimeStamp = currTime + delayMSec;
121 * Schedules a request to retrieve the current value.
123 public synchronized void refresh() {
124 nextRequestIn(0, System.currentTimeMillis());
125 this.numRetriesLeft = this.numTries;
129 * Checks whether sending a new request is required (should be called periodically).
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
136 synchronized boolean shouldSendNextRequest(long timeoutMSec, long currTime) throws LcnException {
138 if (this.nextRequestTimeStamp != 0 && currTime >= this.nextRequestTimeStamp) {
141 // Retry of current request (after no response was received)
142 if (this.isTimeout(timeoutMSec, currTime)) {
143 if (this.numRetriesLeft > 0) {
145 } else if (isPending()) {
146 currRequestTimeStamp = 0;
147 throw new LcnException(label + ": Failed finally after " + numTries + " tries");
155 * Must be called right after a new request has been sent.
156 * Must be activated first.
158 * @param currTime the current time stamp
160 public synchronized void onRequestSent(long currTime) {
161 if (!this.isActive) {
162 logger.warn("Tried to send a request which is not active");
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;
170 // Mark request as pending
171 this.currRequestTimeStamp = currTime;
172 // Schedule next request
173 if (this.maxAgeMSec != -1) {
174 this.nextRequestIn(this.maxAgeMSec, currTime);
176 this.nextRequestTimeStamp = 0;
180 /** Must be called when a response (requested or not) has been received. */
181 public synchronized void onResponseReceived() {
183 this.currRequestTimeStamp = 0; // Mark request (if any) as successful
185 // Reset timer for next transmission
186 if (this.maxAgeMSec != -1) {
187 this.nextRequestIn(this.maxAgeMSec, System.currentTimeMillis());
193 * Sets the timeout of this RequestStatus.
195 * @param maxAgeMSec the timeout in ms
197 public void setMaxAgeMSec(long maxAgeMSec) {
198 this.maxAgeMSec = maxAgeMSec;