Instrument Neutral Distributed Interface INDI 2.2.5
indi_hakos_roof.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Dome Simulator
3 Copyright(c) 2014 Jasem Mutlaq. All rights reserved.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
8 .
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13 .
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*******************************************************************************/
19
20#include "indi_hakos_roof.h"
21
22#include "indicom.h"
23
24#include <cmath>
25#include <cstring>
26#include <ctime>
27#include <curl/curl.h>
28#include <fstream>
29#include <iostream>
30#include <memory>
31#include <stdlib.h>
32#include <sstream>
33#include <unistd.h>
34#include <vector>
35
36
37// We declare an auto pointer to HakosRoof.
38std::unique_ptr<HakosRoof> hakosRoof(new HakosRoof());
39
40
41static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
42{
43 ((std::string*)userp)->append((char*)contents, size * nmemb);
44 return size * nmemb;
45}
46
48{
49 // Hakos Roof doesn't rely on the base dome class for connection
51
52 // Set driver version
53 setVersion(1, 3);
54
55 //Hakos Roof is a Roll Off Roof. We implement only basic Dome functions to open / close the roof
57}
58
60{
61 return (const char *)"Hakos Roll Off Roof";
62}
63
64
65/************************************************************************************
66 *
67* ***********************************************************************************/
69{
71
75
76 // Roof data
77 RoofDataTP[0].fill("MSG", "Last Command", "");
78 RoofDataTP[1].fill("STAT", "Stat", "");
79 RoofDataTP[2].fill("STEXT", "Status", "");
80 RoofDataTP[3].fill("POS", "Position", "");
81 RoofDataTP[4].fill("REL_POSITION", "Current Position %", "");
82 RoofDataTP.fill(getDeviceName(), "ROOF_DATA", "Roof Data", MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE);
83
84 // Source File
85 DataSourceTP[0].fill("URL", "URL", "");
86 DataSourceTP.fill(getDeviceName(), "DATA_SOURCE", "Data Source", OPTIONS_TAB, IP_RW, 60, IPS_IDLE);
87 DataSourceTP.load();
88
89 // Roof Token
90 RoofTokenTP[0].fill("TOKEN", "Token", "");
91 RoofTokenTP.fill(getDeviceName(), "ROOF_TOKEN", "Roof Token", OPTIONS_TAB, IP_RW, 60, IPS_IDLE);
92 RoofTokenTP.load();
93
94 // Encoder Ticks
95 EncoderTicksNP[0].fill("ENCODER_TICKS", "Encoder Ticks", "%6.0f", 0, 100000, 1, 0);
96 EncoderTicksNP.fill(getDeviceName(), "ENCODER_TICKS", "Max Roof Travel", OPTIONS_TAB, IP_RW, 60, IPS_IDLE);
97 EncoderTicksNP.load();
98
99 // Simulation Mode
100 SimulationSP[0].fill("SIMULATE", "Simulate Device", ISS_OFF);
101 SimulationSP.fill(getDeviceName(), "SIMULATION", "Simulation", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
102 SimulationSP.load();
103
104 return true;
105}
106
107
109{
110 if (DataSourceTP[0].getText() == nullptr || DataSourceTP[0].getText()[0] == '\0')
111 {
112 //LOG_ERROR("A source file or url must be specified in options tab.");
113 return true;
114 }
115
116 return true;
117}
118
120{
122
123 if (isConnected())
124 {
126 defineProperty(DataSourceTP);
127 defineProperty(RoofDataTP);
128 defineProperty(RoofTokenTP);
129 defineProperty(EncoderTicksNP);
130 defineProperty(SimulationSP);
131
132
133 }
134 else
135 {
136 deleteProperty(DataSourceTP);
137 deleteProperty(RoofDataTP);
138 deleteProperty(RoofTokenTP);
139 deleteProperty(EncoderTicksNP);
140 deleteProperty(SimulationSP);
141
142 }
143
144 return true;
145}
146
147void HakosRoof::ISGetProperties(const char *dev)
148{
150
151 defineProperty(DataSourceTP);
152 defineProperty(RoofDataTP);
153 defineProperty(RoofTokenTP);
154 defineProperty(EncoderTicksNP);
155 defineProperty(SimulationSP);
156}
157
158bool HakosRoof::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
159{
160 // Make sure it is for us.
161 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
162 {
163 if (EncoderTicksNP.isNameMatch(name))
164 {
165 EncoderTicksNP.update(values, names, n);
166 EncoderTicksNP.setState(IPS_OK);
167 EncoderTicksNP.apply();
168 saveConfig(EncoderTicksNP);
169 return true;
170 }
171 }
172
173 // Nobody has claimed this, so let the parent handle it
174 return INDI::Dome::ISNewNumber(dev, name, values, names, n);
175}
176
177bool HakosRoof::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
178{
179 // Make sure it is for us.
180 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
181 {
182 if (SimulationSP.isNameMatch(name))
183 {
184 SimulationSP.update(states, names, n);
185 isSimulated = (SimulationSP[0].getState() == ISS_ON);
186 SimulationSP.setState(IPS_OK);
187 SimulationSP.apply();
188
189 if (isSimulated)
190 {
191 LOG_INFO("Simulation mode enabled.");
192 simulatedPosition = 0;
193 }
194 else
195 {
196 LOG_INFO("Simulation mode disabled.");
197 }
198 return true;
199 }
200 }
201
202 // Nobody has claimed this, so let the parent handle it
203 return INDI::Dome::ISNewSwitch(dev, name, states, names, n);
204}
205
206bool HakosRoof::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
207{
208 // Make sure it is for us.
209 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
210 {
211 if (DataSourceTP.isNameMatch(name))
212 {
213 DataSourceTP.update(texts, names, n);
214 DataSourceTP.setState(IPS_OK);
215 DataSourceTP.apply();
216 saveConfig(DataSourceTP);
217
218 LOGF_INFO("Data source URL: %s.", DataSourceTP[0].getText());
219 return true;
220 }
221
222 if (RoofTokenTP.isNameMatch(name))
223 {
224 RoofTokenTP.update(texts, names, n);
225 RoofTokenTP.setState(IPS_OK);
226 RoofTokenTP.apply();
227 saveConfig(RoofTokenTP);
228
229 LOGF_INFO("Roof Token: %s.", RoofTokenTP[0].getText());
230 return true;
231 }
232
233 if (RoofDataTP.isNameMatch(name))
234 {
235 RoofDataTP.update(texts, names, n);
236 RoofDataTP.setState(IPS_OK);
237 RoofDataTP.apply();
238
239
240 return true;
241 }
242 }
243
244 return INDI::Dome::ISNewText(dev, name, texts, names, n);
245}
246
248{
249
250 if (!isConnected())
251 return; // No need to reset timer if we are not connected anymore
252
253 if (isSimulated)
254 {
255 simulateRoofCommand("status");
256 }
257 else
258 {
259 sendRoofCommand("status");
260 }
261
262 if (getDomeState() == DOME_IDLE)
263 {
264 LOG_INFO("Dome state IDLE.");
265 }
266 else if (getDomeState() == DOME_UNPARKED )
267 {
268 LOG_INFO("Dome state UNPARKED.");
269 }
270 else if (getDomeState() == DOME_PARKED )
271 {
272 LOG_INFO("Dome state PARKED.");
273 }
274 else if (getDomeState() == DOME_SYNCED )
275 {
276 LOG_INFO("Dome state SYNCED.");
277 }
278 else if (getDomeState() == DOME_UNKNOWN )
279 {
280 LOG_INFO("Dome state UNKNOWN.");
281 }
282 else if (getDomeState() == DOME_UNPARKING )
283 {
284 LOG_INFO("Dome state UNPARKING.");
285 }
286 else if (getDomeState() == DOME_PARKING )
287 {
288 LOG_INFO("Dome state PARKING.");
289 }
290 else if (getDomeState() == DOME_MOVING )
291 {
292 LOG_INFO("Dome state MOVING.");
293 }
294
295 // If the roof was in mount 'not parked' status but the mount
296 //is now parked, the roof has to be closed.
297 if (strcmp(RoofDataTP[2].getText() ,"not parked") == 0 )
298 {
299
301
303 {
304 usleep(2000000);
305 LOG_INFO("Resuming Park command.");
306 Park();
308
309 return;
310 }
311
312 }
313
314 if (strcmp(RoofDataTP[2].getText() ,"open") == 0)
315 {
316 LOG_INFO("Roof is open.");
318 SetParked(false);
320
321 return;
322 }
323 if (strcmp(RoofDataTP[2].getText() ,"closed") == 0)
324 {
325 LOG_INFO("Roof is closed.");
327 SetParked(true);
329 return;
330 }
331
332 if (strcmp(RoofDataTP[2].getText() ,"opening") == 0)
333 {
335 SetParked(false);
336 }
337 if (strcmp(RoofDataTP[2].getText() ,"closing") == 0)
338 {
340 SetParked(false);
341 }
342
343
344 // Abort called
345 if (strcmp(RoofDataTP[2].getText() ,"idle") == 0)
346 {
347 LOG_INFO("Roof motion is stopped.");
349 AbortDriver();
351 return;
352 }
354 {
355 SetTimer(1000);
356 }
357 else
358 {
360
361 }
362
363}
364
366{
367
369
370 RoofTokenTP.save(fp);
371 DataSourceTP.save(fp);
372 EncoderTicksNP.save(fp);
373
374 return true;
375
376}
377
379{
380 // If the roof is 'not parked' then just stop any motion
381
382 if (operation == MOTION_START)
383 {
384 // DOME_CW --> OPEN. If can we are ask to "open" while we are fully opened as the limit switch indicates, then we simply return false.
385
386 if (dir == DOME_CW && strcmp(RoofDataTP[2].getText() ,"open") == 0)
387 {
388 LOG_WARN("Roof is already fully opened.");
389 return IPS_ALERT;
390 }
391 else if (dir == DOME_CCW && strcmp(RoofDataTP[2].getText() ,"closed") == 0)
392 {
393 LOG_WARN("Roof is already fully closed.");
394 return IPS_ALERT;
395 }
396 else if (dir == DOME_CCW && INDI::Dome::isLocked())
397 {
399 "Cannot close dome when mount is locking. See: Telescope parking policy, in options tab");
400 return IPS_ALERT;
401 }
402
403 SetTimer(1000);
404 return IPS_BUSY;
405 }
406
407 return (Dome::Abort() ? IPS_OK : IPS_ALERT);
408}
409
411{
413 if (rc == IPS_BUSY)
414 {
415 sendRoofCommand("close");
416 LOG_INFO("Roof is parking...");
417 return IPS_BUSY;
418 }
419 else
420 return IPS_ALERT;
421}
422
424{
426 if (rc == IPS_BUSY)
427 {
428 sendRoofCommand("open");
429 LOG_INFO("Roof is unparking...");
430 return IPS_BUSY;
431 }
432 else
433 return IPS_ALERT;
434}
435
437{
438 ParkSP.reset();
440 ParkSP.apply();
441 sendRoofCommand("stop");
442 LOG_INFO("Roof stopped");
443
444 return true;
445}
447{
448 ParkSP.reset();
450 ParkSP.apply();
451 LOG_INFO("Roof stopped");
452
453 return true;
454}
455
456bool HakosRoof::sendRoofCommand(const char *action)
457{
458 //LOGF_INFO("Sending command: %s", action);
459
460 // Check if required data is available
461 if (DataSourceTP[0].getText() == nullptr || DataSourceTP[0].getText()[0] == '\0')
462 {
463 LOG_WARN("Data source URL not configured. Falling back to simulation mode.");
464 return simulateRoofCommand(action);
465 }
466
467 if (RoofTokenTP[0].getText() == nullptr || RoofTokenTP[0].getText()[0] == '\0')
468 {
469 LOG_WARN("Roof token not configured. Falling back to simulation mode.");
470 return simulateRoofCommand(action);
471 }
472
473 std::string readBuffer = "";
474
475 curl_global_init(CURL_GLOBAL_ALL);
476 CURL* easyhandle = curl_easy_init();
477 CURLcode res = CURLE_OK;
478
479 std::string reqCommand = DataSourceTP[0].getText();
480 reqCommand += "/remobs?key=";
481 reqCommand += RoofTokenTP[0].getText();
482 reqCommand += "&action=";
483 reqCommand += action;
484
485 curl_easy_setopt(easyhandle, CURLOPT_FOLLOWLOCATION, 1);
486 curl_easy_setopt(easyhandle, CURLOPT_VERBOSE, 1);
487 curl_easy_setopt(easyhandle, CURLOPT_WRITEFUNCTION, WriteCallback);
488 curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, &readBuffer);
489 curl_easy_setopt(easyhandle, CURLOPT_TIMEOUT, 2);
490 curl_easy_setopt(easyhandle, CURLOPT_URL, reqCommand.c_str()); //http://jsonkeeper.com/b/4488
491
492 res = curl_easy_perform(easyhandle);
493 curl_easy_cleanup(easyhandle);
494
495 if (res != CURLE_OK) {
496 LOGF_WARN("Error %s. Falling back to simulation mode.", curl_easy_strerror(res));
497 return simulateRoofCommand(action);
498 }
499 else
500 {
501 LOGF_INFO("json data %s", readBuffer.c_str());
502 if (strcmp(action ,"status") == 0)
503 {
504 json j = json::parse(readBuffer);
505
506 std::string msg = j["msg"];
507 std::string stat= j["stat"];
508 std::string stext= j["stext"];
509 int i = j["pos"];
510 std::string pos = std::to_string(i);
511
512 RoofDataTP[0].setText(msg.c_str());
513 RoofDataTP[1].setText(stat.c_str());
514 RoofDataTP[2].setText(stext.c_str());
515 RoofDataTP[3].setText(pos.c_str());
516
517 RoofDataTP.apply();
518
519
520 }
521 }
522
523 return true;
524}
525
527{
528 std::string statString = RoofDataTP[1].getText();
529 std::vector<std::string> statVector{};
530 std::stringstream sstream(statString);
531 std::string statElement;
532 while (std::getline(sstream, statElement, ',')){
533 statVector.push_back(statElement);
534 }
535
536 if (strcmp(statVector.at(9).c_str() ,"0") == 0)
537 {
538 return true;
539 }
540 else
541 return false;
542}
543
544bool HakosRoof::simulateRoofCommand(const char *action)
545{
546 // Simulate roof behavior for testing without hardware
547 // Transition time: 10 seconds from 0 to 100%
548 const int SIMULATION_TIME_MS = 10000; // 10 seconds
549
550 if (strcmp(action, "open") == 0)
551 {
552 simulationTargetPosition = 100;
553 gettimeofday(&simulationStartTime, nullptr);
554 isMoving = true;
555 LOG_INFO("Simulated: Roof opening (10 second transition)...");
556 }
557 else if (strcmp(action, "close") == 0)
558 {
559 simulationTargetPosition = 0;
560 gettimeofday(&simulationStartTime, nullptr);
561 isMoving = true;
562 LOG_INFO("Simulated: Roof closing (10 second transition)...");
563 }
564 else if (strcmp(action, "stop") == 0)
565 {
566 isMoving = false;
567 LOG_INFO("Simulated: Roof stopped.");
568 }
569 else if (strcmp(action, "status") == 0)
570 {
571 // Update position based on elapsed time if moving
572 if (isMoving)
573 {
574 struct timeval now;
575 gettimeofday(&now, nullptr);
576
577 // Calculate elapsed time in milliseconds
578 long elapsedMs = (now.tv_sec - simulationStartTime.tv_sec) * 1000 +
579 (now.tv_usec - simulationStartTime.tv_usec) / 1000;
580
581 // Calculate progress (0.0 to 1.0)
582 float progress = (float)elapsedMs / SIMULATION_TIME_MS;
583
584 if (progress >= 1.0f)
585 {
586 // Movement complete - snap to target
587 simulatedPosition = simulationTargetPosition;
588 isMoving = false;
589 const char *endMsg = (simulationTargetPosition == 100) ? "Roof fully open" : "Roof fully closed";
590 LOG_INFO(endMsg);
591 }
592 else
593 {
594 // Calculate intermediate position
595 int startPos = (simulationTargetPosition == 100) ? 0 : 100;
596 simulatedPosition = startPos + (simulationTargetPosition - startPos) * progress;
597 }
598 }
599
600 // Update with simulated status data
601 const char *status_msg = isMoving ? "Moving..." : "Idle";
602 // Status text is now based on actual position after movement completes
603 const char *stext = (simulatedPosition >= 100) ? "open" : (simulatedPosition <= 0) ? "closed" : "moving";
604 char pos_str[10];
605 snprintf(pos_str, sizeof(pos_str), "%d", simulatedPosition);
606
607 const char *stat = "0,0,0,0,0,0,0,0,0,1"; // Mount sensor at position 9
608
609 RoofDataTP[0].setText(status_msg);
610 RoofDataTP[1].setText(stat);
611 RoofDataTP[2].setText(stext);
612 RoofDataTP[3].setText(pos_str);
613 RoofDataTP[4].setText(pos_str);
614
615 RoofDataTP.apply();
616
617 if (isMoving)
618 {
619 LOGF_INFO("Simulated roof moving: Position=%d%%, Target=%d%%, Status=%s", simulatedPosition, simulationTargetPosition, stext);
620 }
621 else
622 {
623 LOGF_INFO("Simulated roof status: Position=%d%%, Status=%s", simulatedPosition, stext);
624 }
625 }
626
627 return true;
628}
629
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual IPState Park() override
Goto Park Position. The park position is an absolute azimuth value.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool Abort() override
Abort all dome motion.
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual IPState Move(DomeDirection dir, DomeMotionCommand operation) override
Move the Dome in a particular direction.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
const char * getDefaultName() override
virtual IPState UnPark() override
UnPark dome. The action of the Unpark command is dome specific, but it may include opening the shutte...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool getMountSensorSwitch()
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
bool isConnected() const
INDI::PropertyText getText(const char *name) const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
void SetDomeCapability(uint32_t cap)
SetDomeCapability set the dome capabilities. All capabilities must be initialized.
void setDomeConnection(const uint8_t &value)
setDomeConnection Set Dome connection mode. Child class should call this in the constructor before Do...
@ DOME_UNPARKING
Definition indidome.h:135
@ DOME_UNPARKED
Definition indidome.h:137
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
Definition indidome.cpp:270
DomeMotionCommand
Definition indidome.h:97
@ MOTION_START
Definition indidome.h:98
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
Definition indidome.cpp:105
@ DOME_CAN_PARK
Definition indidome.h:159
@ DOME_CAN_ABORT
Definition indidome.h:156
bool isLocked()
isLocked, is the dome currently locked?
INDI::PropertySwitch ParkSP
Definition indidome.h:548
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition indidome.cpp:284
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition indidome.cpp:503
INDI::PropertySwitch DomeMotionSP
Definition indidome.h:534
@ CONNECTION_NONE
Definition indidome.h:171
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Dome Presets in the configuration file
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition indidome.cpp:809
void setDomeState(const DomeState &value)
void SetParkDataType(DomeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition indidome.cpp:394
DomeState getDomeState() const
Definition indidome.h:285
virtual IPState Move(DomeDirection dir, DomeMotionCommand operation)
Move the Dome in a particular direction.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
void save(FILE *f) const
save Save property to configuration file.
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
bool update(const char *const texts[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
INDI::PropertyViewText * getText() const
a class to store JSON values
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
std::unique_ptr< HakosRoof > hakosRoof(new HakosRoof())
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
Implementations for common driver routines.
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
Definition indilogger.h:56
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName