1 /*******************************************************************************
2 Copyright(c) 2011 Jasem Mutlaq. All rights reserved.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17 *******************************************************************************/
18
19 #include "defaultdevice.h"
20 #include "indicom.h"
21 #include "indistandardproperty.h"
22 #include "connectionplugins/connectionserial.h"
23
24 #include <cstdlib>
25 #include <cstring>
26 #include <assert.h>
27
28 const char *COMMUNICATION_TAB = "Communication";
29 const char *MAIN_CONTROL_TAB = "Main Control";
30 const char *CONNECTION_TAB = "Connection";
31 const char *MOTION_TAB = "Motion Control";
32 const char *DATETIME_TAB = "Date/Time";
33 const char *SITE_TAB = "Site Management";
34 const char *OPTIONS_TAB = "Options";
35 const char *FILTER_TAB = "Filter Wheel";
36 const char *FOCUS_TAB = "Focuser";
37 const char *GUIDE_TAB = "Guide";
38 const char *ALIGNMENT_TAB = "Alignment";
39 const char *INFO_TAB = "General Info";
40
timerfunc(void * t)41 void timerfunc(void *t)
42 {
43 //fprintf(stderr,"Got a timer hit with %x\n",t);
44 INDI::DefaultDevice *devPtr = static_cast<INDI::DefaultDevice *>(t);
45 if (devPtr != nullptr)
46 {
47 // this was for my device
48 // but we dont have a way of telling
49 // WHICH timer was hit :(
50 devPtr->TimerHit();
51 }
52 return;
53 }
54
55 namespace INDI
56 {
57
DefaultDevice()58 DefaultDevice::DefaultDevice()
59 {
60 pDebug = false;
61 pSimulation = false;
62 isInit = false;
63
64 majorVersion = 1;
65 minorVersion = 0;
66 interfaceDescriptor = GENERAL_INTERFACE;
67 memset(&ConnectionModeSP, 0, sizeof(ConnectionModeSP));
68 }
69
~DefaultDevice()70 DefaultDevice::~DefaultDevice()
71 {
72 }
73
loadConfig(bool silent,const char * property)74 bool DefaultDevice::loadConfig(bool silent, const char *property)
75 {
76 char errmsg[MAXRBUF];
77 bool pResult = IUReadConfig(nullptr, deviceID, property, silent ? 1 : 0, errmsg) == 0 ? true : false;
78
79 if (!silent)
80 {
81 if (pResult)
82 {
83 LOG_DEBUG("Configuration successfully loaded.");
84 }
85 else
86 LOG_INFO("No previous configuration found. To save driver configuration, click Save Configuration in Options tab");
87 }
88
89 IUSaveDefaultConfig(nullptr, nullptr, deviceID);
90
91 return pResult;
92 }
93
saveConfigItems(FILE * fp)94 bool DefaultDevice::saveConfigItems(FILE *fp)
95 {
96 IUSaveConfigSwitch(fp, &DebugSP);
97 IUSaveConfigNumber(fp, &PollPeriodNP);
98 if (ConnectionModeS != nullptr)
99 IUSaveConfigSwitch(fp, &ConnectionModeSP);
100
101 if (activeConnection)
102 activeConnection->saveConfigItems(fp);
103
104 return INDI::Logger::saveConfigItems(fp);
105 }
106
saveAllConfigItems(FILE * fp)107 bool DefaultDevice::saveAllConfigItems(FILE *fp)
108 {
109 std::vector<INDI::Property *>::iterator orderi;
110
111 ISwitchVectorProperty *svp = nullptr;
112 INumberVectorProperty *nvp = nullptr;
113 ITextVectorProperty *tvp = nullptr;
114 IBLOBVectorProperty *bvp = nullptr;
115
116 for (orderi = pAll.begin(); orderi != pAll.end(); ++orderi)
117 {
118 INDI_PROPERTY_TYPE pType = (*orderi)->getType();
119 void *pPtr = (*orderi)->getProperty();
120
121 switch (pType)
122 {
123 case INDI_NUMBER:
124 nvp = static_cast<INumberVectorProperty *>(pPtr);
125 //IDLog("Trying to save config for number %s\n", nvp->name);
126 IUSaveConfigNumber(fp, nvp);
127 break;
128 case INDI_TEXT:
129 tvp = static_cast<ITextVectorProperty *>(pPtr);
130 IUSaveConfigText(fp, tvp);
131 break;
132 case INDI_SWITCH:
133 svp = static_cast<ISwitchVectorProperty *>(pPtr);
134 /* Never save CONNECTION property. Don't save switches with no switches on if the rule is one of many */
135 if (!strcmp(svp->name, INDI::SP::CONNECTION) || (svp->r == ISR_1OFMANY && !IUFindOnSwitch(svp)))
136 continue;
137 IUSaveConfigSwitch(fp, svp);
138 break;
139 case INDI_BLOB:
140 bvp = static_cast<IBLOBVectorProperty *>(pPtr);
141 IUSaveConfigBLOB(fp, bvp);
142 break;
143 case INDI_LIGHT:
144 case INDI_UNKNOWN:
145 break;
146 }
147 }
148 return true;
149 }
150
purgeConfig()151 bool DefaultDevice::purgeConfig()
152 {
153 char errmsg[MAXRBUF];
154 if (IUPurgeConfig(nullptr, deviceID, errmsg) == -1)
155 {
156 LOGF_WARN("%s", errmsg);
157 return false;
158 }
159
160 LOG_INFO("Configuration file successfully purged.");
161 return true;
162 }
163
saveConfig(bool silent,const char * property)164 bool DefaultDevice::saveConfig(bool silent, const char *property)
165 {
166 //std::vector<orderPtr>::iterator orderi;
167 char errmsg[MAXRBUF];
168
169 FILE *fp = nullptr;
170
171 if (property == nullptr)
172 {
173 fp = IUGetConfigFP(nullptr, deviceID, "w", errmsg);
174
175 if (fp == nullptr)
176 {
177 if (!silent)
178 LOGF_WARN("Failed to save configuration. %s", errmsg);
179 return false;
180 }
181
182 IUSaveConfigTag(fp, 0, getDeviceName(), silent ? 1 : 0);
183
184 saveConfigItems(fp);
185
186 IUSaveConfigTag(fp, 1, getDeviceName(), silent ? 1 : 0);
187
188 fclose(fp);
189
190 IUSaveDefaultConfig(nullptr, nullptr, deviceID);
191
192 LOG_DEBUG("Configuration successfully saved.");
193 }
194 else
195 {
196 fp = IUGetConfigFP(nullptr, deviceID, "r", errmsg);
197
198 if (fp == nullptr)
199 {
200 //if (!silent)
201 // LOGF_ERROR("Error saving configuration. %s", errmsg);
202 //return false;
203 // If we don't have an existing file pointer, save all properties.
204 return saveConfig(silent);
205 }
206
207 LilXML *lp = newLilXML();
208 XMLEle *root = readXMLFile(fp, lp, errmsg);
209
210 fclose(fp);
211 delLilXML(lp);
212
213 if (root == nullptr)
214 return false;
215
216 XMLEle *ep = nullptr;
217 bool propertySaved = false;
218
219 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
220 {
221 const char *elemName = findXMLAttValu(ep, "name");
222 const char *tagName = tagXMLEle(ep);
223
224 if (strcmp(elemName, property))
225 continue;
226
227 if (!strcmp(tagName, "newSwitchVector"))
228 {
229 ISwitchVectorProperty *svp = getSwitch(elemName);
230 if (svp == nullptr)
231 {
232 delXMLEle(root);
233 return false;
234 }
235
236 XMLEle *sw = nullptr;
237 for (sw = nextXMLEle(ep, 1); sw != nullptr; sw = nextXMLEle(ep, 0))
238 {
239 ISwitch *oneSwitch = IUFindSwitch(svp, findXMLAttValu(sw, "name"));
240 if (oneSwitch == nullptr)
241 {
242 delXMLEle(root);
243 return false;
244 }
245 char formatString[MAXRBUF];
246 snprintf(formatString, MAXRBUF, " %s\n", sstateStr(oneSwitch->s));
247 editXMLEle(sw, formatString);
248 }
249
250 propertySaved = true;
251 break;
252 }
253 else if (!strcmp(tagName, "newNumberVector"))
254 {
255 INumberVectorProperty *nvp = getNumber(elemName);
256 if (nvp == nullptr)
257 {
258 delXMLEle(root);
259 return false;
260 }
261
262 XMLEle *np = nullptr;
263 for (np = nextXMLEle(ep, 1); np != nullptr; np = nextXMLEle(ep, 0))
264 {
265 INumber *oneNumber = IUFindNumber(nvp, findXMLAttValu(np, "name"));
266 if (oneNumber == nullptr)
267 return false;
268
269 char formatString[MAXRBUF];
270 snprintf(formatString, MAXRBUF, " %.20g\n", oneNumber->value);
271 editXMLEle(np, formatString);
272 }
273
274 propertySaved = true;
275 break;
276 }
277 else if (!strcmp(tagName, "newTextVector"))
278 {
279 ITextVectorProperty *tvp = getText(elemName);
280 if (tvp == nullptr)
281 {
282 delXMLEle(root);
283 return false;
284 }
285
286 XMLEle *tp = nullptr;
287 for (tp = nextXMLEle(ep, 1); tp != nullptr; tp = nextXMLEle(ep, 0))
288 {
289 IText *oneText = IUFindText(tvp, findXMLAttValu(tp, "name"));
290 if (oneText == nullptr)
291 return false;
292
293 char formatString[MAXRBUF];
294 snprintf(formatString, MAXRBUF, " %s\n", oneText->text ? oneText->text : "");
295 editXMLEle(tp, formatString);
296 }
297
298 propertySaved = true;
299 break;
300 }
301 }
302
303 if (propertySaved)
304 {
305 fp = IUGetConfigFP(nullptr, deviceID, "w", errmsg);
306 prXMLEle(fp, root, 0);
307 fclose(fp);
308 delXMLEle(root);
309 LOGF_DEBUG("Configuration successfully saved for %s.", property);
310 return true;
311 }
312 else
313 {
314 delXMLEle(root);
315 // If property does not exist, save the whole thing
316 return saveConfig(silent);
317 }
318 }
319
320 return true;
321 }
322
loadDefaultConfig()323 bool DefaultDevice::loadDefaultConfig()
324 {
325 char configDefaultFileName[MAXRBUF];
326 char errmsg[MAXRBUF];
327 bool pResult = false;
328
329 if (getenv("INDICONFIG"))
330 snprintf(configDefaultFileName, MAXRBUF, "%s.default", getenv("INDICONFIG"));
331 else
332 snprintf(configDefaultFileName, MAXRBUF, "%s/.indi/%s_config.xml.default", getenv("HOME"), deviceID);
333
334 LOGF_DEBUG("Requesting to load default config with: %s", configDefaultFileName);
335
336 pResult = IUReadConfig(configDefaultFileName, deviceID, nullptr, 0, errmsg) == 0 ? true : false;
337
338 if (pResult)
339 LOG_INFO("Default configuration loaded.");
340 else
341 LOGF_INFO("Error loading default configuraiton. %s", errmsg);
342
343 return pResult;
344 }
345
ISNewSwitch(const char * dev,const char * name,ISState * states,char * names[],int n)346 bool DefaultDevice::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
347 {
348 // ignore if not ours //
349 if (strcmp(dev, deviceID))
350 return false;
351
352 ISwitchVectorProperty *svp = getSwitch(name);
353
354 if (!svp)
355 return false;
356
357 ////////////////////////////////////////////////////
358 // Connection
359 ////////////////////////////////////////////////////
360 if (!strcmp(svp->name, ConnectionSP.name))
361 {
362 bool rc = false;
363
364 for (int i = 0; i < n; i++)
365 {
366 if (!strcmp(names[i], "CONNECT") && (states[i] == ISS_ON))
367 {
368 // If disconnected, try to connect.
369 if (isConnected() == false)
370 {
371 rc = Connect();
372
373 if (rc)
374 {
375 // Connection is successful, set it to OK and updateProperties.
376 setConnected(true, IPS_OK);
377 updateProperties();
378 }
379 else
380 setConnected(false, IPS_ALERT);
381 }
382 else
383 // Already connected, tell client we're connected already.
384 setConnected(true);
385 }
386 else if (!strcmp(names[i], "DISCONNECT") && (states[i] == ISS_ON))
387 {
388 // If connected, try to disconnect.
389 if (isConnected() == true)
390 {
391 rc = Disconnect();
392 // Disconnection is successful, set it IDLE and updateProperties.
393 if (rc)
394 {
395 setConnected(false, IPS_IDLE);
396 updateProperties();
397 }
398 else
399 setConnected(true, IPS_ALERT);
400 }
401 // Already disconnected, tell client we're disconnected already.
402 else
403 setConnected(false, IPS_IDLE);
404 }
405 }
406
407 return true;
408 }
409
410 ////////////////////////////////////////////////////
411 // Connection Mode
412 ////////////////////////////////////////////////////
413 if (!strcmp(name, ConnectionModeSP.name))
414 {
415 IUUpdateSwitch(&ConnectionModeSP, states, names, n);
416
417 int activeConnectionIndex = IUFindOnSwitchIndex(&ConnectionModeSP);
418
419 if (activeConnectionIndex >= 0 && activeConnectionIndex < static_cast<int>(connections.size()))
420 {
421 activeConnection = connections[activeConnectionIndex];
422 activeConnection->Activated();
423
424 for (Connection::Interface *oneConnection : connections)
425 {
426 if (oneConnection == activeConnection)
427 continue;
428
429 oneConnection->Deactivated();
430 }
431
432 ConnectionModeSP.s = IPS_OK;
433 }
434 else
435 ConnectionModeSP.s = IPS_ALERT;
436
437 IDSetSwitch(&ConnectionModeSP, nullptr);
438
439 return true;
440 }
441
442 ////////////////////////////////////////////////////
443 // Debug
444 ////////////////////////////////////////////////////
445 if (!strcmp(svp->name, "DEBUG"))
446 {
447 IUUpdateSwitch(svp, states, names, n);
448 ISwitch *sp = IUFindOnSwitch(svp);
449
450 assert(sp != nullptr);
451
452 if (!strcmp(sp->name, "ENABLE"))
453 setDebug(true);
454 else
455 setDebug(false);
456
457 return true;
458 }
459
460 ////////////////////////////////////////////////////
461 // Simulation
462 ////////////////////////////////////////////////////
463 if (!strcmp(svp->name, "SIMULATION"))
464 {
465 IUUpdateSwitch(svp, states, names, n);
466 ISwitch *sp = IUFindOnSwitch(svp);
467
468 assert(sp != nullptr);
469
470 if (!strcmp(sp->name, "ENABLE"))
471 setSimulation(true);
472 else
473 setSimulation(false);
474 return true;
475 }
476
477 ////////////////////////////////////////////////////
478 // Configuration
479 ////////////////////////////////////////////////////
480 if (!strcmp(svp->name, "CONFIG_PROCESS"))
481 {
482 IUUpdateSwitch(svp, states, names, n);
483 ISwitch *sp = IUFindOnSwitch(svp);
484 IUResetSwitch(svp);
485 bool pResult = false;
486
487 // Not suppose to happen (all switches off) but let's handle it anyway
488 if (sp == nullptr)
489 {
490 svp->s = IPS_IDLE;
491 IDSetSwitch(svp, nullptr);
492 return true;
493 }
494
495 if (!strcmp(sp->name, "CONFIG_LOAD"))
496 pResult = loadConfig();
497 else if (!strcmp(sp->name, "CONFIG_SAVE"))
498 pResult = saveConfig();
499 else if (!strcmp(sp->name, "CONFIG_DEFAULT"))
500 pResult = loadDefaultConfig();
501 else if (!strcmp(sp->name, "CONFIG_PURGE"))
502 pResult = purgeConfig();
503
504 if (pResult)
505 svp->s = IPS_OK;
506 else
507 svp->s = IPS_ALERT;
508
509 IDSetSwitch(svp, nullptr);
510 return true;
511 }
512
513 ////////////////////////////////////////////////////
514 // Debugging and Logging Levels
515 ////////////////////////////////////////////////////
516 if (!strcmp(svp->name, "DEBUG_LEVEL") || !strcmp(svp->name, "LOGGING_LEVEL") || !strcmp(svp->name, "LOG_OUTPUT"))
517 {
518 bool rc = Logger::ISNewSwitch(dev, name, states, names, n);
519
520 if (!strcmp(svp->name, "LOG_OUTPUT"))
521 {
522 ISwitch *sw = IUFindSwitch(svp, "FILE_DEBUG");
523 if (sw && sw->s == ISS_ON)
524 DEBUGF(Logger::DBG_SESSION, "Session log file %s", Logger::getLogFile().c_str());
525 }
526
527 return rc;
528 }
529
530 bool rc = false;
531 for (Connection::Interface *oneConnection : connections)
532 rc |= oneConnection->ISNewSwitch(dev, name, states, names, n);
533
534 return rc;
535 }
536
ISNewNumber(const char * dev,const char * name,double values[],char * names[],int n)537 bool DefaultDevice::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
538 {
539 ////////////////////////////////////////////////////
540 // Polling Period
541 ////////////////////////////////////////////////////
542 if (!strcmp(name, PollPeriodNP.name))
543 {
544 IUUpdateNumber(&PollPeriodNP, values, names, n);
545 PollPeriodNP.s = IPS_OK;
546 POLLMS = static_cast<uint32_t>(PollPeriodN[0].value);
547 IDSetNumber(&PollPeriodNP, nullptr);
548 return true;
549 }
550
551 for (Connection::Interface *oneConnection : connections)
552 oneConnection->ISNewNumber(dev, name, values, names, n);
553
554 return false;
555 }
556
ISNewText(const char * dev,const char * name,char * texts[],char * names[],int n)557 bool DefaultDevice::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
558 {
559 for (Connection::Interface *oneConnection : connections)
560 oneConnection->ISNewText(dev, name, texts, names, n);
561
562 return false;
563 }
564
ISNewBLOB(const char * dev,const char * name,int sizes[],int blobsizes[],char * blobs[],char * formats[],char * names[],int n)565 bool DefaultDevice::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
566 char *formats[], char *names[], int n)
567 {
568 for (Connection::Interface *oneConnection : connections)
569 oneConnection->ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
570
571 return false;
572 }
573
ISSnoopDevice(XMLEle * root)574 bool DefaultDevice::ISSnoopDevice(XMLEle *root)
575 {
576 INDI_UNUSED(root);
577 return false;
578 }
579
addDebugControl()580 void DefaultDevice::addDebugControl()
581 {
582 registerProperty(&DebugSP, INDI_SWITCH);
583 pDebug = false;
584 }
585
addSimulationControl()586 void DefaultDevice::addSimulationControl()
587 {
588 registerProperty(&SimulationSP, INDI_SWITCH);
589 pSimulation = false;
590 }
591
addConfigurationControl()592 void DefaultDevice::addConfigurationControl()
593 {
594 registerProperty(&ConfigProcessSP, INDI_SWITCH);
595 }
596
addPollPeriodControl()597 void DefaultDevice::addPollPeriodControl()
598 {
599 registerProperty(&PollPeriodNP, INDI_NUMBER);
600 }
601
addAuxControls()602 void DefaultDevice::addAuxControls()
603 {
604 addDebugControl();
605 addSimulationControl();
606 addConfigurationControl();
607 addPollPeriodControl();
608 }
609
setDebug(bool enable)610 void DefaultDevice::setDebug(bool enable)
611 {
612 if (pDebug == enable)
613 {
614 DebugSP.s = IPS_OK;
615 IDSetSwitch(&DebugSP, nullptr);
616 return;
617 }
618
619 IUResetSwitch(&DebugSP);
620
621 if (enable)
622 {
623 ISwitch *sp = IUFindSwitch(&DebugSP, "ENABLE");
624 if (sp)
625 {
626 sp->s = ISS_ON;
627 LOG_INFO("Debug is enabled.");
628 }
629 }
630 else
631 {
632 ISwitch *sp = IUFindSwitch(&DebugSP, "DISABLE");
633 if (sp)
634 {
635 sp->s = ISS_ON;
636 LOG_INFO("Debug is disabled.");
637 }
638 }
639
640 pDebug = enable;
641
642 // Inform logger
643 if (Logger::updateProperties(enable) == false)
644 DEBUG(Logger::DBG_WARNING, "setLogDebug: Logger error");
645
646 debugTriggered(enable);
647 DebugSP.s = IPS_OK;
648 IDSetSwitch(&DebugSP, nullptr);
649 }
650
setSimulation(bool enable)651 void DefaultDevice::setSimulation(bool enable)
652 {
653 if (pSimulation == enable)
654 {
655 SimulationSP.s = IPS_OK;
656 IDSetSwitch(&SimulationSP, nullptr);
657 return;
658 }
659
660 IUResetSwitch(&SimulationSP);
661
662 if (enable)
663 {
664 ISwitch *sp = IUFindSwitch(&SimulationSP, "ENABLE");
665 if (sp)
666 {
667 LOG_INFO("Simulation is enabled.");
668 sp->s = ISS_ON;
669 }
670 }
671 else
672 {
673 ISwitch *sp = IUFindSwitch(&SimulationSP, "DISABLE");
674 if (sp)
675 {
676 sp->s = ISS_ON;
677 LOG_INFO("Simulation is disabled.");
678 }
679 }
680
681 pSimulation = enable;
682 simulationTriggered(enable);
683 SimulationSP.s = IPS_OK;
684 IDSetSwitch(&SimulationSP, nullptr);
685 }
686
isDebug()687 bool DefaultDevice::isDebug()
688 {
689 return pDebug;
690 }
691
isSimulation()692 bool DefaultDevice::isSimulation()
693 {
694 return pSimulation;
695 }
696
debugTriggered(bool enable)697 void DefaultDevice::debugTriggered(bool enable)
698 {
699 INDI_UNUSED(enable);
700 }
701
simulationTriggered(bool enable)702 void DefaultDevice::simulationTriggered(bool enable)
703 {
704 INDI_UNUSED(enable);
705 }
706
ISGetProperties(const char * dev)707 void DefaultDevice::ISGetProperties(const char *dev)
708 {
709 if (isInit == false)
710 {
711 if (dev != nullptr)
712 setDeviceName(dev);
713 else if (*getDeviceName() == '\0')
714 {
715 char *envDev = getenv("INDIDEV");
716 if (envDev != nullptr)
717 setDeviceName(envDev);
718 else
719 setDeviceName(getDefaultName());
720 }
721
722 strncpy(ConnectionSP.device, getDeviceName(), MAXINDIDEVICE);
723 initProperties();
724 addConfigurationControl();
725
726 // If we have no connections, move Driver Info to General Info tab
727 if (connections.size() == 0)
728 strncpy(DriverInfoTP.group, INFO_TAB, MAXINDINAME);
729 }
730
731 for (INDI::Property *oneProperty : pAll)
732 {
733 INDI_PROPERTY_TYPE pType = oneProperty->getType();
734 void *pPtr = oneProperty->getProperty();
735
736 if (defineDynamicProperties == false && oneProperty->isDynamic())
737 continue;
738
739 switch (pType)
740 {
741 case INDI_NUMBER:
742 IDDefNumber(static_cast<INumberVectorProperty *>(pPtr), nullptr);
743 break;
744 case INDI_TEXT:
745 IDDefText(static_cast<ITextVectorProperty *>(pPtr), nullptr);
746 break;
747 case INDI_SWITCH:
748 IDDefSwitch(static_cast<ISwitchVectorProperty *>(pPtr), nullptr);
749 break;
750 case INDI_LIGHT:
751 IDDefLight(static_cast<ILightVectorProperty *>(pPtr), nullptr);
752 break;
753 case INDI_BLOB:
754 IDDefBLOB(static_cast<IBLOBVectorProperty *>(pPtr), nullptr);
755 break;
756 case INDI_UNKNOWN:
757 break;
758 }
759 }
760
761 // Remember debug & logging settings
762 if (isInit == false)
763 {
764 loadConfig(true, "DEBUG");
765 loadConfig(true, "DEBUG_LEVEL");
766 loadConfig(true, "LOGGING_LEVEL");
767 loadConfig(true, "POLLING_PERIOD");
768 loadConfig(true, "LOG_OUTPUT");
769 }
770
771 if (ConnectionModeS == nullptr)
772 {
773 if (connections.size() > 0)
774 {
775 ConnectionModeS = static_cast<ISwitch *>(malloc(connections.size() * sizeof(ISwitch)));
776 ISwitch *sp = ConnectionModeS;
777 for (Connection::Interface *oneConnection : connections)
778 {
779 IUFillSwitch(sp++, oneConnection->name().c_str(), oneConnection->label().c_str(), ISS_OFF);
780 }
781
782 activeConnection = connections[0];
783 ConnectionModeS[0].s = ISS_ON;
784 IUFillSwitchVector(&ConnectionModeSP, ConnectionModeS, connections.size(), getDeviceName(),
785 "CONNECTION_MODE", "Connection Mode", CONNECTION_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
786
787 defineSwitch(&ConnectionModeSP);
788 activeConnection->Activated();
789 loadConfig(true, "CONNECTION_MODE");
790 }
791 }
792
793 isInit = true;
794 }
795
resetProperties()796 void DefaultDevice::resetProperties()
797 {
798 std::vector<INDI::Property *>::iterator orderi;
799
800 for (orderi = pAll.begin(); orderi != pAll.end(); ++orderi)
801 {
802 INDI_PROPERTY_TYPE pType = (*orderi)->getType();
803 void *pPtr = (*orderi)->getProperty();
804
805 switch (pType)
806 {
807 case INDI_NUMBER:
808 static_cast<INumberVectorProperty *>(pPtr)->s = IPS_IDLE;
809 IDSetNumber(static_cast<INumberVectorProperty *>(pPtr), nullptr);
810 break;
811 case INDI_TEXT:
812 static_cast<ITextVectorProperty *>(pPtr)->s = IPS_IDLE;
813 IDSetText(static_cast<ITextVectorProperty *>(pPtr), nullptr);
814 break;
815 case INDI_SWITCH:
816 static_cast<ISwitchVectorProperty *>(pPtr)->s = IPS_IDLE;
817 IDSetSwitch(static_cast<ISwitchVectorProperty *>(pPtr), nullptr);
818 break;
819 case INDI_LIGHT:
820 static_cast<ILightVectorProperty *>(pPtr)->s = IPS_IDLE;
821 IDSetLight(static_cast<ILightVectorProperty *>(pPtr), nullptr);
822 break;
823 case INDI_BLOB:
824 static_cast<IBLOBVectorProperty *>(pPtr)->s = IPS_IDLE;
825 IDSetBLOB(static_cast<IBLOBVectorProperty *>(pPtr), nullptr);
826 break;
827 case INDI_UNKNOWN:
828 break;
829 }
830 }
831 }
832
setConnected(bool status,IPState state,const char * msg)833 void DefaultDevice::setConnected(bool status, IPState state, const char *msg)
834 {
835 ISwitch *sp = nullptr;
836 ISwitchVectorProperty *svp = getSwitch(INDI::SP::CONNECTION);
837 if (!svp)
838 return;
839
840 IUResetSwitch(svp);
841
842 // Connect
843 if (status)
844 {
845 sp = IUFindSwitch(svp, "CONNECT");
846 if (!sp)
847 return;
848 sp->s = ISS_ON;
849 }
850 // Disconnect
851 else
852 {
853 sp = IUFindSwitch(svp, "DISCONNECT");
854 if (!sp)
855 return;
856 sp->s = ISS_ON;
857 }
858
859 svp->s = state;
860
861 if (msg == nullptr)
862 IDSetSwitch(svp, nullptr);
863 else
864 IDSetSwitch(svp, "%s", msg);
865 }
866
867 // This is a helper function
868 // that just encapsulates the Indi way into our clean c++ way of doing things
SetTimer(uint32_t ms)869 int DefaultDevice::SetTimer(uint32_t ms)
870 {
871 return IEAddTimer(ms, timerfunc, this);
872 }
873
874 // Just another helper to help encapsulate indi into a clean class
RemoveTimer(int id)875 void DefaultDevice::RemoveTimer(int id)
876 {
877 IERmTimer(id);
878 return;
879 }
880
881 // This is just a placeholder
882 // This function should be overriden by child classes if they use timers
883 // So we should never get here
TimerHit()884 void DefaultDevice::TimerHit()
885 {
886 return;
887 }
888
updateProperties()889 bool DefaultDevice::updateProperties()
890 {
891 // The base device has no properties to update
892 return true;
893 }
894
getDriverInterface()895 uint16_t DefaultDevice::getDriverInterface()
896 {
897 return interfaceDescriptor;
898 }
899
setDriverInterface(uint16_t value)900 void DefaultDevice::setDriverInterface(uint16_t value)
901 {
902 char interfaceStr[16];
903 interfaceDescriptor = value;
904 snprintf(interfaceStr, 16, "%d", interfaceDescriptor);
905 IUSaveText(&DriverInfoT[3], interfaceStr);
906 }
907
initProperties()908 bool DefaultDevice::initProperties()
909 {
910 char versionStr[16];
911 char interfaceStr[16];
912
913 snprintf(versionStr, 16, "%d.%d", majorVersion, minorVersion);
914 snprintf(interfaceStr, 16, "%d", interfaceDescriptor);
915
916 IUFillSwitch(&ConnectionS[0], "CONNECT", "Connect", ISS_OFF);
917 IUFillSwitch(&ConnectionS[1], "DISCONNECT", "Disconnect", ISS_ON);
918 IUFillSwitchVector(&ConnectionSP, ConnectionS, 2, getDeviceName(), INDI::SP::CONNECTION, "Connection", "Main Control",
919 IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
920 registerProperty(&ConnectionSP, INDI_SWITCH);
921
922 IUFillText(&DriverInfoT[0], "DRIVER_NAME", "Name", getDriverName());
923 IUFillText(&DriverInfoT[1], "DRIVER_EXEC", "Exec", getDriverExec());
924 IUFillText(&DriverInfoT[2], "DRIVER_VERSION", "Version", versionStr);
925 IUFillText(&DriverInfoT[3], "DRIVER_INTERFACE", "Interface", interfaceStr);
926 IUFillTextVector(&DriverInfoTP, DriverInfoT, 4, getDeviceName(), "DRIVER_INFO", "Driver Info", CONNECTION_TAB,
927 IP_RO, 60, IPS_IDLE);
928 registerProperty(&DriverInfoTP, INDI_TEXT);
929
930 IUFillSwitch(&DebugS[0], "ENABLE", "Enable", ISS_OFF);
931 IUFillSwitch(&DebugS[1], "DISABLE", "Disable", ISS_ON);
932 IUFillSwitchVector(&DebugSP, DebugS, NARRAY(DebugS), getDeviceName(), "DEBUG", "Debug", "Options", IP_RW,
933 ISR_1OFMANY, 0, IPS_IDLE);
934
935 IUFillSwitch(&SimulationS[0], "ENABLE", "Enable", ISS_OFF);
936 IUFillSwitch(&SimulationS[1], "DISABLE", "Disable", ISS_ON);
937 IUFillSwitchVector(&SimulationSP, SimulationS, NARRAY(SimulationS), getDeviceName(), "SIMULATION", "Simulation",
938 "Options", IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
939
940 IUFillSwitch(&ConfigProcessS[0], "CONFIG_LOAD", "Load", ISS_OFF);
941 IUFillSwitch(&ConfigProcessS[1], "CONFIG_SAVE", "Save", ISS_OFF);
942 IUFillSwitch(&ConfigProcessS[2], "CONFIG_DEFAULT", "Default", ISS_OFF);
943 IUFillSwitch(&ConfigProcessS[3], "CONFIG_PURGE", "Purge", ISS_OFF);
944 IUFillSwitchVector(&ConfigProcessSP, ConfigProcessS, NARRAY(ConfigProcessS), getDeviceName(), "CONFIG_PROCESS",
945 "Configuration", "Options", IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
946
947 IUFillNumber(&PollPeriodN[0], "PERIOD_MS", "Period (ms)", "%.f", 10, 600000, 1000, POLLMS);
948 IUFillNumberVector(&PollPeriodNP, PollPeriodN, 1, getDeviceName(), "POLLING_PERIOD", "Polling", "Options", IP_RW, 0, IPS_IDLE);
949
950 INDI::Logger::initProperties(this);
951
952 // Ready the logger
953 std::string logFile = getDriverExec();
954
955 DEBUG_CONF(logFile, Logger::file_off | Logger::screen_on, Logger::defaultlevel, Logger::defaultlevel);
956
957 return true;
958 }
959
deleteProperty(const char * propertyName)960 bool DefaultDevice::deleteProperty(const char *propertyName)
961 {
962 char errmsg[MAXRBUF];
963
964 if (propertyName == nullptr)
965 {
966 //while(!pAll.empty()) delete bar.back(), bar.pop_back();
967 IDDelete(getDeviceName(), nullptr, nullptr);
968 return true;
969 }
970
971 // Keep dynamic properties in existing property list so they can be reused
972 if (deleteDynamicProperties == false)
973 {
974 INDI::Property *prop = getProperty(propertyName);
975 if (prop && prop->isDynamic())
976 {
977 IDDelete(getDeviceName(), propertyName, nullptr);
978 return true;
979 }
980 }
981
982 if (removeProperty(propertyName, errmsg) == 0)
983 {
984 IDDelete(getDeviceName(), propertyName, nullptr);
985 return true;
986 }
987 else
988 return false;
989 }
990
defineNumber(INumberVectorProperty * nvp)991 void DefaultDevice::defineNumber(INumberVectorProperty *nvp)
992 {
993 registerProperty(nvp, INDI_NUMBER);
994 IDDefNumber(nvp, nullptr);
995 }
996
defineText(ITextVectorProperty * tvp)997 void DefaultDevice::defineText(ITextVectorProperty *tvp)
998 {
999 registerProperty(tvp, INDI_TEXT);
1000 IDDefText(tvp, nullptr);
1001 }
1002
defineSwitch(ISwitchVectorProperty * svp)1003 void DefaultDevice::defineSwitch(ISwitchVectorProperty *svp)
1004 {
1005 registerProperty(svp, INDI_SWITCH);
1006 IDDefSwitch(svp, nullptr);
1007 }
1008
defineLight(ILightVectorProperty * lvp)1009 void DefaultDevice::defineLight(ILightVectorProperty *lvp)
1010 {
1011 registerProperty(lvp, INDI_LIGHT);
1012 IDDefLight(lvp, nullptr);
1013 }
1014
defineBLOB(IBLOBVectorProperty * bvp)1015 void DefaultDevice::defineBLOB(IBLOBVectorProperty *bvp)
1016 {
1017 registerProperty(bvp, INDI_BLOB);
1018 IDDefBLOB(bvp, nullptr);
1019 }
1020
Connect()1021 bool DefaultDevice::Connect()
1022 {
1023 if (isConnected())
1024 return true;
1025
1026 if (activeConnection == nullptr)
1027 {
1028 LOG_ERROR("No active connection defined.");
1029 return false;
1030 }
1031
1032 bool rc = activeConnection->Connect();
1033
1034 if (rc)
1035 {
1036 saveConfig(true, "CONNECTION_MODE");
1037 if (POLLMS > 0)
1038 SetTimer(POLLMS);
1039 }
1040
1041 return rc;
1042 }
1043
Disconnect()1044 bool DefaultDevice::Disconnect()
1045 {
1046 if (isSimulation())
1047 {
1048 DEBUGF(Logger::DBG_SESSION, "%s is offline.", getDeviceName());
1049 return true;
1050 }
1051
1052 if (activeConnection)
1053 {
1054 bool rc = activeConnection->Disconnect();
1055 if (rc)
1056 {
1057 DEBUGF(Logger::DBG_SESSION, "%s is offline.", getDeviceName());
1058 return true;
1059 }
1060 else
1061 return false;
1062 }
1063
1064 return false;
1065 }
1066
registerConnection(Connection::Interface * newConnection)1067 void DefaultDevice::registerConnection(Connection::Interface *newConnection)
1068 {
1069 connections.push_back(newConnection);
1070 }
1071
unRegisterConnection(Connection::Interface * existingConnection)1072 bool DefaultDevice::unRegisterConnection(Connection::Interface *existingConnection)
1073 {
1074 auto i = std::begin(connections);
1075
1076 while (i != std::end(connections))
1077 {
1078 if (*i == existingConnection)
1079 {
1080 i = connections.erase(i);
1081 return true;
1082 }
1083 else
1084 ++i;
1085 }
1086
1087 return false;
1088 }
1089
setDefaultPollingPeriod(uint32_t period)1090 void DefaultDevice::setDefaultPollingPeriod(uint32_t period)
1091 {
1092 PollPeriodN[0].value = period;
1093 POLLMS = period;
1094 }
1095
setPollingPeriodRange(uint32_t minimum,uint32_t maximum)1096 void DefaultDevice::setPollingPeriodRange(uint32_t minimum, uint32_t maximum)
1097 {
1098 PollPeriodN[0].min = minimum;
1099 PollPeriodN[0].max = maximum;
1100 IUUpdateMinMax(&PollPeriodNP);
1101 }
1102
1103 }
1104