]> git.basschouten.com Git - openhab-addons.git/blob
0439ca2dc321ab899800f9def85b4f7e01ae5fde
[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     @Override
100     public String toString() {
101         return "ItemBase{" + "instanceKey=" + instanceKey + ", pendingProperties=" + pendingGets + '}';
102     }
103
104     public void update(String channelId, State state) {
105     }
106
107     public void removed() {
108     }
109
110     public void refresh(String channelId) {
111     }
112
113     public void applyHeader(Esv esv, short tid, long nowMs) {
114         if ((esv == Esv.Get_Res || esv == Esv.Get_SNA)) {
115             final long sentTimestampMs = this.inflightGetRequest.timestampMs;
116             if (this.inflightGetRequest.responseReceived(tid)) {
117                 logger.debug("{} response time: {}ms", esv, nowMs - sentTimestampMs);
118             } else {
119                 logger.warn("Unexpected {} response: {}", esv, tid);
120                 this.inflightGetRequest.checkOldResponse(tid, nowMs);
121             }
122         } else if ((esv == Esv.Set_Res || esv == Esv.SetC_SNA)) {
123             final long sentTimestampMs = this.inflightSetRequest.timestampMs;
124             if (this.inflightSetRequest.responseReceived(tid)) {
125                 logger.debug("{} response time: {}ms", esv, nowMs - sentTimestampMs);
126             } else {
127                 logger.warn("Unexpected {} response: {}", esv, tid);
128                 this.inflightSetRequest.checkOldResponse(tid, nowMs);
129             }
130         }
131     }
132
133     public void checkTimeouts() {
134     }
135
136     protected static class InflightRequest {
137         private static final long NULL_TIMESTAMP = -1;
138
139         private final Logger logger = LoggerFactory.getLogger(InflightRequest.class);
140         private final long timeoutMs;
141         private final String name;
142         private final Map<Short, Long> oldRequests = new HashMap<>();
143
144         private short tid;
145         private long timestampMs = NULL_TIMESTAMP;
146         @SuppressWarnings("unused")
147         private int timeoutCount = 0;
148
149         InflightRequest(long timeoutMs, InflightRequest existing) {
150             this(timeoutMs, existing.name);
151             this.tid = existing.tid;
152             this.timestampMs = existing.timestampMs;
153         }
154
155         InflightRequest(long timeoutMs, String name) {
156             this.timeoutMs = timeoutMs;
157             this.name = name;
158         }
159
160         void requestSent(short tid, long timestampMs) {
161             this.tid = tid;
162             this.timestampMs = timestampMs;
163         }
164
165         boolean responseReceived(short tid) {
166             timestampMs = NULL_TIMESTAMP;
167             timeoutCount = 0;
168
169             return this.tid == tid;
170         }
171
172         boolean hasTimedOut(long nowMs) {
173             final boolean timedOut = timestampMs + timeoutMs <= nowMs;
174             if (timedOut) {
175                 logger.debug("Timed out {}, tid={}, timestampMs={} + timeoutMs={} <= nowMs={}", name, tid, timestampMs,
176                         timeoutMs, nowMs);
177                 timeoutCount++;
178
179                 if (NULL_TIMESTAMP != tid) {
180                     oldRequests.put(tid, timestampMs);
181                 }
182             }
183             return timedOut;
184         }
185
186         public boolean isInflight() {
187             return NULL_TIMESTAMP != timestampMs;
188         }
189
190         public void checkOldResponse(short tid, long nowMs) {
191             final Long oldResponseTimestampMs = oldRequests.remove(tid);
192             if (null != oldResponseTimestampMs) {
193                 logger.debug("Timed out request, tid={}, actually took={}", tid, nowMs - oldResponseTimestampMs);
194             }
195         }
196
197         public int timeoutCount() {
198             return timeoutCount;
199         }
200     }
201 }