| 1 | /*
 | 
|---|
| 2 |    INDI Developers Manual
 | 
|---|
| 3 |    Tutorial #1
 | 
|---|
| 4 |    
 | 
|---|
| 5 |    "Hello INDI"
 | 
|---|
| 6 |    
 | 
|---|
| 7 |    We construct a most basic (and useless) device driver to illustate INDI.
 | 
|---|
| 8 |    
 | 
|---|
| 9 |    Refer to README, which contains instruction on how to build this driver, and use it 
 | 
|---|
| 10 |    with an INDI-compatible client.
 | 
|---|
| 11 | 
 | 
|---|
| 12 | */
 | 
|---|
| 13 | 
 | 
|---|
| 14 | /** \file tutorial_one.c
 | 
|---|
| 15 |     \brief Construct a basic INDI driver with only one property.
 | 
|---|
| 16 |     \author Jasem Mutlaq
 | 
|---|
| 17 | */
 | 
|---|
| 18 | 
 | 
|---|
| 19 | /* Standard headers */
 | 
|---|
| 20 | #include <stdio.h>
 | 
|---|
| 21 | #include <stdlib.h>
 | 
|---|
| 22 | #include <string.h>
 | 
|---|
| 23 | #include <math.h>
 | 
|---|
| 24 | #include <unistd.h>
 | 
|---|
| 25 | 
 | 
|---|
| 26 | /* INDI Core headers */
 | 
|---|
| 27 | 
 | 
|---|
| 28 | /* indidevapi.h contains API declerations */
 | 
|---|
| 29 | #include "indidevapi.h"
 | 
|---|
| 30 | 
 | 
|---|
| 31 | /* INDI Eventloop mechanism */
 | 
|---|
| 32 | #include "eventloop.h"
 | 
|---|
| 33 | 
 | 
|---|
| 34 | /* INDI Common Routines */
 | 
|---|
| 35 | #include "indicom.h"
 | 
|---|
| 36 | 
 | 
|---|
| 37 | /* Definitions */
 | 
|---|
| 38 | 
 | 
|---|
| 39 | #define mydev           "Simple Device"                         /* Device name */
 | 
|---|
| 40 | #define MAIN_GROUP      "Main Control"                          /* Group name */
 | 
|---|
| 41 | 
 | 
|---|
| 42 | /* Function protptypes */
 | 
|---|
| 43 | void connectDevice(void);
 | 
|---|
| 44 | 
 | 
|---|
| 45 | /*INDI controls */
 | 
|---|
| 46 | 
 | 
|---|
| 47 | /* We will define only one vector switch property. The CONNECTION property is an INDI Standard Property and should be implemented in all INDI drivers.
 | 
|---|
| 48 |  * A vector property may have one or more members. */
 | 
|---|
| 49 | 
 | 
|---|
| 50 | /* First, we define and initilize the members of the vector property */
 | 
|---|
| 51 | static ISwitch PowerS[]                 = {
 | 
|---|
| 52 |                                                           {"CONNECT"                    /* 1st Swtich name */
 | 
|---|
| 53 |                                                           , "Connect"                   /* Switch Label, i.e. what the GUI displays */
 | 
|---|
| 54 |                                                           , ISS_OFF                     /* State of the switch, initially off */
 | 
|---|
| 55 |                                                           , 0                                   /* auxiluary, set to 0 for now.*/
 | 
|---|
| 56 |                                                           , 0}                                  /* auxiluary, set to 0 for now */
 | 
|---|
| 57 |                                                          
 | 
|---|
| 58 |                                                          ,{"DISCONNECT"         /* 2nd Swtich name */
 | 
|---|
| 59 |                                                          , "Disconnect"                 /* Switch Label, i.e. what the GUI displays */
 | 
|---|
| 60 |                                                          , ISS_ON                               /* State of the switch, initially on */
 | 
|---|
| 61 |                                                          , 0                                    /* auxiluary, set to 0 for now.*/
 | 
|---|
| 62 |                                                          , 0}                                   /* auxiluary, set to 0 for now */
 | 
|---|
| 63 |                                                      };
 | 
|---|
| 64 |                                                      
 | 
|---|
| 65 | /* Next, we define and initlize the vector switch property that contains the two switches we defined above */
 | 
|---|
| 66 | static ISwitchVectorProperty PowerSP    = { 
 | 
|---|
| 67 |                                                                 mydev                   /* Device name */
 | 
|---|
| 68 |                                                                 , "CONNECTION"          /* Property name */
 | 
|---|
| 69 |                                                                 , "Connection"          /* Property label */
 | 
|---|
| 70 |                                                                 , MAIN_GROUP    /* Property group */
 | 
|---|
| 71 |                                                                 , IP_RW                 /* Property premission, it's both read and write */
 | 
|---|
| 72 |                                                                 , ISR_1OFMANY   /* Switch behavior. Only 1 of many switches are allowed to be on at the same time */
 | 
|---|
| 73 |                                                                 , 0                             /* Timeout, 0 seconds */
 | 
|---|
| 74 |                                                                 , IPS_IDLE              /* Initial state is idle */
 | 
|---|
| 75 |                                                                 , PowerS                        /* Switches comprimising this vector that we defined above */
 | 
|---|
| 76 |                                                                 , NARRAY(PowerS)        /* Number of Switches. NARRAY is defined in indiapi.h */
 | 
|---|
| 77 |                                                                 , ""                            /* Timestamp, set to 0 */
 | 
|---|
| 78 |                                                                 , 0};                           /* auxiluary, set to 0 for now */
 | 
|---|
| 79 | 
 | 
|---|
| 80 | 
 | 
|---|
| 81 | /* void ISGetProperties (const char *dev)
 | 
|---|
| 82 | *  INDI will call this function when the client inquires about the device properties.
 | 
|---|
| 83 | *  Here we will use IDxxx functions to define new properties to the client */
 | 
|---|
| 84 | void ISGetProperties (const char *dev)
 | 
|---|
| 85 | { 
 | 
|---|
| 86 |   /* Tell the client to create a new Switch property PowerSP */
 | 
|---|
| 87 |   IDDefSwitch(&PowerSP, NULL); 
 | 
|---|
| 88 | }
 | 
|---|
| 89 |   
 | 
|---|
| 90 | /* void ISNewSwitch(...)
 | 
|---|
| 91 |  * INDI will call this function when the client wants to set a new state of existing switches 
 | 
|---|
| 92 |  ** Parameters **
 | 
|---|
| 93 |  
 | 
|---|
| 94 |  * dev: the device name
 | 
|---|
| 95 |  * name: the property name the client wants to update
 | 
|---|
| 96 |  * states: an array of states of members switches (ISS_ON and ISS_OFF)
 | 
|---|
| 97 |  * names: names of the switches. The names are parallel to the states. That is, member names[0] has a state states[0], and so on...
 | 
|---|
| 98 |  * n: number of switches to update, which is also the dimension of *states and names[]
 | 
|---|
| 99 | */
 | 
|---|
| 100 | void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
 | 
|---|
| 101 | {
 | 
|---|
| 102 |       /* Let's check if the property the client wants to change is the PowerSP (name: CONNECTION) property*/
 | 
|---|
| 103 |      if (!strcmp (name, PowerSP.name))
 | 
|---|
| 104 |      {
 | 
|---|
| 105 |           /* If the clients wants to update this property, let's perform the following */
 | 
|---|
| 106 |           
 | 
|---|
| 107 |           /* A. We update the switches by sending their names and updated states IUUpdateSwitches function. If there is an error, we return */
 | 
|---|
| 108 |           if (IUUpdateSwitch(&PowerSP, states, names, n) < 0) return;
 | 
|---|
| 109 |           
 | 
|---|
| 110 |           /* B. We try to establish a connection to our device */
 | 
|---|
| 111 |           connectDevice();
 | 
|---|
| 112 |           return;
 | 
|---|
| 113 |      }
 | 
|---|
| 114 | }
 | 
|---|
| 115 | 
 | 
|---|
| 116 | /* void ISNewText(...)
 | 
|---|
| 117 |  * INDI will call this function when the client wants to update an existing text.
 | 
|---|
| 118 |  ** Parameters **
 | 
|---|
| 119 |  
 | 
|---|
| 120 |  * dev: the device name
 | 
|---|
| 121 |  * name: the property name the client wants to update
 | 
|---|
| 122 |  * texts: an array of texts.
 | 
|---|
| 123 |  * names: names of the members. The names are parallel to the texts.
 | 
|---|
| 124 |  * n: number of texts to update, which is also the dimension of *texts and names[]
 | 
|---|
| 125 | */
 | 
|---|
| 126 | void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
 | 
|---|
| 127 | {
 | 
|---|
| 128 |         /* Even though we didn't define any text members, we need to define this function. Otherwise, the driver will not compile */
 | 
|---|
| 129 |         /* Since there is nothing to do, we simply return */
 | 
|---|
| 130 |         return; 
 | 
|---|
| 131 | }
 | 
|---|
| 132 | 
 | 
|---|
| 133 | /* void ISNewNumber(...)
 | 
|---|
| 134 |  * INDI will call this function when the client wants to update an existing number.
 | 
|---|
| 135 |  ** Parameters **
 | 
|---|
| 136 |  
 | 
|---|
| 137 |  * dev: the device name
 | 
|---|
| 138 |  * name: the property name the client wants to update
 | 
|---|
| 139 |  * values: an array of values.
 | 
|---|
| 140 |  * names: names of the members. The names are parallel to the values.
 | 
|---|
| 141 |  * n: number of numbers to update, which is the dimension of *numbers and names[]
 | 
|---|
| 142 | */
 | 
|---|
| 143 | void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
 | 
|---|
| 144 | {
 | 
|---|
| 145 |          /* Even though we didn't define any number members, we need to define this function. Otherwise, the driver will not compile */
 | 
|---|
| 146 |         /* Since there is nothing to do, we simply return */
 | 
|---|
| 147 |         return;
 | 
|---|
| 148 | }
 | 
|---|
| 149 | 
 | 
|---|
| 150 | /* Note that we must define ISNewBLOB and ISSnoopDevice even if we don't use them, otherwise, the driver will NOT compile */
 | 
|---|
| 151 | void ISNewBLOB (const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) {}
 | 
|---|
| 152 | void ISSnoopDevice (XMLEle *root) {}
 | 
|---|
| 153 | 
 | 
|---|
| 154 | 
 | 
|---|
| 155 | /* void connectDevice(void)
 | 
|---|
| 156 |  * This function is called when the state of PowerSP is changed in the ISNewSwitch() function.
 | 
|---|
| 157 |  * We check the state of CONNECT and DISCONNECT switches, and connect or disconnect our fake device accordingly */
 | 
|---|
| 158 | void connectDevice(void)
 | 
|---|
| 159 | {
 | 
|---|
| 160 | 
 | 
|---|
| 161 |   /* Now we check the state of CONNECT, the 1st member of the PowerSP property we defined earliar */
 | 
|---|
| 162 |   switch (PowerS[0].s)
 | 
|---|
| 163 |   {
 | 
|---|
| 164 |      /* If CONNECT is on, then try to establish a connection to the device */
 | 
|---|
| 165 |      case ISS_ON:
 | 
|---|
| 166 |       
 | 
|---|
| 167 |       /* The IDLog function is a very useful function that will log time-stamped messages to stderr. This function is invaluable to debug your drivers.
 | 
|---|
| 168 |        * It operates like printf */
 | 
|---|
| 169 |      IDLog ("Establishing a connection to %s...\n", mydev);
 | 
|---|
| 170 | 
 | 
|---|
| 171 |       /* Change the state of the PowerSP (CONNECTION) property to OK */
 | 
|---|
| 172 |       PowerSP.s = IPS_OK;
 | 
|---|
| 173 |       
 | 
|---|
| 174 |       /* Tell the client to update the states of the PowerSP property, and send a message to inform successful connection */
 | 
|---|
| 175 |       IDSetSwitch(&PowerSP, "Connection to %s is successful.", mydev);
 | 
|---|
| 176 |       break;
 | 
|---|
| 177 |       
 | 
|---|
| 178 |     /* If CONNECT is off (which is the same thing as DISCONNECT being on), then try to disconnect the device */
 | 
|---|
| 179 |     case ISS_OFF:
 | 
|---|
| 180 |     
 | 
|---|
| 181 |        IDLog ("Terminating connection to %s...\n", mydev);
 | 
|---|
| 182 |        
 | 
|---|
| 183 |       /* The device is disconnected, change the state to IDLE */
 | 
|---|
| 184 |       PowerSP.s = IPS_IDLE;
 | 
|---|
| 185 |       
 | 
|---|
| 186 |       /* Tell the client to update the states of the PowerSP property, and send a message to inform successful disconnection */
 | 
|---|
| 187 |       IDSetSwitch(&PowerSP, "%s has been disconneced.", mydev);
 | 
|---|
| 188 |       
 | 
|---|
| 189 |       break;
 | 
|---|
| 190 |      }
 | 
|---|
| 191 |      
 | 
|---|
| 192 | }
 | 
|---|
| 193 | 
 | 
|---|
| 194 | /* That's it! check out tutorial_two where we simulate a simple telescope! */
 | 
|---|