| 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! */
|
|---|