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