Instrument Neutral Distributed Interface INDI 2.2.5
wanderer_cover.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2025 Jérémie Klein. All rights reserved.
3
4 Wanderer cover V3
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 Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 The full GNU General Public License is included in this distribution in the
22 file called LICENSE.
23*******************************************************************************/
24
25#include "wanderer_cover.h"
26
27#include "indicom.h"
29
30#include <cerrno>
31#include <cstring>
32#include <memory>
33#include <termios.h>
34#include <unistd.h>
35#include <inttypes.h>
36#include <sys/ioctl.h>
37
38// We declare an auto pointer to WandererCover.
39static std::unique_ptr<WandererCover> wanderercover(new WandererCover());
40#define PARK 1
41#define DATA_BITS 8
42#define FLAT_CMD 6
43#define WANDERER_RESPONSE_SIZE 1
44#define COMMAND_WAITING_TIME 120
45
46#define FIRST_SUPPORTED_VERSION "20220920"
47
48#define TAB_NAME_CONFIGURATION "Dust cover configuration"
49
50#define CLOSE_COVER_COMMAND "1000\n"
51#define HANDSHAKE_COMMAND "1500001\n"
52#define OPEN_COVER_COMMAND "1001\n"
53#define TURN_OFF_LIGHT_PANEL_COMMAND "9999\n"
54
55# define SET_CURRENT_POSITION_TO_OPEN_POSITION "257\n"
56# define SET_CURRENT_POSITION_TO_CLOSED_POSITION "256\n"
57
58
63
65{
66 LOG_INFO("Initializing Wanderer Cover properties...");
70
73
74 // Status
75 IUFillText(&StatusT[0], "Cover", "Cover", nullptr);
76 IUFillText(&StatusT[1], "Light", "Light", nullptr);
77 IUFillTextVector(&StatusTP, StatusT, 2, getDeviceName(), "Status", "Status", MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE);
78
79 // Firmware version
80 IUFillText(&FirmwareT[0], "Version", "Version", nullptr);
81 IUFillTextVector(&FirmwareTP, FirmwareT, 1, getDeviceName(), "Firmware", "Firmware", MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE);
82
83 // Configuration
84 IUFillSwitch(&ControlPositionPositiveDegreesConfigurationV[PLUS_1_DEGREE], "PLUS_1_DEGREE", "+ 1°", ISS_OFF);
85 IUFillSwitch(&ControlPositionPositiveDegreesConfigurationV[PLUS_10_DEGREE], "PLUS_10_DEGREE", "+ 10°", ISS_OFF);
86 IUFillSwitch(&ControlPositionPositiveDegreesConfigurationV[PLUS_50_DEGREE], "PLUS_50_DEGREE", "+ 50°", ISS_OFF);
87 IUFillSwitchVector(&ControlPositionPositiveDegreesConfigurationVP, ControlPositionPositiveDegreesConfigurationV, 3,
88 getDeviceName(), "Open dust cap", "Open dust cap", TAB_NAME_CONFIGURATION, IP_RW,
90
91 IUFillSwitch(&ControlPositionNegativeDegreesConfigurationV[MINUS_1_DEGREE], "MINUS_1_DEGREE", "- 1°", ISS_OFF);
92 IUFillSwitch(&ControlPositionNegativeDegreesConfigurationV[MINUS_10_DEGREE], "MINUS_10_DEGREE", "- 10°", ISS_OFF);
93 IUFillSwitch(&ControlPositionNegativeDegreesConfigurationV[MINUS_50_DEGREE], "MINUS_50_DEGREE", "- 50°", ISS_OFF);
94 IUFillSwitchVector(&ControlPositionNegativeDegreesConfigurationVP, ControlPositionNegativeDegreesConfigurationV, 3,
95 getDeviceName(), "Close dust cap", "Close dust cap", TAB_NAME_CONFIGURATION, IP_RW,
97
98 IUFillSwitch(&DefinePositionConfigurationV[SET_CURRENT_POSITION_OPEN], "Set current position as open",
99 " 1 - Set current position as open", ISS_OFF);
100 IUFillSwitch(&DefinePositionConfigurationV[SET_CURRENT_POSITION_CLOSE], "Set current position as close",
101 "2 - Set current position as close", ISS_OFF);
102 IUFillSwitchVector(&DefinePositionConfigurationVP, DefinePositionConfigurationV, 2, getDeviceName(), "Define position",
103 "Action", TAB_NAME_CONFIGURATION, IP_RW,
105
106 LightIntensityNP[0].setMin(1);
107 LightIntensityNP[0].setMax(255);
108 LightIntensityNP[0].setStep(10);
109
110 serialConnection = new Connection::Serial(this);
112 serialConnection->registerHandshake([&]()
113 {
114 return Handshake();
115 });
116 registerConnection(serialConnection);
117
118 LOG_INFO("Properties initialization complete");
119 return true;
120}
121
122bool WandererCover::Handshake()
123{
124 LOG_INFO("Starting handshake with device...");
125 if (isSimulation())
126 {
127 LOGF_INFO("Connected successfully to simulated %s. Retrieving startup data...", getDeviceName());
128
129 // SetTimer(getCurrentPollingPeriod());
130 IUSaveText(&FirmwareT[0], "Simulation version");
131 IDSetText(&FirmwareTP, nullptr);
132
133 updateCoverStatus((char*) "0");
134
135 setLightBoxStatusAsSwitchedOff();
136
137 NumberOfStepsBeetweenOpenAndCloseState = 0;
138 setNumberOfStepsStatusValue(NumberOfStepsBeetweenOpenAndCloseState);
139
141 return true;
142 }
143 PortFD = serialConnection->getPortFD();
144 tcflush(PortFD, TCIOFLUSH);
145 int nbytes_read_name = 0, nbytes_written = 0, rc = -1;
146 char name[64] = {0};
147 LOGF_DEBUG("CMD <%s>", HANDSHAKE_COMMAND);
148 if ((rc = tty_write_string(PortFD, HANDSHAKE_COMMAND, &nbytes_written)) != TTY_OK)
149 {
150 char errorMessage[MAXRBUF];
151 tty_error_msg(rc, errorMessage, MAXRBUF);
152 LOGF_ERROR("Serial write error: %s", errorMessage);
153 return false;
154 }
155
156 if ((rc = tty_read_section(PortFD, name, 'A', 5, &nbytes_read_name)) != TTY_OK)
157 {
158 char errorMessage[MAXRBUF];
159 tty_error_msg(rc, errorMessage, MAXRBUF);
160 LOGF_ERROR("Device read error: %s", errorMessage);
161 return false;
162 }
163 name[nbytes_read_name - 1] = '\0';
164 LOGF_DEBUG("Name : <%s>", name);
165
166 int nbytes_read_version = 0;
167 char version[64] = {0};
168 if ((rc = tty_read_section(PortFD, version, 'A', 5, &nbytes_read_version)) != TTY_OK)
169 {
170 char errorMessage[MAXRBUF];
171 tty_error_msg(rc, errorMessage, MAXRBUF);
172 LOGF_ERROR("Device read error: %s", errorMessage);
173 LOGF_ERROR("You have an old firmware. This version is not supported. You should update the device as described here : %s",
174 "https://www.wandererastro.com/en/col.jsp?id=106");
175 return true;
176 }
177
178 displayConfigurationMessage();
179 version[nbytes_read_version - 1] = '\0';
180 LOGF_INFO("Version : %s", version);
181 IUSaveText(&FirmwareT[0], version);
182 IDSetText(&FirmwareTP, nullptr);
183
184
185 // Cover status
186 char cover_state[64] = {0};
187 int nbytes_read_cover_state = 0;
188
189 if ((rc = tty_read_section(PortFD, cover_state, 'A', 5, &nbytes_read_cover_state)) != TTY_OK)
190 {
191 char errorMessage[MAXRBUF];
192 tty_error_msg(rc, errorMessage, MAXRBUF);
193 LOGF_ERROR("Device read error: %s", errorMessage);
194 return false;
195 }
196 cover_state[nbytes_read_cover_state - 1] = '\0';
197 LOGF_INFO("Cover state : %s", cover_state);
198 updateCoverStatus(cover_state);
199
200 // Number of steps
201 char number_of_steps[64] = {0};
202 int nbytes_read_number_of_steps = 0;
203
204 if ((rc = tty_read_section(PortFD, number_of_steps, 'A', 5, &nbytes_read_number_of_steps)) != TTY_OK)
205 {
206 char errorMessage[MAXRBUF];
207 tty_error_msg(rc, errorMessage, MAXRBUF);
208 LOGF_ERROR("Device read error: %s", errorMessage);
209 return false;
210 }
211 number_of_steps[nbytes_read_number_of_steps - 1] = '\0';
212 LOGF_INFO("Number of steps between open and close states : %s", cover_state);
213 NumberOfStepsBeetweenOpenAndCloseState = sscanf(number_of_steps, "%d", &NumberOfStepsBeetweenOpenAndCloseState);
214 if (NumberOfStepsBeetweenOpenAndCloseState == 0)
215 {
216 LOGF_ERROR("The number of steps is 0 meaning the falt panel may hit an obstacle. You should define opening and closing position first.",
217 "");
218 }
219 setNumberOfStepsStatusValue(NumberOfStepsBeetweenOpenAndCloseState);
220
221 setLightBoxStatusAsSwitchedOff();
222
223 LOGF_INFO("Handshake successful:%s", name);
224 tcflush(PortFD, TCIOFLUSH);
225 return true;
226}
227
228void WandererCover::updateCoverStatus(char* res)
229{
230 if (strcmp(res, "0") == 0 && isCoverOpen == true)
231 {
232 isCoverOpen = false;
233 setParkCapStatusAsClosed();
234 }
235 else if (strcmp(res, "1") == 0 && isCoverOpen == false)
236 {
237 isCoverOpen = true;
238 setParkCapStatusAsOpen();
239 }
240 else if (strcmp(res, "255") == 0)
241 {
242 LOGF_INFO("No cover status information available. You should first open/close the cover.", "");
243 }
244}
245
247{
249
250 // Get Light box properties
252}
253
255{
257
259
260 if (isConnected())
261 {
262 defineProperty(&StatusTP);
263 defineProperty(&FirmwareTP);
264
265 defineProperty(&ControlPositionPositiveDegreesConfigurationVP);
266 defineProperty(&ControlPositionNegativeDegreesConfigurationVP);
267 defineProperty(&DefinePositionConfigurationVP);
268
269 getStartupData();
270 }
271 else
272 {
273 deleteProperty(StatusTP.name);
274 deleteProperty(FirmwareTP.name);
275
276 deleteProperty(ControlPositionPositiveDegreesConfigurationVP.name);
277 deleteProperty(ControlPositionNegativeDegreesConfigurationVP.name);
278 deleteProperty(DefinePositionConfigurationVP.name);
279 }
280
282 return true;
283}
284
286{
287 return "Wanderer Cover v3";
288}
289
290bool WandererCover::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
291{
292 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
293 {
294 if (LI::processNumber(dev, name, values, names, n))
295 return true;
296 }
297
298 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
299}
300
301bool WandererCover::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
302{
303 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
304 {
305 if (LI::processText(dev, name, texts, names, n))
306 return true;
307 }
308
309 return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
310}
311
312bool WandererCover::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
313{
314 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
315 {
316 if (DI::processSwitch(dev, name, states, names, n))
317 return true;
318
319 if (LI::processSwitch(dev, name, states, names, n))
320 return true;
321
322 if (processConfigurationButtonSwitch(dev, name, states, names, n))
323 return true;
324 }
325
326 return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
327}
328
330{
331 LI::snoop(root);
332
334}
335
342
343bool WandererCover::getStartupData()
344{
345 // // Closing cover
346 // IUSaveText(&StatusT[0], "Closed");
347 // IUResetSwitch(&ParkCapSP);
348 // ParkCapS[0].s = ISS_ON;
349 // ParkCapSP.s = IPS_OK;
350 // LOG_INFO("Cover assumed as closed.");
351 // IDSetSwitch(&ParkCapSP, nullptr);
352
353 // // Switching off lamp
354 // IUSaveText(&StatusT[1], "Off");
355 // LightS[0].s = ISS_OFF;
356 // LightS[1].s = ISS_ON;
357 // LightSP.apply();
358 // LightIntensityN[0].value = 0;
359 // LOG_INFO("Light assumed as off.");
360 // LightIntensityNP.apply();
361
362 return true;
363}
364
366{
367 LOG_DEBUG("=== Starting ParkCap() - Attempting to close dust cap ===");
368
369 LOGF_DEBUG("Checking number of steps: %d", NumberOfStepsBeetweenOpenAndCloseState);
370 if (NumberOfStepsBeetweenOpenAndCloseState == 0)
371 {
372 LOG_ERROR("Number of steps is 0 - Configuration required");
373 LOGF_ERROR("The number of steps is 0 meaning the flat panel may hit an obstacle. You should define opening and closing position first.", "");
374 return IPS_ALERT;
375 }
376
377 LOGF_DEBUG("Checking current cover state: isCoverOpen = %s", isCoverOpen ? "true" : "false");
378 if (!isCoverOpen)
379 {
380 LOG_INFO("Cover is already closed - No action needed");
381 return IPS_OK;
382 }
383
384 LOGF_DEBUG("Checking simulation mode: %s", isSimulation() ? "true" : "false");
385 if (isSimulation())
386 {
387 LOG_INFO("In simulation mode - Setting status to closed");
388 setParkCapStatusAsClosed();
389 return IPS_OK;
390 }
391
392 LOG_DEBUG("Sending close command to device");
393 char response[20];
394 if (!sendCommand(CLOSE_COVER_COMMAND, response, true))
395 {
396 LOG_ERROR("Failed to send close command");
397 return IPS_ALERT;
398 }
399
400 LOGF_DEBUG("Checking response for errors: %s", response);
401 if (hasWandererSentAnError(response))
402 {
403 LOG_ERROR("Device returned error - Configuration required");
404 LOG_ERROR("You need to configure Open and closed position first in 'Dust cover configuration' tab.");
405 return IPS_ALERT;
406 }
407
408 LOG_DEBUG("Close command successful - Updating status");
409 setParkCapStatusAsClosed();
410
411 LOG_DEBUG("=== ParkCap() completed successfully ===");
412 return IPS_OK;
413}
414
416{
417 LOG_DEBUG("=== Starting UnParkCap() - Attempting to open dust cap ===");
418
419 LOGF_DEBUG("Checking number of steps: %d", NumberOfStepsBeetweenOpenAndCloseState);
420 if (NumberOfStepsBeetweenOpenAndCloseState == 0)
421 {
422 LOG_ERROR("Number of steps is 0 - Configuration required");
423 LOG_ERROR("The number of steps is 0 meaning the flat panel may hit an obstacle. You should define opening and closing position first.");
424 return IPS_ALERT;
425 }
426
427 LOGF_DEBUG("Checking current cover state: isCoverOpen = %s", isCoverOpen ? "true" : "false");
428 if (isCoverOpen)
429 {
430 LOG_INFO("Cover is already open - No action needed");
431 return IPS_OK;
432 }
433
434 LOGF_DEBUG("Checking simulation mode: %s", isSimulation() ? "true" : "false");
435 if (isSimulation())
436 {
437 LOG_INFO("In simulation mode - Setting status to open");
438 setParkCapStatusAsOpen();
439 return IPS_OK;
440 }
441
442 LOG_DEBUG("Sending open command to device");
443 char response[20];
444 if (!sendCommand(OPEN_COVER_COMMAND, response, true))
445 {
446 LOG_ERROR("Failed to send open command");
447 return IPS_ALERT;
448 }
449
450 LOGF_DEBUG("Checking response for errors: %s", response);
451 if (hasWandererSentAnError(response))
452 {
453 LOG_ERROR("Device returned error - Displaying configuration message");
454 displayConfigurationMessage();
455 return IPS_ALERT;
456 }
457
458 LOG_DEBUG("Open command successful - Updating status");
459 setParkCapStatusAsOpen();
460
461 LOG_DEBUG("=== UnParkCap() completed successfully ===");
462 return IPS_OK;
463}
464
465void WandererCover::setParkCapStatusAsClosed()
466{
467 IUSaveText(&StatusT[0], "Closed");
468 IDSetText(&StatusTP, nullptr);
473 isCoverOpen = false;
474 LOG_INFO("Cover closed.");
476}
477
478void WandererCover::setParkCapStatusAsOpen()
479{
480 IUSaveText(&StatusT[0], "Open");
481 IDSetText(&StatusTP, nullptr);
486 isCoverOpen = true;
487 LOG_INFO("Cover open.");
489}
490
492{
493 LOGF_INFO("Setting light box state to: %s", enable ? "ON" : "OFF");
494 if (enable == isLightOn)
495 {
496 LOG_INFO(enable ? "Light box is already on." : "Light box is already off.");
497 return true;
498 }
499
500 if (enable)
501 {
502 return SetLightBoxBrightness(255);
503 }
504 else
505 {
506 return switchOffLightBox();
507 }
508
509 return false;
510}
511
512bool WandererCover::switchOffLightBox()
513{
514 if (isSimulation())
515 {
516 setLightBoxStatusAsSwitchedOff();
517 return true;
518 }
519 char response[WANDERER_RESPONSE_SIZE];
520
521 if (!sendCommand(TURN_OFF_LIGHT_PANEL_COMMAND, response, false))
522 return false;
523
524 setLightBoxStatusAsSwitchedOff();
525 return true;
526}
527
528void WandererCover::setLightBoxStatusAsSwitchedOff()
529{
530 IUSaveText(&StatusT[1], "Off");
531 IDSetText(&StatusTP, nullptr);
534 LightIntensityNP[0].setValue(0);
536 LightSP.apply();
537 isLightOn = false;
538 LOG_INFO("Light panel switched off");
539}
540
541
543{
544 LOGF_INFO("Setting light box brightness to %d", value);
545 if (isSimulation())
546 {
547 setLightBoxBrightnesStatusToValue(value);
548 return true;
549 }
550 char response[WANDERER_RESPONSE_SIZE];
551 char command[5] = {0};
552 snprintf(command, 5, "%03d\n", value);
553 if (!sendCommand(command, response, false))
554 return false;
555
556 setLightBoxBrightnesStatusToValue(value);
557
558 return true;
559}
560
561bool WandererCover::setCurrentPositionToOpenPosition()
562{
563 LOG_INFO("Setting current position as open position...");
564 if (isSimulation())
565 {
566 LOG_INFO("Current position set to open position");
567 NumberOfDegreesSinceLastOpenPositionSet = 0;
568 return true;
569 }
570 char response[WANDERER_RESPONSE_SIZE];
571 if (!sendCommand(SET_CURRENT_POSITION_TO_OPEN_POSITION, response, false))
572 return false;
573
574 NumberOfDegreesSinceLastOpenPositionSet = 0;
575
576 LOG_INFO("Open position successfully set");
577 return true;
578}
579
580bool WandererCover::setCurrentPositionToClosedPosition()
581{
582 LOG_INFO("Setting current position as closed position...");
583 int cumulative_angle_value = ((abs(NumberOfDegreesSinceLastOpenPositionSet) * 222.22) / 10) + 10000;
584 LOGF_INFO("Calculated cumulative angle: %d", cumulative_angle_value);
585
586 if (isSimulation())
587 {
588 LOG_INFO("Current position set to closed position");
589 LOGF_INFO("Sending cumulative angle of %d", cumulative_angle_value);
590 setNumberOfStepsStatusValue(cumulative_angle_value);
591 setParkCapStatusAsClosed();
592 return true;
593 }
594 char response[WANDERER_RESPONSE_SIZE];
595 if (!sendCommand(SET_CURRENT_POSITION_TO_CLOSED_POSITION, response, false))
596 return false;
597
598 if (!sendCommand(std::to_string(cumulative_angle_value).c_str(), response, false))
599 return false;
600
601 setNumberOfStepsStatusValue(cumulative_angle_value);
602
603 setParkCapStatusAsClosed();
604
605 LOG_INFO("Closed position successfully set");
606 return true;
607}
608
609void WandererCover::setLightBoxBrightnesStatusToValue(uint16_t value)
610{
611 LightIntensityNP[0].setValue(value);
613 if (value == 0)
614 {
615 setLightBoxStatusAsSwitchedOff();
616 }
617 else
618 {
619 IUSaveText(&StatusT[1], "On");
620 IDSetText(&StatusTP, nullptr);
623 LightSP.apply();
624 isLightOn = true;
625 }
626 LOGF_INFO("Brightness set to %d.", value);
627}
628
629void WandererCover::setNumberOfStepsStatusValue(int value)
630{
631 LOGF_DEBUG("Current number of steps value configured between open and closed position : %d", value);
632 NumberOfStepsBeetweenOpenAndCloseState = value;
633 // NumberOfStepsBeetweenOpenAndCloseStateT[0].text = new char[5];
634 // NumberOfStepsBeetweenOpenAndCloseStateT[0].text = (char*) std::to_string(value).c_str();
635 // IDSetText(&NumberOfStepsBeetweenOpenAndCloseStateTP, nullptr);
636}
637
638bool WandererCover::sendCommand(std::string command, char *response, bool waitForAnswer)
639{
640 LOGF_INFO("Sending command: %s", command.c_str());
641 int nbytes_read = 0, nbytes_written = 0, rc = -1;
642 std::string command_termination = "\n";
643 LOGF_DEBUG("CMD: %s", command.c_str());
644 if ((rc = tty_write_string(PortFD, (command + command_termination).c_str(), &nbytes_written)) != TTY_OK)
645 {
646 char errorMessage[MAXRBUF];
647 tty_error_msg(rc, errorMessage, MAXRBUF);
648 LOGF_ERROR("Serial write error: %s", errorMessage);
649 return false;
650 }
651
652 if (waitForAnswer && (rc = tty_read_section(PortFD, response, 'A', COMMAND_WAITING_TIME, &nbytes_read)) != TTY_OK)
653 {
654 char errorMessage[MAXRBUF];
655 tty_error_msg(rc, errorMessage, MAXRBUF);
656 LOGF_ERROR("Device read error: %s", errorMessage);
657 return false;
658 }
659 LOGF_DEBUG("RESPONSE: %s", response);
660 SetTimer(150);
661 if (waitForAnswer)
662 {
663 LOGF_INFO("Received response: %s", response);
664 }
665 return true;
666}
667
668IPState WandererCover::moveDustCap(int degrees)
669{
670 LOGF_INFO("Moving dust cap by %d degrees", degrees);
671 if (degrees < -360 or degrees > 360)
672 {
673 LOGF_ERROR("Degrees must be between -360 and 360 : %d", degrees);
674 return IPS_ALERT;
675 }
676 if (isSimulation())
677 {
678 LOGF_INFO("Moving dust cap cover of %d degrees", degrees);
679 NumberOfDegreesSinceLastOpenPositionSet += degrees;
680 LOGF_INFO("Number of degrees since last open position set : %d", NumberOfDegreesSinceLastOpenPositionSet);
681 return IPS_OK;
682 }
683
684 int stepping_offset = 100000;
685 if (degrees < 0)
686 {
687 stepping_offset = -100000;
688 }
689
690 int command_value = (degrees * 222.22) + stepping_offset;
691
692 char response[3];
693 if (!sendCommand(std::to_string(command_value).c_str(), response, true))
694 return IPS_ALERT;
695
696 NumberOfDegreesSinceLastOpenPositionSet += degrees;
697 LOGF_DEBUG("Number of degrees since last open position set : %d", NumberOfDegreesSinceLastOpenPositionSet);
698 LOG_INFO("Dust cap movement complete");
699 return IPS_OK;
700}
701
702bool WandererCover::processConfigurationButtonSwitch(const char *dev, const char *name, ISState *states, char *names[],
703 int n)
704{
705 LOG_INFO("Processing configuration button press...");
706 if (strcmp(dev, getDeviceName()) == 0)
707 {
708 // configuration of open state clicked
709 if (!strcmp(ControlPositionPositiveDegreesConfigurationVP.name, name))
710 {
711 IUUpdateSwitch(&ControlPositionPositiveDegreesConfigurationVP, states, names, n);
712 if (ControlPositionPositiveDegreesConfigurationV[PLUS_1_DEGREE].s )
713 {
714 moveDustCap(1);
715 ControlPositionPositiveDegreesConfigurationV[PLUS_1_DEGREE].s = ISS_OFF;
716 }
717 else if (ControlPositionPositiveDegreesConfigurationV[PLUS_10_DEGREE].s == ISS_ON)
718 {
719 moveDustCap(10);
720 ControlPositionPositiveDegreesConfigurationV[PLUS_10_DEGREE].s = ISS_OFF;
721 }
722 else if (ControlPositionPositiveDegreesConfigurationV[PLUS_50_DEGREE].s == ISS_ON)
723 {
724 moveDustCap(50);
725 ControlPositionPositiveDegreesConfigurationV[PLUS_50_DEGREE].s = ISS_OFF;
726 }
727 IDSetSwitch(&ControlPositionPositiveDegreesConfigurationVP, nullptr);
728
729 LOG_INFO("Configuration button processing complete");
730 return true;
731 }
732
733 if (!strcmp(ControlPositionNegativeDegreesConfigurationVP.name, name))
734 {
735 IUUpdateSwitch(&ControlPositionNegativeDegreesConfigurationVP, states, names, n);
736 if (ControlPositionNegativeDegreesConfigurationV[MINUS_1_DEGREE].s )
737 {
738 moveDustCap(-1);
739 ControlPositionNegativeDegreesConfigurationV[MINUS_1_DEGREE].s = ISS_OFF;
740 }
741 else if (ControlPositionNegativeDegreesConfigurationV[MINUS_10_DEGREE].s == ISS_ON)
742 {
743 moveDustCap(-10);
744 ControlPositionNegativeDegreesConfigurationV[MINUS_10_DEGREE].s = ISS_OFF;
745 }
746 else if (ControlPositionNegativeDegreesConfigurationV[MINUS_50_DEGREE].s == ISS_ON)
747 {
748 moveDustCap(-50);
749 ControlPositionNegativeDegreesConfigurationV[MINUS_50_DEGREE].s = ISS_OFF;
750 }
751 IDSetSwitch(&ControlPositionNegativeDegreesConfigurationVP, nullptr);
752
753 LOG_INFO("Configuration button processing complete");
754 return true;
755 }
756
757 if (!strcmp(DefinePositionConfigurationVP.name, name))
758 {
759 IUUpdateSwitch(&DefinePositionConfigurationVP, states, names, n);
760 if (DefinePositionConfigurationV[SET_CURRENT_POSITION_OPEN].s == ISS_ON)
761 {
762 setCurrentPositionToOpenPosition();
763 DefinePositionConfigurationV[SET_CURRENT_POSITION_OPEN].s = ISS_OFF;
764 }
765 else if (DefinePositionConfigurationV[SET_CURRENT_POSITION_CLOSE].s == ISS_ON)
766 {
767 setCurrentPositionToClosedPosition();
768 DefinePositionConfigurationV[SET_CURRENT_POSITION_CLOSE].s = ISS_OFF;
769 }
770 IDSetSwitch(&DefinePositionConfigurationVP, nullptr);
771 LOG_INFO("Configuration button processing complete");
772 return true;
773 }
774 }
775
776 LOG_INFO("Configuration button processing complete");
777 return false;
778}
779
784
785bool WandererCover::hasWandererSentAnError(char* response)
786{
787 const std::string response_string( response );
788 size_t found = response_string.find("Error");
789 if (found != std::string::npos)
790 return true;
791
792 return false;
793}
794
795void WandererCover::displayConfigurationMessage()
796{
797 LOG_WARN(" - Once thesest steps are done, the dust cover will remember the park and unpark positions.");
798 LOG_WARN(" - Click on 'Set current position as close' to define the park position");
799 LOG_WARN(" - Use again the select list to move your cover panel in close position on the scope");
800 LOG_WARN(" - Click on 'Set current position as open' to define the unpark position");
801 LOG_WARN(" - Use the select controller to move your panel to the open position");
802 LOG_WARN("In order to do so, go to 'Dust cover configurtation' tab and do the following steps :");
803 LOG_WARN("Before first use, or when you change your setup, you need to configure Open and closed position first in 'Dust cover configurtation' tab.");
804}
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the initial connection to the devic...
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
Provides interface to implement remotely controlled dust cover.
bool isConnected() const
const char * getDeviceName() const
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
virtual void ISGetProperties(const char *dev)
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool ISSnoopDevice(XMLEle *root)
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
uint32_t getPollingPeriod() const
getPollingPeriod Return the polling period.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
bool isSimulation() const
void syncDriverInfo()
syncDriverInfo sends the current driver information to the client.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
INDI::PropertySwitch ParkCapSP
bool updateProperties()
updateProperties Defines or Delete properties based on default device connection status
void initProperties(const char *group, uint32_t capabilities=0)
Initialize dust cap properties. It is recommended to call this function within initProperties() of yo...
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process dust cap switch properties.
void initProperties(const char *group, uint32_t capabilities)
Initialize light box properties. It is recommended to call this function within initProperties() of y...
void ISGetProperties(const char *deviceName)
isGetLightBoxProperties Get light box properties
INDI::PropertyNumber LightIntensityNP
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process light box switch properties.
bool processText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process light box text properties.
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process light box number properties.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
Provides interface to implement controllable light box/switch device.
virtual bool SetLightBoxBrightness(uint16_t value) override
setBrightness Set light level. Must be implemented in the child class, if supported.
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 EnableLightBox(bool enable) override
EnableLightBox Turn on/off on a light box. Must be implemented in the child class.
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual IPState ParkCap() override
Park dust cap (close cover). Must be implemented by child.
virtual IPState UnParkCap() override
unPark dust cap (open cover). Must be implemented by child.
const char * getDefaultName() override
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
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_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_ATMOST1
Definition indiapi.h:172
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:653
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition indidevapi.c:376
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition indidevapi.c:416
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248
#define TAB_NAME_CONFIGURATION
#define CLOSE_COVER_COMMAND
#define HANDSHAKE_COMMAND
#define WANDERER_RESPONSE_SIZE
#define OPEN_COVER_COMMAND
#define SET_CURRENT_POSITION_TO_CLOSED_POSITION
#define COMMAND_WAITING_TIME
#define TURN_OFF_LIGHT_PANEL_COMMAND
#define SET_CURRENT_POSITION_TO_OPEN_POSITION