Instrument Neutral Distributed Interface INDI 2.2.5
driver_support.h
Go to the documentation of this file.
1/*
2 Gemini Power Box Hub Advanced v3 INDI Driver
3
4 Copyright (C) 2026 Dieter R Kedrowitsch <[email protected]>
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU General Public License along with
17 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 The full GNU General Public License is included in this distribution in the
21 file called LICENSE.
22*/
23
24#pragma once
25
26#include <algorithm>
27#include <chrono>
28#include <cstddef>
29#include <cstring>
30#include <deque>
31#include <mutex>
32#include <optional>
33#include <vector>
34
38
40{
41
43{
44 Idle,
45 Ok,
46 Busy,
47 Alert
48};
49
57
67
69{
71 std::size_t index,
73 std::chrono::steady_clock::time_point deadline)
75 {
76 }
77
79 std::size_t index = 0;
81 std::chrono::steady_clock::time_point deadline;
82};
83
84inline const char *errorName(Error error)
85{
86 switch (error)
87 {
88 case Error::None:
89 return "none";
91 return "invalid channel";
93 return "invalid value";
95 return "not connected";
97 return "communication failure";
99 return "command queue rejected the request";
101 return "protocol rejected the request";
103 return "value unavailable";
105 return "invalid connection state";
106 case Error::Busy:
107 return "device busy";
108 case Error::Cancelled:
109 return "operation cancelled";
110 }
111 return "unknown error";
112}
113
118
120{
121 switch (kind)
122 {
124 return "DC output";
126 return "USB output";
128 return "heater enabled state";
130 return "heater mode";
131 }
132 return "operation";
133}
134
135inline bool sameLogicalOperation(const PendingOperation &operation, PendingOperationKind kind, std::size_t index)
136{
137 return operation.kind == kind && operation.index == index;
138}
139
140inline void replacePendingOperation(std::vector<PendingOperation> &operations, PendingOperation replacement)
141{
142 operations.erase(std::remove_if(operations.begin(), operations.end(),
143 [&](const PendingOperation & operation)
144 {
145 return sameLogicalOperation(operation, replacement.kind, replacement.index);
146 }), operations.end());
147 operations.push_back(replacement);
148}
149
165
167{
168 return OperationTarget::heaterMode(index, mode);
169}
170
172 const TelemetrySnapshot *snapshot,
173 std::chrono::steady_clock::time_point now)
174{
175 if (now > operation.deadline)
177
178 if (!snapshot)
180
181 switch (confirmOperation(operation.target, snapshot))
182 {
193 }
195}
196
197inline const char *heaterModeName(HeaterMode mode)
198{
199 switch (mode)
200 {
201 case HeaterMode::Auto:
202 return "Auto";
204 return "Manual PWM";
206 return "Switch";
207 }
208 return "Unknown";
209}
210
211inline bool heaterModeFromSwitchName(const char *name, HeaterMode &mode)
212{
213 if (std::strcmp(name, "AUTO") == 0)
214 {
215 mode = HeaterMode::Auto;
216 return true;
217 }
218 if (std::strcmp(name, "MANUAL_PWM") == 0)
219 {
221 return true;
222 }
223 if (std::strcmp(name, "SWITCH") == 0)
224 {
226 return true;
227 }
228 return false;
229}
230
231inline std::size_t heaterModeSwitchIndex(HeaterMode mode)
232{
233 switch (mode)
234 {
235 case HeaterMode::Auto:
236 return 0;
238 return 1;
240 return 2;
241 }
242 return 0;
243}
244
246{
247 return false;
248}
249
251{
253 return false;
254
255 const auto *payload = std::get_if<instrumentation::ConnectionPayload>(&event.payload);
256 if (payload == nullptr)
257 return false;
258
259 switch (payload->state)
260 {
264 return true;
266 return false;
267 }
268 return false;
269}
270
272{
273 public:
274 bool stageTelemetry(const TelemetrySnapshot &snapshot)
275 {
276 std::lock_guard<std::mutex> lock(mutex_);
277 if (shutdown_)
278 return false;
279 stagedTelemetry_.emplace(snapshot);
280 latestTelemetry_.emplace(snapshot);
281 telemetryDirty_ = true;
282 return true;
283 }
284
286 {
287 std::lock_guard<std::mutex> lock(mutex_);
288 if (shutdown_)
289 return false;
290 stagedConnectionEvents_.push_back(event);
291 return true;
292 }
293
294 std::optional<TelemetrySnapshot> takeTelemetry()
295 {
296 std::lock_guard<std::mutex> lock(mutex_);
297 if (!telemetryDirty_ || !stagedTelemetry_)
298 return std::nullopt;
299 std::optional<TelemetrySnapshot> snapshot;
300 snapshot.emplace(*stagedTelemetry_);
301 telemetryDirty_ = false;
302 return snapshot;
303 }
304
305 std::optional<TelemetrySnapshot> latestTelemetry() const
306 {
307 std::lock_guard<std::mutex> lock(mutex_);
308 if (!latestTelemetry_)
309 return std::nullopt;
310 std::optional<TelemetrySnapshot> snapshot;
311 snapshot.emplace(*latestTelemetry_);
312 return snapshot;
313 }
314
315 std::deque<ConnectionEvent> takeConnectionEvents()
316 {
317 std::lock_guard<std::mutex> lock(mutex_);
318 std::deque<ConnectionEvent> events;
319 events.swap(stagedConnectionEvents_);
320 return events;
321 }
322
323 void clear()
324 {
325 std::lock_guard<std::mutex> lock(mutex_);
326 stagedTelemetry_.reset();
327 latestTelemetry_.reset();
328 stagedConnectionEvents_.clear();
329 telemetryDirty_ = false;
330 }
331
333 {
334 std::lock_guard<std::mutex> lock(mutex_);
335 shutdown_ = true;
336 stagedTelemetry_.reset();
337 latestTelemetry_.reset();
338 stagedConnectionEvents_.clear();
339 telemetryDirty_ = false;
340 }
341
342 private:
343 mutable std::mutex mutex_;
344 std::optional<TelemetrySnapshot> stagedTelemetry_;
345 std::optional<TelemetrySnapshot> latestTelemetry_;
346 std::deque<ConnectionEvent> stagedConnectionEvents_;
347 bool telemetryDirty_ = false;
348 bool shutdown_ = false;
349};
350
351} // namespace geminipbh::indi_driver
static ValueResult< OperationTarget > usbOutput(std::size_t channel, bool enabled)
static ValueResult< OperationTarget > heaterEnabled(std::size_t channel, bool enabled)
static ValueResult< OperationTarget > dcOutput(std::size_t channel, bool enabled)
static ValueResult< OperationTarget > heaterMode(std::size_t channel, HeaterMode mode)
static ValueResult failure(Error error)
Definition result.h:86
std::optional< TelemetrySnapshot > takeTelemetry()
bool stageTelemetry(const TelemetrySnapshot &snapshot)
std::optional< TelemetrySnapshot > latestTelemetry() const
std::deque< ConnectionEvent > takeConnectionEvents()
bool stageConnectionEvent(ConnectionEvent event)
const char * errorName(Error error)
ValueResult< OperationTarget > makeHeaterModeTarget(std::size_t index, HeaterMode mode)
bool heaterModeFromSwitchName(const char *name, HeaterMode &mode)
std::size_t heaterModeSwitchIndex(HeaterMode mode)
bool instrumentationDuplicatesSemanticDriverLog(const instrumentation::Event &event)
DriverPropertyState propertyStateForImmediateResult(Result result)
ValueResult< OperationTarget > makeBooleanTarget(PendingOperationKind kind, std::size_t index, bool enabled)
PendingEvaluation evaluatePendingOperation(const PendingOperation &operation, const TelemetrySnapshot *snapshot, std::chrono::steady_clock::time_point now)
bool sameLogicalOperation(const PendingOperation &operation, PendingOperationKind kind, std::size_t index)
const char * heaterModeName(HeaterMode mode)
void replacePendingOperation(std::vector< PendingOperation > &operations, PendingOperation replacement)
const char * pendingOperationName(PendingOperationKind kind)
Confirmation confirmOperation(const DcOutputTarget &target, const TelemetrySnapshot *snapshot)
std::chrono::steady_clock::time_point deadline
PendingOperation(PendingOperationKind kind, std::size_t index, OperationTarget target, std::chrono::steady_clock::time_point deadline)