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 #pragma once
20 
21 #include "basedevice.h"
22 #include "indidriver.h"
23 #include "indilogger.h"
24 
25 #include <stdint.h>
26 
27 namespace Connection
28 {
29 class Interface;
30 class Serial;
31 class TCP;
32 }
33 /**
34  * @brief COMMUNICATION_TAB Where all the properties required to connect/disconnect from
35  * a device are located. Usually such properties may include port number, IP address, or
36  * any property necessarily to establish a connection to the device.
37  */
38 extern const char *COMMUNICATION_TAB;
39 
40 /**
41  * @brief MAIN_CONTROL_TAB Where all the primary controls for the device are located.
42  */
43 extern const char *MAIN_CONTROL_TAB;
44 
45 /**
46  * @brief CONNECTION_TAB Where all device connection settings (serial, usb, ethernet) are defined and controlled.
47  */
48 extern const char *CONNECTION_TAB;
49 
50 /**
51  * @brief MOTION_TAB Where all the motion control properties of the device are located.
52  */
53 extern const char *MOTION_TAB;
54 
55 /**
56  * @brief DATETIME_TAB Where all date and time setting properties are located.
57  */
58 extern const char *DATETIME_TAB;
59 
60 /**
61  * @brief SITE_TAB Where all site information setting are located.
62  */
63 extern const char *SITE_TAB;
64 
65 /**
66  * @brief OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls, driver
67  * metadata, version information..etc.
68  */
69 extern const char *OPTIONS_TAB;
70 
71 /**
72  * @brief FILTER_TAB Where all the properties for filter wheels are located.
73  */
74 extern const char *FILTER_TAB;
75 
76 /**
77  * @brief FOCUS_TAB Where all the properties for focuser are located.
78  */
79 extern const char *FOCUS_TAB;
80 
81 /**
82  * @brief GUIDE_TAB Where all the properties for guiding are located.
83  */
84 extern const char *GUIDE_TAB;
85 
86 /**
87  * @brief ALIGNMENT_TAB Where all the properties for guiding are located.
88  */
89 extern const char *ALIGNMENT_TAB;
90 
91 /**
92  * @brief INFO_TAB Where all the properties for general information are located.
93  */
94 extern const char *INFO_TAB;
95 
96 /**
97  * \class INDI::DefaultDevice
98  * \brief Class to provide extended functionality for devices in addition
99  * to the functionality provided by INDI::BaseDevice. This class should \e only be subclassed by
100  * drivers directly as it is linked with main(). Virtual drivers cannot employ INDI::DefaultDevice.
101  *
102  * INDI::DefaultDevice provides capability to add Debug, Simulation, and Configuration controls.
103  * These controls (switches) are defined to the client. Configuration options permit saving and
104  * loading of AS-IS property values.
105  *
106  * \see <a href='tutorial__four_8h_source.html'>Tutorial Four</a>
107  * \author Jasem Mutlaq
108  */
109 class INDI::DefaultDevice : public INDI::BaseDevice
110 {
111     public:
112         DefaultDevice();
113         virtual ~DefaultDevice() override;
114 
115         /** \brief Add Debug, Simulation, and Configuration options to the driver */
116         void addAuxControls();
117 
118         /** \brief Add Debug control to the driver */
119         void addDebugControl();
120 
121         /** \brief Add Simulation control to the driver */
122         void addSimulationControl();
123 
124         /** \brief Add Configuration control to the driver */
125         void addConfigurationControl();
126 
127         /** \brief Add Polling period control to the driver */
128         void addPollPeriodControl();
129 
130         /** \brief Set all properties to IDLE state */
131         void resetProperties();
132 
133         /**
134          * \brief Define number vector to client & register it. Alternatively, IDDefNumber can
135          * be used but the property will not get registered and the driver will not be able to
136          * save configuration files.
137          * \param nvp The number vector property to be defined
138          */
139         void defineNumber(INumberVectorProperty *nvp);
140 
141         /**
142          * \brief Define text vector to client & register it. Alternatively, IDDefText can be
143          * used but the property will not get registered and the driver will not be able to save
144          * configuration files.
145          * \param tvp The text vector property to be defined
146          */
147         void defineText(ITextVectorProperty *tvp);
148 
149         /**
150          * \brief Define switch vector to client & register it. Alternatively, IDDefswitch can be
151          * used but the property will not get registered and the driver will not be able to save
152          * configuration files.
153          * \param svp The switch vector property to be defined
154          */
155         void defineSwitch(ISwitchVectorProperty *svp);
156 
157         /**
158          * \brief Define light vector to client & register it. Alternatively, IDDeflight can be
159          * used but the property will not get registered and the driver will not be able to save
160          * configuration files.
161          * \param lvp The light vector property to be defined
162          */
163         void defineLight(ILightVectorProperty *lvp);
164 
165         /**
166          * \brief Define BLOB vector to client & register it. Alternatively, IDDefBLOB can be
167          * used but the property will not get registered and the driver will not be able to
168          * save configuration files.
169          * \param bvp The BLOB vector property to be defined
170          */
171         void defineBLOB(IBLOBVectorProperty *bvp);
172 
173         /**
174          * \brief Delete a property and unregister it. It will also be deleted from all clients.
175          * \param propertyName name of property to be deleted.
176          */
177         virtual bool deleteProperty(const char *propertyName);
178 
179         /**
180          * \brief Set connection switch status in the client.
181          * \param status If true, the driver will attempt to connect to the device (CONNECT=ON).
182          * If false, it will attempt to disconnect the device.
183          * \param status True to set CONNECT on, false to set DISCONNECT on.
184          * \param state State of CONNECTION properti, by default IPS_OK.
185          * \param msg A message to be sent along with connect/disconnect command, by default nullptr.
186          */
187         virtual void setConnected(bool status, IPState state = IPS_OK, const char *msg = nullptr);
188 
189         /**
190          * \brief Set a timer to call the function TimerHit after ms milliseconds
191          * \param ms timer duration in milliseconds.
192          * \return id of the timer to be used with RemoveTimer
193         */
194         int SetTimer(uint32_t ms);
195 
196         /**
197          * \brief Remove timer added with SetTimer
198          * \param id ID of the timer as returned from SetTimer
199          */
200         void RemoveTimer(int id);
201 
202         /** \brief Callback function to be called once SetTimer duration elapses. */
203         virtual void TimerHit();
204 
205         /** \return driver executable filename */
getDriverExec()206         virtual const char *getDriverExec()
207         {
208             return me;
209         }
210 
211         /** \return driver name */
getDriverName()212         virtual const char *getDriverName()
213         {
214             return getDefaultName();
215         }
216 
217         /**
218          * \brief Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor
219          * \param vMajor major revision number
220          * \param vMinor minor revision number
221          */
setVersion(uint16_t vMajor,uint16_t vMinor)222         void setVersion(uint16_t vMajor, uint16_t vMinor)
223         {
224             majorVersion = vMajor;
225             minorVersion = vMinor;
226         }
227 
228         /** \return Major driver version number. */
getMajorVersion()229         uint16_t getMajorVersion()
230         {
231             return majorVersion;
232         }
233 
234         /** \return Minor driver version number. */
getMinorVersion()235         uint16_t getMinorVersion()
236         {
237             return minorVersion;
238         }
239 
240         /**
241          * \brief define the driver's properties to the client.
242          * Usually, only a minimum set of properties are defined to the client in this function
243          * if the device is in disconnected state. Those properties should be enough to enable the
244          * client to establish a connection to the device. In addition to CONNECT/DISCONNECT, such
245          * properties may include port name, IP address, etc. You should check if the device is
246          * already connected, and if this is true, then you must define the remainder of the
247          * the properties to the client in this function. Otherwise, the remainder of the driver's
248          * properties are defined to the client in updateProperties() function which is called when
249          * a client connects/disconnects from a device.
250          * \param dev name of the device
251          * \note This function is called by the INDI framework, do not call it directly. See LX200
252          * Generic driver for an example implementation
253          */
254         virtual void ISGetProperties(const char *dev);
255 
256         /**
257          * \brief Process the client newSwitch command
258          * \note This function is called by the INDI framework, do not call it directly.
259          * \returns True if any property was successfully processed, false otherwise.
260          */
261         virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n);
262 
263         /**
264          * \brief Process the client newNumber command
265          * \note This function is called by the INDI framework, do not call it directly.
266          * \returns True if any property was successfully processed, false otherwise.
267          */
268         virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n);
269 
270         /**
271          * \brief Process the client newSwitch command
272          * \note This function is called by the INDI framework, do not call it directly.
273          * \returns True if any property was successfully processed, false otherwise.
274          */
275         virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n);
276 
277         /**
278          * \brief Process the client newBLOB command
279          * \note This function is called by the INDI framework, do not call it directly.
280          * \returns True if any property was successfully processed, false otherwise.
281          */
282         virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
283                                char *formats[], char *names[], int n);
284 
285         /**
286          * \brief Process a snoop event from INDI server. This function is called when a snooped property is
287          * updated in a snooped driver.
288          * \note This function is called by the INDI framework, do not call it directly.
289          * \returns True if any property was successfully processed, false otherwise.
290          */
291         virtual bool ISSnoopDevice(XMLEle *root);
292 
293         /**
294          * @return getInterface Return the interface declared by the driver.
295          */
296         virtual uint16_t getDriverInterface() override;
297 
298         /**
299          * @brief setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE.
300          * You may send an ORed list of DeviceInterface values.
301          * @param value ORed list of DeviceInterface values.
302          */
303         void setDriverInterface(uint16_t value);
304 
305     protected:
306         /**
307          * @brief setDynamicPropertiesBehavior controls handling of dynamic properties. Dyanmic properties
308          * are those generated from an external skeleton XML file. By default all properties, including
309          * dynamic properties, are defined to the client in ISGetProperties(). Furthermore, when
310          * űdeleteProperty(properyName) is called, the dynamic property is deleted by default, and can only
311          * be restored by calling buildSkeleton(filename) again. However, it is sometimes desirable to skip
312          * the definition of the dynamic properties on startup and delegate this task to the child class.
313          * To control this behavior, set enabled to false.
314          * @param defineEnabled True to define all dynamic properties in INDI::DefaultDevice own
315          * ISGetProperties() on startup. False to skip defining dynamic properties.
316          * @param deleteEnabled True to delete dynamic properties from memory in deleteProperty(name).
317          * False to keep dynamic property in the properties list, but delete it from the client.
318          * @note This function has no effect on regular properties initialized directly by the driver.
319          */
setDynamicPropertiesBehavior(bool defineEnabled,bool deleteEnabled)320         void setDynamicPropertiesBehavior(bool defineEnabled, bool deleteEnabled)
321         {
322             defineDynamicProperties = defineEnabled;
323             deleteDynamicProperties = deleteEnabled;
324         }
325 
326         // Configuration
327 
328         /**
329          * \brief Load the last saved configuration file
330          * \param silent if true, don't report any error or notification messages.
331          * \param property Name of property to load configuration for. If nullptr, all properties in the
332          * configuration file are loaded which is the default behavior.
333          * \return True if successful, false otherwise.
334          */
335         virtual bool loadConfig(bool silent = false, const char *property = nullptr);
336 
337         /**
338          * \brief Save the current properties in a configuration file
339          * \param silent if true, don't report any error or notification messages.
340          * \param property Name of specific property to save while leaving all others properties in the
341          * file as is.
342          * \return True if successful, false otherwise.
343          */
344         virtual bool saveConfig(bool silent = false, const char *property = nullptr);
345 
346         /**
347          * @brief purgeConfig Remove config file from disk.
348          * @return True if successful, false otherwise.
349          */
350         virtual bool purgeConfig();
351 
352         /**
353          * @brief saveConfigItems Save specific properties in the provide config file handler. Child
354          * class usually override this function to save their own properties and the base class
355          * saveConfigItems(fp) must be explicitly called by each child class. The Default Device
356          * saveConfigItems(fp) only save Debug properties options in the config file.
357          * @param fp Pointer to config file handler
358          * @return True if successful, false otherwise.
359          */
360         virtual bool saveConfigItems(FILE *fp);
361 
362         /**
363          * @brief saveAllConfigItems Save all the drivers' properties in the configuration file
364          * @param fp pointer to config file handler
365          * @return  True if successful, false otherwise.
366          */
367         virtual bool saveAllConfigItems(FILE *fp);
368 
369         /**
370          * \brief Load the default configuration file
371          * \return True if successful, false otherwise.
372          */
373         virtual bool loadDefaultConfig();
374 
375         // Simulatin & Debug
376 
377         /**
378          * \brief Toggle driver debug status
379          * A driver can be more verbose if Debug option is enabled by the client.
380          * \param enable If true, the Debug option is set to ON.
381          */
382         void setDebug(bool enable);
383 
384         /**
385          * \brief Toggle driver simulation status
386          * A driver can run in simulation mode if Simulation option is enabled by the client.
387          * \param enable If true, the Simulation option is set to ON.
388          */
389         void setSimulation(bool enable);
390 
391         /**
392          * \brief Inform driver that the debug option was triggered.
393          * This function is called after setDebug is triggered by the client. Reimplement this
394          * function if your driver needs to take specific action after debug is enabled/disabled.
395          * Otherwise, you can use isDebug() to check if simulation is enabled or disabled.
396          * \param enable If true, the debug option is set to ON.
397          */
398         virtual void debugTriggered(bool enable);
399 
400         /**
401          * \brief Inform driver that the simulation option was triggered.
402          * This function is called after setSimulation is triggered by the client. Reimplement this
403          * function if your driver needs to take specific action after simulation is enabled/disabled.
404          * Otherwise, you can use isSimulation() to check if simulation is enabled or disabled.
405          * \param enable If true, the simulation option is set to ON.
406          */
407         virtual void simulationTriggered(bool enable);
408 
409         /** \return True if Debug is on, False otherwise. */
410         bool isDebug();
411 
412         /** \return True if Simulation is on, False otherwise. */
413         bool isSimulation();
414 
415         /**
416          * \brief Initilize properties initial state and value. The child class must implement this function.
417          * \return True if initilization is successful, false otherwise.
418          */
419         virtual bool initProperties();
420 
421         /**
422          * \brief updateProperties is called whenever there is a change in the CONNECTION status of
423          * the driver. This will enable the driver to react to changes of switching ON/OFF a device.
424          * For example, a driver may only define a set of properties after a device is connected, but
425          * not before.
426          * \return True if update is successful, false otherwise.
427          */
428         virtual bool updateProperties();
429 
430         /**
431          * \brief Connect to the device. INDI::DefaultDevice implementation connects to appropriate
432          * connection interface (Serial or TCP) governed by connectionMode. If connection is successful,
433          * it proceed to call Handshake() function to ensure communication with device is successful.
434          * For other communication interface, override the method in the child class implementation
435          * \return True if connection is successful, false otherwise
436          */
437         virtual bool Connect();
438 
439         /**
440          * \brief Disconnect from device
441          * \return True if successful, false otherwise
442          */
443         virtual bool Disconnect();
444 
445         /**
446          * @brief registerConnection Add new connection plugin to the existing connection pool. The
447          * connection type shall be defined to the client in ISGetProperties()
448          * @param newConnection Pointer to new connection plugin
449          */
450         void registerConnection(Connection::Interface *newConnection);
451 
452         /**
453          * @brief unRegisterConnection Remove connection from existing pool
454          * @param existingConnection pointer to connection interface
455          * @return True if connection is removed, false otherwise.
456          */
457         bool unRegisterConnection(Connection::Interface *existingConnection);
458 
459         /** @return Return actively selected connection plugin */
getActiveConnection()460         Connection::Interface *getActiveConnection()
461         {
462             return activeConnection;
463         }
464 
465         void setDefaultPollingPeriod(uint32_t period);
466         void setPollingPeriodRange(uint32_t minimum, uint32_t maximum);
getPollingPeriod()467         uint32_t getPollingPeriod()
468         {
469             return static_cast<uint32_t>(PollPeriodN[0].value);
470         }
471 
472         /** \return Default name of the device. */
473         virtual const char *getDefaultName() = 0;
474 
475         /// Period in milliseconds to call TimerHit(). Default 1000 ms
476         uint32_t POLLMS = 1000;
477 
478     private:
479         bool isInit { false };
480         bool pDebug { false };
481         bool pSimulation { false };
482 
483         uint16_t majorVersion { 1 };
484         uint16_t minorVersion { 0 };
485         uint16_t interfaceDescriptor { 0 };
486 
487         ISwitch DebugS[2];
488         ISwitch SimulationS[2];
489         ISwitch ConfigProcessS[4];
490         ISwitch ConnectionS[2];
491         INumber PollPeriodN[1];
492 
493         ISwitchVectorProperty DebugSP;
494         ISwitchVectorProperty SimulationSP;
495         ISwitchVectorProperty ConfigProcessSP;
496         ISwitchVectorProperty ConnectionSP;
497         INumberVectorProperty PollPeriodNP;
498 
499         IText DriverInfoT[4] {};
500         ITextVectorProperty DriverInfoTP;
501 
502         // Connection modes
503         ISwitch *ConnectionModeS = nullptr;
504         ISwitchVectorProperty ConnectionModeSP;
505 
506         std::vector<Connection::Interface *> connections;
507         Connection::Interface *activeConnection = nullptr;
508 
509         // Connection Plugins
510         friend class Connection::Serial;
511         friend class Connection::TCP;
512         friend class FilterInterface;
513 
514         bool defineDynamicProperties = true;
515         bool deleteDynamicProperties = true;
516 };
517