]> git.basschouten.com Git - openhab-addons.git/blob
c479bbf64f3fdd79077c45cb1dd1ace215cae3dc
[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.echonetlite.internal;
14
15 import static org.openhab.binding.echonetlite.internal.EchonetLiteBindingConstants.DEFAULT_RETRY_TIMEOUT_MS;
16
17 import java.nio.ByteBuffer;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Map;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.openhab.core.types.State;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * @author Michael Barker - Initial contribution
29  */
30 @NonNullByDefault
31 public abstract class EchonetObject {
32
33     private final Logger logger = LoggerFactory.getLogger(EchonetObject.class);
34
35     protected final InstanceKey instanceKey;
36     protected final HashSet<Epc> pendingGets = new HashSet<>();
37
38     protected InflightRequest inflightGetRequest = new InflightRequest(DEFAULT_RETRY_TIMEOUT_MS, "GET");
39     protected InflightRequest inflightSetRequest = new InflightRequest(DEFAULT_RETRY_TIMEOUT_MS, "SET");
40
41     protected long pollIntervalMs;
42
43     public EchonetObject(final InstanceKey instanceKey, final Epc initialProperty) {
44         this.instanceKey = instanceKey;
45         pendingGets.add(initialProperty);
46     }
47
48     public InstanceKey instanceKey() {
49         return instanceKey;
50     }
51
52     public void applyProperty(InstanceKey sourceInstanceKey, Esv esv, final int epcCode, final int pdc,
53             final ByteBuffer edt) {
54     }
55
56     public boolean buildPollMessage(final EchonetMessageBuilder messageBuilder, final ShortSupplier tidSupplier,
57             long nowMs, InstanceKey managementControllerKey) {
58         if (pendingGets.isEmpty()) {
59             return false;
60         }
61
62         if (hasInflight(nowMs, this.inflightGetRequest)) {
63             return false;
64         }
65
66         final short tid = tidSupplier.getAsShort();
67         messageBuilder.start(tid, managementControllerKey, instanceKey(), Esv.Get);
68
69         for (Epc pendingProperty : pendingGets) {
70             messageBuilder.appendEpcRequest(pendingProperty.code());
71         }
72
73         this.inflightGetRequest.requestSent(tid, nowMs);
74
75         return true;
76     }
77
78     protected boolean hasInflight(long nowMs, InflightRequest inflightRequest) {
79         if (inflightRequest.isInflight()) {
80             return !inflightRequest.hasTimedOut(nowMs);
81         }
82         return false;
83     }
84
85     protected void setTimeouts(long pollIntervalMs, long retryTimeoutMs) {
86         this.pollIntervalMs = pollIntervalMs;
87         this.inflightGetRequest = new InflightRequest(retryTimeoutMs, inflightGetRequest);
88         this.inflightSetRequest = new InflightRequest(retryTimeoutMs, inflightSetRequest);
89     }
90
91     public boolean buildUpdateMessage(final EchonetMessageBuilder messageBuilder, final ShortSupplier tid,
92             final long nowMs, InstanceKey managementControllerKey) {
93         return false;
94     }
95
96     public void refreshAll(long nowMs) {
97     }
98
99     public String toString() {
100         return "ItemBase{" + "instanceKey=" + instanceKey + ", pendingProperties=" + pendingGets + '}';
101     }
102
103     public void update(String channelId, State state) {
104     }
105
106     public void removed() {
107     }
108
109     public void refresh(String channelId) {
110     }
111
112     public void applyHeader(Esv esv, short tid, long nowMs) {
113         if ((esv == Esv.Get_Res || esv == Esv.Get_SNA)) {
114             final long sentTimestampMs = this.inflightGetRequest.timestampMs;
115             if (this.inflightGetRequest.responseReceived(tid)) {
116                 logger.debug("{} response time: {}ms", esv, nowMs - sentTimestampMs);
117             } else {
118                 logger.warn("Unexpected {} response: {}", esv, tid);
119                 this.inflightGetRequest.checkOldResponse(tid, nowMs);
120             }
121         } else if ((esv == Esv.Set_Res || esv == Esv.SetC_SNA)) {
122             final long sentTimestampMs = this.inflightSetRequest.timestampMs;
123             if (this.inflightSetRequest.responseReceived(tid)) {
124                 logger.debug("{} response time: {}ms", esv, nowMs - sentTimestampMs);
125             } else {
126                 logger.warn("Unexpected {} response: {}", esv, tid);
127                 this.inflightSetRequest.checkOldResponse(tid, nowMs);
128             }
129         }
130     }
131
132     public void checkTimeouts() {
133     }
134
135     protected static class InflightRequest {
136         private static final long NULL_TIMESTAMP = -1;
137
138         private final Logger logger = LoggerFactory.getLogger(InflightRequest.class);
139         private final long timeoutMs;
140         private final String name;
141         private final Map<Short, Long> oldRequests = new HashMap<>();
142
143         private short tid;
144         private long timestampMs = NULL_TIMESTAMP;
145         @SuppressWarnings("unused")
146         private int timeoutCount = 0;
147
148         InflightRequest(long timeoutMs, InflightRequest existing) {
149             this(timeoutMs, existing.name);
150             this.tid = existing.tid;
151             this.timestampMs = existing.timestampMs;
152         }
153
154         InflightRequest(long timeoutMs, String name) {
155             this.timeoutMs = timeoutMs;
156             this.name = name;
157         }
158
159         void requestSent(short tid, long timestampMs) {
160             this.tid = tid;
161             this.timestampMs = timestampMs;
162         }
163
164         boolean responseReceived(short tid) {
165             timestampMs = NULL_TIMESTAMP;
166             timeoutCount = 0;
167
168             return this.tid == tid;
169         }
170
171         boolean hasTimedOut(long nowMs) {
172             final boolean timedOut = timestampMs + timeoutMs <= nowMs;
173             if (timedOut) {
174                 logger.debug("Timed out {}, tid={}, timestampMs={} + timeoutMs={} <= nowMs={}", name, tid, timestampMs,
175                         timeoutMs, nowMs);
176                 timeoutCount++;
177
178                 if (NULL_TIMESTAMP != tid) {
179                     oldRequests.put(tid, timestampMs);
180                 }
181             }
182             return timedOut;
183         }
184
185         public boolean isInflight() {
186             return NULL_TIMESTAMP != timestampMs;
187         }
188
189         public void checkOldResponse(short tid, long nowMs) {
190             final Long oldResponseTimestampMs = oldRequests.remove(tid);
191             if (null != oldResponseTimestampMs) {
192                 logger.debug("Timed out request, tid={}, actually took={}", tid, nowMs - oldResponseTimestampMs);
193             }
194         }
195
196         public int timeoutCount() {
197             return timeoutCount;
198         }
199     }
200 }