Instrument Neutral Distributed Interface INDI 2.2.5
gemini_flatpanel.cpp
Go to the documentation of this file.
1#include "config.h"
2#include "gemini_flatpanel.h"
3#include "indicom.h"
4#include <termios.h>
5#include <functional>
6#include <vector>
7#include <cstdlib>
8
9
10static std::unique_ptr<GeminiFlatpanel> mydriver(new GeminiFlatpanel());
11
16
18{
19 return "Gemini Flatpanel";
20}
21
23{
25
28
31
32
33 // Driver interface will be set dynamically in Handshake() based on device capabilities
35
36 // Initialize device selection property
37 DeviceTypeSP.fill(
39 "DEVICE_TYPE",
40 "Device Type",
42 IP_RW,
44 60,
46 );
47 DeviceTypeSP[DEVICE_AUTO].fill("AUTO", "Auto-detect", ISS_ON);
48 DeviceTypeSP[DEVICE_REV1].fill("REV1", "Revision 1", ISS_OFF);
49 DeviceTypeSP[DEVICE_REV2].fill("REV2", "Revision 2", ISS_OFF);
50 DeviceTypeSP[DEVICE_LITE].fill("LITE", "Lite", ISS_OFF);
51 DeviceTypeSP[DEVICE_PRO].fill("PRO", "Pro", ISS_OFF);
52 DeviceTypeSP.load();
53
55
56 serialConnection = new Connection::Serial(this);
58 serialConnection->registerHandshake([&]()
59 {
60 return Handshake();
61 });
62 registerConnection(serialConnection);
63
64 return true;
65}
66
68{
70
71 if (isConnected())
72 {
73 // Hide device selection when connected
74 deleteProperty(DeviceTypeSP);
75
76 defineProperty(StatusTP);
77 defineProperty(ConfigurationTP);
78
79 // Only register advanced features if supported by the current adapter
80 if (adapter && adapter->supportsBeep())
81 {
82 defineProperty(BeepSP);
83 }
84
85 // Only register dust cap movement controls if supported
86 if (adapter && adapter->supportsDustCap())
87 {
88 defineProperty(ConfigureSP);
89 defineProperty(ClosedPositionSP);
90 defineProperty(SetClosedSP);
91 defineProperty(OpenPositionSP);
92 defineProperty(SetOpenSP);
93 }
94 }
95 else
96 {
97 // Show device selection when disconnected
98 defineProperty(DeviceTypeSP);
99
100 deleteProperty(StatusTP);
101 deleteProperty(ConfigurationTP);
102
103 // Only delete properties that were defined
104 if (adapter && adapter->supportsBeep())
105 {
106 deleteProperty(BeepSP);
107 }
108
109 // Only delete dust cap properties that were defined
110 if (adapter && adapter->supportsDustCap())
111 {
112 deleteProperty(ConfigureSP);
113 deleteProperty(ClosedPositionSP);
114 deleteProperty(SetClosedSP);
115 deleteProperty(OpenPositionSP);
116 deleteProperty(SetOpenSP);
117 }
118 }
120
121 // Only update dust cap properties if the device supports dust cap functionality
122 if (adapter && adapter->supportsDustCap())
123 {
124 result = result && DI::updateProperties();
125 }
126
127 return result;
128}
129
130void GeminiFlatpanel::ISGetProperties(const char *deviceName)
131{
132 LI::ISGetProperties(deviceName);
134}
135
136bool GeminiFlatpanel::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
137{
138 if (dev != nullptr and strcmp(dev, getDeviceName()) == 0)
139 {
140 // Handle device type selection
141 if (DeviceTypeSP.isNameMatch(name))
142 {
143 DeviceTypeSP.update(states, names, n);
144 DeviceTypeSP.setState(IPS_OK);
145 DeviceTypeSP.apply();
146 saveConfig(DeviceTypeSP);
147 return true;
148 }
149
150 if (LI::processSwitch(dev, name, states, names, n))
151 {
152 return true;
153 }
154 if (adapter && adapter->supportsDustCap() && DI::processSwitch(dev, name, states, names, n))
155 {
156 return true;
157 }
158 }
159 return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
160}
161
162bool GeminiFlatpanel::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
163{
164 if (dev != nullptr and strcmp(dev, getDeviceName()) == 0)
165 {
166 if (LI::processNumber(dev, name, values, names, n))
167 {
168 return true;
169 }
170 }
171 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
172}
173
174bool GeminiFlatpanel::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
175{
176 if (dev != nullptr and strcmp(dev, getDeviceName()) == 0)
177 {
178 if (LI::processText(dev, name, texts, names, n))
179 {
180 return true;
181 }
182 }
183 return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
184}
185
191
193{
195
196 // Save device type selection
197 DeviceTypeSP.save(fp);
198
199 return LI::saveConfigItems(fp);
200}
201
203{
204 StatusTP.fill(
206 "STATUS",
207 "Status",
209 IP_RO,
210 0,
211 IPS_IDLE);
212 StatusTP[STATUS_COVER].fill("COVER", "Cover", nullptr);
213 StatusTP[STATUS_LIGHT].fill("LIGHT", "Light", nullptr);
214 StatusTP[STATUS_MOTOR].fill("MOTOR", "Motor", nullptr);
215
216 ConfigurationTP.fill(
218 "CONFIGURATION",
219 "Configuration",
221 IP_RO,
222 0,
223 IPS_IDLE);
224 ConfigurationTP[0].fill("CONFIGURATION", "Configuration", nullptr);
225
226 // Add beep control
227 BeepSP.fill(
229 "BEEP",
230 "Beep",
232 IP_RW,
234 0,
235 IPS_IDLE);
236 BeepSP[0].fill("BEEP_OFF", "Off", ISS_ON);
237 BeepSP[1].fill("BEEP_ON", "On", ISS_OFF);
238 BeepSP.onUpdate([this]()
239 {
240 onBeepChange();
241 });
242}
243
245{
246 bool enable = BeepSP[1].getState() == ISS_ON;
247
248 if (!adapter || !adapter->supportsBeep())
249 {
250 LOG_WARN("Beep functionality not supported by this device.");
251 BeepSP.setState(IPS_ALERT);
252 BeepSP.apply();
253 return;
254 }
255
256 if (setBeep(enable))
257 {
258 BeepSP.setState(IPS_OK);
259 }
260 else
261 {
262 BeepSP.setState(IPS_ALERT);
263 }
264 BeepSP.apply();
265}
266
268{
269 ConfigureSP.fill(
271 "CONFIGURE",
272 "Configure",
274 IP_RW,
276 0,
277 IPS_IDLE);
278 ConfigureSP[0].fill("CONFIGURE", "Configure", ISS_OFF);
279 ConfigureSP.onUpdate([this]
280 { startConfiguration(); });
281
282 ClosedPositionSP.fill(
284 "CLOSE_LIMIT",
285 "Close position",
287 IP_RW,
289 0,
290 IPS_IDLE);
291 ClosedPositionSP[MOVEMENT_LIMITS_45].fill("45", "-45", ISS_OFF);
292 ClosedPositionSP[MOVEMENT_LIMITS_10].fill("10", "-10", ISS_OFF);
293 ClosedPositionSP[MOVEMENT_LIMITS_01].fill("1", "-1", ISS_OFF);
294 ClosedPositionSP.onUpdate([this]
296
297 SetClosedSP.fill(
299 "SET_CLOSE_LIMIT",
300 "Set closed",
302 IP_RW,
304 0,
305 IPS_IDLE);
306 SetClosedSP[0].fill("SET_CLOSED", "Set closed", ISS_OFF);
307 SetClosedSP.onUpdate([this]
309
310 OpenPositionSP.fill(
312 "OPEN_LIMIT",
313 "Open position",
315 IP_RW,
317 0,
318 IPS_IDLE);
319 OpenPositionSP[MOVEMENT_LIMITS_45].fill("45", "45", ISS_OFF);
320 OpenPositionSP[MOVEMENT_LIMITS_10].fill("10", "10", ISS_OFF);
321 OpenPositionSP[MOVEMENT_LIMITS_01].fill("1", "1", ISS_OFF);
322 OpenPositionSP.onUpdate([this]
324
325 SetOpenSP.fill(
327 "SET_OPEN_LIMIT",
328 "Set open",
330 IP_RW,
332 0,
333 IPS_IDLE);
334 SetOpenSP[0].fill("SET_OPEN", "Set open", ISS_OFF);
335 SetOpenSP.onUpdate([this]
337}
338
340{
341 if (!isConnected() && !isSimulation())
342 {
343 return;
344 }
345
346 int coverStatus = 0, lightStatus = 0, motorStatus = 0, brightness = 0;
347 bool error = false;
348
349 // Use adapter for both real hardware and simulation
350 error |= !getStatus(&coverStatus, &lightStatus, &motorStatus);
351 error |= !getBrightness(&brightness);
352
353 if (error)
354 {
355 return;
356 }
357
358 // Reflect the currently applied mode in the unified 0-511 value shown to the user
359 // (see SetLightBoxBrightness() below) -- the device itself only ever reports the
360 // raw 0-255 value for whichever mode is currently active.
361 if (adapter && adapter->supportsBrightnessMode() && brightnessMode == GEMINI_BRIGHTNESS_MODE_HIGH)
362 {
363 brightness += GEMINI_MAX_BRIGHTNESS + 1;
364 }
365
366 if (updateBrightness(brightness))
367 {
369 }
370
371 bool statusUpdated = updateCoverStatus(coverStatus) ||
372 updateLightStatus(lightStatus) ||
373 updateMotorStatus(motorStatus);
374
375 if (statusUpdated)
376 {
377 StatusTP.apply();
378 }
379
380 const bool isPro = (adapter && adapter->getRevision() == GEMINI_REVISION_PRO);
381 if (motorStatus == GEMINI_MOTOR_STATUS_RUNNING && (coverStatus == GEMINI_COVER_STATUS_TIMED_OUT
382 || (!isPro && coverStatus == GEMINI_COVER_STATUS_MOVING)))
383 {
384 LOG_WARN("Motor running with unknown cover status.");
385 configStatus = GEMINI_CONFIG_NOTREADY;
386 updateConfigStatus();
387 }
388
389 if (configStatus == GEMINI_CONFIG_READY)
390 {
392 }
393}
394
395// Decodes a unified 0-511 value into a mode (Low/High) and a raw 0-255 value, and
396// pushes both to the device. The mode relay is only toggled when it actually differs
397// from what we last commanded, unless forceMode is set. Only called when the adapter
398// supports a brightness mode.
399bool GeminiFlatpanel::applyUnifiedBrightness(int unifiedValue, bool forceMode)
400{
402 int rawValue = mode == GEMINI_BRIGHTNESS_MODE_HIGH ? unifiedValue - (GEMINI_MAX_BRIGHTNESS + 1) : unifiedValue;
403
404 if ((forceMode || mode != brightnessMode) && !setBrightnessMode(mode))
405 {
406 return false;
407 }
408
409 if (!setBrightness(rawValue))
410 {
411 return false;
412 }
413
414 brightnessMode = mode;
415 return true;
416}
417
418// LightBoxInterface methods
420{
421 if (!validateOperation())
422 {
423 return false;
424 }
425
426 // Devices without a brightness mode (Rev1/Pro) keep the plain 0-255 range.
427 if (!adapter || !adapter->supportsBrightnessMode())
428 {
429 return setBrightness((int)value);
430 }
431
432 // Devices with a brightness mode (Rev2/Lite) get a unified 0-511 range instead of a
433 // separate BRIGHTNESS_MODE switch: 0-255 maps to Low, 256-511 to High. This previews
434 // the single-continuous-range behavior Gemini has said they plan to move the
435 // firmware to (indilib/indi#2487) using the mode switch the current firmware already
436 // has, entirely on the driver side.
437 return applyUnifiedBrightness(value, false);
438}
439
441{
442 if (!validateOperation())
443 {
444 return false;
445 }
446
447 // The firmware has no command to read back which mode is currently active, so our
448 // in-memory brightnessMode could have drifted from reality (e.g. a power cycle, or
449 // the mode having last been set from outside this driver). Turning the light on is
450 // the point where that would actually become visible, so unconditionally re-assert
451 // both the mode and the matching raw value we believe are active right before doing so.
452 if (enable && adapter && adapter->supportsBrightnessMode()
453 && !applyUnifiedBrightness(static_cast<int>(LightIntensityNP[0].getValue()), true))
454 {
455 return false;
456 }
457
458 return enable ? lightOn() : lightOff();
459}
460
461// DustCapInterface methods
463{
464 if (!validateOperation())
465 {
466 return IPS_ALERT;
467 }
470 if (closeCover())
471 {
472 return (adapter && adapter->getRevision() == GEMINI_REVISION_PRO) ? IPS_BUSY : IPS_OK;
473 }
474 return IPS_ALERT;
475}
476
478{
479 if (!validateOperation())
480 {
481 return IPS_ALERT;
482 }
485 if (openCover())
486 {
487 return (adapter && adapter->getRevision() == GEMINI_REVISION_PRO) ? IPS_BUSY : IPS_OK;
488 }
489 return IPS_ALERT;
490}
491
493{
494 if (adapter && adapter->getRevision() == GEMINI_REVISION_PRO)
495 {
496 return IPS_OK;
497 }
498
499 return IPS_ALERT;
500}
501
502// Handshake method
503bool GeminiFlatpanel::Handshake()
504{
505 if (isSimulation())
506 {
507 // Use simulation adapter (can simulate both Rev1 and Rev2 features)
508 adapter = std::make_unique<GeminiFlatpanelSimulationAdapter>(true); // true = Rev2 features
509 deviceRevision = adapter->getRevision();
510 commandTerminator = adapter->getCommandTerminator();
511
512 // Widen the brightness range to cover both modes -- see SetLightBoxBrightness().
513 if (adapter->supportsBrightnessMode())
514 {
515 LightIntensityNP[0].setMax(2 * GEMINI_MAX_BRIGHTNESS + 1);
516 }
517
518 // Set driver interface based on device capabilities
519 if (adapter && adapter->supportsDustCap())
520 {
523 }
524
525 // Get config status from adapter
526 int adapterConfigStatus;
527 if (adapter->getConfigStatus(&adapterConfigStatus))
528 {
529 configStatus = adapterConfigStatus;
530 updateConfigStatus();
531 }
532
533 LOGF_INFO("Connected successfully to simulated %s.", getDeviceName());
534 return true;
535 }
536
537 PortFD = serialConnection->getPortFD();
538
539 // Check if user has selected a specific device type
540 int selectedDeviceType = DeviceTypeSP.findOnSwitchIndex();
541
542 // List of adapter types to try in order
543 struct AdapterInfo
544 {
545 std::function<std::unique_ptr<GeminiFlatpanelAdapter>()> factory;
546 std::string name;
547 int deviceType;
548 };
549
550 std::vector<AdapterInfo> adapterTypes;
551
552 if (selectedDeviceType == DEVICE_AUTO)
553 {
554 // Auto-detect: try all adapters in order
555 adapterTypes =
556 {
557 {[]() { return std::make_unique<GeminiFlatpanelRev1Adapter>(); }, "Rev1", DEVICE_REV1},
558 {[]() { return std::make_unique<GeminiFlatpanelRev2Adapter>(); }, "Rev2", DEVICE_REV2},
559 {[]() { return std::make_unique<GeminiFlatpanelLiteAdapter>(); }, "Lite", DEVICE_LITE},
560 {[]() { return std::make_unique<GeminiFlatpanelProAdapter>(); }, "Pro", DEVICE_PRO}
561 };
562 }
563 else
564 {
565 // Use only the selected adapter type
566 switch (selectedDeviceType)
567 {
568 case DEVICE_REV1:
569 adapterTypes = {{[]() { return std::make_unique<GeminiFlatpanelRev1Adapter>(); }, "Rev1", DEVICE_REV1}};
570 break;
571 case DEVICE_REV2:
572 adapterTypes = {{[]() { return std::make_unique<GeminiFlatpanelRev2Adapter>(); }, "Rev2", DEVICE_REV2}};
573 break;
574 case DEVICE_LITE:
575 adapterTypes = {{[]() { return std::make_unique<GeminiFlatpanelLiteAdapter>(); }, "Lite", DEVICE_LITE}};
576 break;
577 case DEVICE_PRO:
578 adapterTypes = {{[]() { return std::make_unique<GeminiFlatpanelProAdapter>(); }, "Pro", DEVICE_PRO}};
579 break;
580 }
581 }
582
583 // Try each adapter type until one succeeds
584 bool connected = false;
585 for (const auto &adapterInfo : adapterTypes)
586 {
587 auto testAdapter = adapterInfo.factory();
588 testAdapter->setupCommunication(PortFD);
589
590 if (testAdapter->ping())
591 {
592 adapter = std::move(testAdapter);
593 deviceRevision = adapter->getRevision();
594 commandTerminator = adapter->getCommandTerminator();
595
596 // Log connection success with firmware version details
597 int firmwareVersion = 0;
598 if (adapter->getFirmwareVersion(&firmwareVersion))
599 {
600 LOGF_INFO("Connected successfully to %s.", getDeviceName());
601 LOGF_INFO("Device revision: %s (Firmware v%d)", adapterInfo.name.c_str(), firmwareVersion);
602 }
603 else
604 {
605 LOGF_INFO("Connected successfully to %s.", getDeviceName());
606 LOGF_INFO("Device revision: %s", adapterInfo.name.c_str());
607 }
608
609 connected = true;
610 break;
611 }
612 }
613
614 if (!connected)
615 {
616 LOG_ERROR("Handshake failed. Unable to communicate with the device.");
617 return false;
618 }
619
620 // Widen the brightness range to cover both modes -- see SetLightBoxBrightness().
621 if (adapter->supportsBrightnessMode())
622 {
623 LightIntensityNP[0].setMax(2 * GEMINI_MAX_BRIGHTNESS + 1);
624 }
625
626 // Set driver interface based on device capabilities
627 if (adapter && adapter->supportsDustCap())
628 {
631 }
632
633 // Check config status using adapter
634 int adapterConfigStatus;
635 if (adapter->getConfigStatus(&adapterConfigStatus))
636 {
637 configStatus = adapterConfigStatus;
638 updateConfigStatus();
639 return true;
640 }
641
642 return false;
643}
644
645// Note: Protocol-specific methods moved to adapter classes
646
647// Device command methods - now using adapter pattern
648bool GeminiFlatpanel::getBrightness(int *const brightness)
649{
650 return adapter ? adapter->getBrightness(brightness) : false;
651}
652
653bool GeminiFlatpanel::setBrightness(int value)
654{
655 return adapter ? adapter->setBrightness(value) : false;
656}
657
658bool GeminiFlatpanel::lightOn()
659{
660 return adapter ? adapter->lightOn() : false;
661}
662
663bool GeminiFlatpanel::lightOff()
664{
665 return adapter ? adapter->lightOff() : false;
666}
667
668bool GeminiFlatpanel::openCover()
669{
670 return adapter ? adapter->openCover() : false;
671}
672
673bool GeminiFlatpanel::closeCover()
674{
675 return adapter ? adapter->closeCover() : false;
676}
677
678bool GeminiFlatpanel::setBeep(bool enable)
679{
680 return adapter ? adapter->setBeep(enable) : false;
681}
682
683bool GeminiFlatpanel::setBrightnessMode(int mode)
684{
685 return adapter ? adapter->setBrightnessMode(mode) : false;
686}
687
688bool GeminiFlatpanel::getStatus(int *const coverStatus, int *const lightStatus, int *const motorStatus)
689{
690 return adapter ? adapter->getStatus(coverStatus, lightStatus, motorStatus) : false;
691}
692
693bool GeminiFlatpanel::move(uint16_t value, int direction)
694{
695 return adapter ? adapter->move(value, direction) : false;
696}
697
698bool GeminiFlatpanel::setClosePosition()
699{
700 return adapter ? adapter->setClosePosition() : false;
701}
702
703bool GeminiFlatpanel::setOpenPosition()
704{
705 return adapter ? adapter->setOpenPosition() : false;
706}
707
708// Status update and transitions
709bool GeminiFlatpanel::updateCoverStatus(char coverStatus)
710{
711 bool statusUpdated = false;
712
713 if (coverStatus != prevCoverStatus)
714 {
715 prevCoverStatus = coverStatus;
716 statusUpdated = true;
717
718 switch (coverStatus)
719 {
721 StatusTP[STATUS_COVER].setText("Moving");
725 break;
727 StatusTP[STATUS_COVER].setText("Closed");
729 {
733 LOG_INFO("Cover closed.");
735 }
736 break;
738 StatusTP[STATUS_COVER].setText("Open");
740 {
744 LOG_INFO("Cover open.");
746 }
747 break;
749 StatusTP[STATUS_COVER].setText("Timed Out");
752 LOG_ERROR("Cover operation timed out.");
754 break;
755 }
756 }
757
758 return statusUpdated;
759}
760
761bool GeminiFlatpanel::updateLightStatus(char lightStatus)
762{
763 bool statusUpdated = false;
764
765 if (lightStatus != prevLightStatus)
766 {
767 prevLightStatus = lightStatus;
768 statusUpdated = true;
769
770 switch (lightStatus)
771 {
773 StatusTP[STATUS_LIGHT].setText("Off");
774 if (LightSP[0].getState() == ISS_ON)
775 {
776 LightSP.reset();
778 LightSP.apply();
779 }
780 break;
782 StatusTP[STATUS_LIGHT].setText("On");
783 if (LightSP[1].getState() == ISS_ON)
784 {
785 LightSP.reset();
787 LightSP.apply();
788 }
789 break;
790 }
791 }
792
793 return statusUpdated;
794}
795
796bool GeminiFlatpanel::updateMotorStatus(char motorStatus)
797{
798 bool statusUpdated = false;
799
800 if (motorStatus != prevMotorStatus)
801 {
802 prevMotorStatus = motorStatus;
803 statusUpdated = true;
804
805 switch (motorStatus)
806 {
808 StatusTP[STATUS_MOTOR].setText("Stopped");
809 break;
811 StatusTP[STATUS_MOTOR].setText("Running");
812 break;
813 }
814 }
815
816 return statusUpdated;
817}
818
819bool GeminiFlatpanel::updateBrightness(int brightness)
820{
821 bool statusUpdated = false;
822 if (brightness != prevBrightness)
823 {
824 prevBrightness = brightness;
825 statusUpdated = true;
826
827 LightIntensityNP[0].setValue(brightness);
828 }
829
830 return statusUpdated;
831}
832
833void GeminiFlatpanel::updateConfigStatus()
834{
835 const char *configStatusText = nullptr;
836 switch (configStatus)
837 {
839 configStatusText = "Not ready";
840 break;
842 configStatusText = "Ready";
843 break;
845 configStatusText = "Open";
846 break;
848 configStatusText = "Closed";
849 break;
850 }
851 ConfigurationTP[0].setText(configStatusText);
852 ConfigurationTP.apply();
853}
854
859
861{
862 int adapterConfigStatus;
863 if (adapter && adapter->getConfigStatus(&adapterConfigStatus))
864 {
865 configStatus = adapterConfigStatus;
866 if (configStatus == GEMINI_CONFIG_READY)
867 {
868 LOG_INFO("Configuration completed successfully.");
870 }
871 else
872 {
873 LOGF_WARN("Invalid configuration status. Please restart configuration in %s tab.", MOTION_TAB);
874 configStatus = GEMINI_CONFIG_NOTREADY;
875 }
876 }
877 else
878 {
879 LOG_WARN("Failed to get configuration status.");
880 configStatus = GEMINI_CONFIG_NOTREADY;
881 }
882
883 ConfigureSP.reset();
884 ConfigureSP[0].setState(ISS_OFF);
885 ConfigureSP.setState(IPS_IDLE);
886 ConfigureSP.apply();
887}
888
890{
891 if (configStatus != GEMINI_CONFIG_READY)
892 {
893 LOG_WARN("Flatpanel not ready. Click the configure button to start configuration.");
894 return false;
895 }
896 return true;
897}
898
900{
901 if (configStatus == GEMINI_CONFIG_NOTREADY || configStatus == GEMINI_CONFIG_READY)
902 {
903 LOG_WARN("Click the configure button to start configuration.");
904 return false;
905 }
906 if (configStatus == GEMINI_CONFIG_CLOSED && direction != GEMINI_DIRECTION_CLOSE)
907 {
908 LOG_WARN("Please set the closed configuration using the close position controls.");
909 return false;
910 }
911 if (configStatus == GEMINI_CONFIG_OPEN && direction != GEMINI_DIRECTION_OPEN)
912 {
913 LOG_WARN("Please set the open configuration using the open position controls.");
914 return false;
915 }
916 return true;
917}
918
919void GeminiFlatpanel::cleanupSwitch(INDI::PropertySwitch &currentSwitch, int switchIndex)
920{
921 currentSwitch[switchIndex].setState(ISS_OFF);
922 currentSwitch.setState(IPS_IDLE);
923 currentSwitch.apply();
924}
925
926void GeminiFlatpanel::onMove(int direction)
927{
928 // Check if dust cap functionality is supported
929 if (!adapter || !adapter->supportsDustCap())
930 {
931 LOG_WARN("Dust cap movement not supported by this device.");
932 return;
933 }
934
935 // Determine the switch based solely on the direction.
936 auto &currentSwitch = (direction == GEMINI_DIRECTION_CLOSE) ? ClosedPositionSP : OpenPositionSP;
937 int switchIndex = currentSwitch.findOnSwitchIndex();
938
939 if (!validateCalibrationOperation(direction))
940 {
941 cleanupSwitch(currentSwitch, switchIndex);
942 return;
943 }
944
945 // Perform movement based on the current switch state.
946 int steps = 0;
947 switch (currentSwitch.findOnSwitchIndex())
948 {
949 case MOVEMENT_LIMITS_45:
950 steps = 45;
951 break;
952 case MOVEMENT_LIMITS_10:
953 steps = 10;
954 break;
955 case MOVEMENT_LIMITS_01:
956 steps = 1;
957 break;
958 }
959
960 // Use adapter for both real hardware and simulation
961 move(steps, direction);
962
963 cleanupSwitch(currentSwitch, switchIndex);
964}
965
967{
968 // Check if dust cap functionality is supported
969 if (!adapter || !adapter->supportsDustCap())
970 {
971 LOG_WARN("Dust cap position setting not supported by this device.");
972 return;
973 }
974
975 auto currentSwitch = (direction == GEMINI_DIRECTION_CLOSE) ? SetClosedSP : SetOpenSP;
976 auto switchIndex = currentSwitch.findOnSwitchIndex();
977
978 if (!validateCalibrationOperation(direction))
979 {
980 cleanupSwitch(currentSwitch, switchIndex);
981 return;
982 }
983
984 // Set the position based on the given direction.
985 switch (direction)
986 {
988 LOG_INFO("Close position set.");
989 setClosePosition();
990 configStatus = GEMINI_CONFIG_OPEN;
991 break;
993 LOG_INFO("Setting open position.");
994 setOpenPosition();
996 break;
997 }
998
999 cleanupSwitch(currentSwitch, switchIndex);
1000}
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.
virtual IPState UnParkCap() override
unPark dust cap (open cover). Must be implemented by child.
bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual const char * getDefaultName() override
bool EnableLightBox(bool enable) override
EnableLightBox Turn on/off on a light box. Must be implemented in the child class.
bool validateCalibrationOperation(int direction)
virtual IPState AbortCap() override
Abort motion. Must be implemented by child.
bool SetLightBoxBrightness(uint16_t value) override
setBrightness Set light level. Must be implemented in the child class, if supported.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
void onSetPosition(int direction)
void ISGetProperties(const char *deviceName) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
void onMove(int direction)
bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual IPState ParkCap() override
Park dust cap (close cover). Must be implemented by child.
void cleanupSwitch(INDI::PropertySwitch &currentSwitch, int switchIndex)
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
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 saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
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.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
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.
uint32_t getDriverInterface() const
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
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 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)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
void onUpdate(const std::function< void()> &callback)
Provides interface to implement controllable light box/switch device.
const char * CONNECTION_TAB
CONNECTION_TAB Where all device connection settings (serial, usb, ethernet) are defined and controlle...
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
@ GEMINI_CONFIG_NOTREADY
@ GEMINI_COVER_STATUS_TIMED_OUT
@ GEMINI_COVER_STATUS_CLOSED
@ GEMINI_COVER_STATUS_OPEN
@ GEMINI_COVER_STATUS_MOVING
@ GEMINI_DIRECTION_OPEN
@ GEMINI_DIRECTION_CLOSE
@ GEMINI_BRIGHTNESS_MODE_HIGH
@ GEMINI_BRIGHTNESS_MODE_LOW
@ GEMINI_MOTOR_STATUS_STOPPED
@ GEMINI_MOTOR_STATUS_RUNNING
@ GEMINI_LIGHT_STATUS_ON
@ GEMINI_LIGHT_STATUS_OFF
#define GEMINI_MAX_BRIGHTNESS
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
@ ISR_ATMOST1
Definition indiapi.h:172
Implementations for common driver routines.
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOG_INFO(txt)
Definition indilogger.h:74