Applied comments from Florian FOSTER, added more documentation, pause, fixed some...
[collectd.git] / src / gps.c
1 /**
2  * collectd - src/gps.c
3  * Copyright (C) 2015  Nicolas JOURDEN
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Nicolas JOURDEN <nicolas.jourden at laposte.net>
25  **/
26
27
28
29 #include "collectd.h"
30 #include "common.h"
31 #include "plugin.h"
32
33
34 #define GPS_DEFAULT_HOST    "localhost"
35 #define GPS_DEFAULT_PORT    "2947"
36 #define GPS_DEFAULT_TIMEOUT 15
37 #define GPS_DEFAULT_PAUSE   1
38
39
40 #if HAVE_GPS_H
41 #include <gps.h>
42 #endif
43
44 #if HAVE_LIBPTHREAD
45 #include <pthread.h>
46 #endif
47
48
49 typedef struct
50 {
51   char *host;
52   char *port;
53   int timeout;
54   int pause;
55 } gps_definition_t;
56 static gps_definition_t gps_data_config;
57
58
59 typedef struct {
60  int satellites;
61  double vdop;
62  double hdop;
63 } gpsdata_t;
64 static gpsdata_t gps_data_read;
65
66
67 static const char *config_keys[] =
68 {
69   "Host",
70   "Port",
71   "Timeout",
72   "Pause"
73 };
74 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
75
76
77 // Thread items:
78 static pthread_t connector = (pthread_t) 0;
79 static pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
80
81
82 /**
83  * Thread reading from gpsd.
84  */
85 static void * gps_collectd_thread (void * pData)
86 {
87   struct gps_data_t gps_data;
88
89   while (1)
90   {
91     if (gps_open(gps_data_config.host, gps_data_config.port, &gps_data) < 0)
92     {
93       WARNING ("gps: cannot connect to: %s:%s", gps_data_config.host, gps_data_config.port);
94       sleep(60);
95       continue;
96     }
97
98     gps_stream(&gps_data, WATCH_ENABLE | WATCH_JSON | WATCH_NEWSTYLE, NULL);
99     gps_send(&gps_data, "?WATCH={\"enable\":true,\"json\":true,\"nmea\":false}\r\n");
100
101     while (1)
102     {
103       if (gps_waiting (&gps_data, gps_data_config.timeout ) )
104       {
105         DEBUG ("gps: reading\n");
106
107         if (gps_read (&gps_data) == -1)
108         {
109             WARNING ("gps: incorrect data !\n");
110         } 
111         else {
112           pthread_mutex_lock (&data_mutex);
113           DEBUG ("gps: parsing\n");
114
115           // Dop data:
116           if (isnan(gps_data.dop.vdop) == 0)
117           {
118             DEBUG ("gps: isnan(gps_data.dop.vdop) == 0 [OK]\n");
119             gps_data_read.vdop = gps_data.dop.vdop;
120           }
121           if (isnan(gps_data.dop.hdop) == 0)
122           {
123             DEBUG ("gps: isnan(gps_data.dop.hdop) == 0 [OK]\n");
124             gps_data_read.hdop = gps_data.dop.hdop;
125           }
126
127           // Sat in view:
128           if ((gps_data.set & LATLON_SET))
129           {
130             DEBUG ("gps: gps_data.set & LATLON_SET [OK] ... \n");
131             gps_data_read.satellites = gps_data.satellites_used;
132           }
133  
134           DEBUG ("gps: raw is hdop=%1.3f, vdop=%1.3f, sat-used=%02d, lat=%02.05f, lon=%03.05f\n", 
135             gps_data.dop.hdop,
136             gps_data.dop.vdop,
137             gps_data.satellites_used,
138             gps_data.fix.latitude,
139             gps_data.fix.longitude
140           );
141
142           pthread_mutex_unlock (&data_mutex);
143           sleep(gps_data_config.pause);
144         }
145       }
146     }
147   }
148
149   gps_stream(&gps_data, WATCH_DISABLE, NULL);
150   gps_close(&gps_data);
151
152   pthread_exit ((void *)0);
153 }
154
155
156 /**
157  * Submit a piece of the data.
158  */
159 static void gps_collectd_submit (const char *type, gauge_t value, const char *type_instance)
160 {
161   value_t values[1];
162   value_list_t vl = VALUE_LIST_INIT;
163
164   values[0].gauge = value;
165
166   vl.values = values;
167   vl.values_len = 1;
168   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
169   sstrncpy (vl.plugin, "gps", sizeof (vl.plugin));
170   sstrncpy (vl.type, type, sizeof (vl.type));
171   sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
172
173   plugin_dispatch_values (&vl);
174 }
175
176
177 /**
178  * Read the data and submit by piece.
179  */
180 static int gps_collectd_read ()
181 {
182   pthread_mutex_lock (&data_mutex);
183   gps_collectd_submit("dilution_of_precision", (gauge_t) gps_data_read.hdop, "horizontal");
184   gps_collectd_submit("dilution_of_precision", (gauge_t) gps_data_read.vdop, "vertical");
185   gps_collectd_submit("satellites", (gauge_t) gps_data_read.satellites, "gps");
186   DEBUG ("gps: hdop=%1.3f, vdop=%1.3f, sat=%02d.\n", 
187     gps_data_read.hdop,
188     gps_data_read.vdop,
189     gps_data_read.satellites
190   );
191   pthread_mutex_unlock (&data_mutex);
192   return (0);
193 }
194
195
196 /**
197  * Read configuration.
198  */
199 static int gps_collectd_config (const char *key, const char *value)
200 {
201   char *endptr = NULL;
202
203   if (strcasecmp (key, "Host") == 0)
204   {
205     if (gps_data_config.host != NULL)
206     {
207       free (gps_data_config.host);
208     }
209     gps_data_config.host = sstrdup (value);
210   }
211   if (strcasecmp (key, "Port") == 0)
212   {
213     if (gps_data_config.port != NULL)
214     {
215       free (gps_data_config.port);
216     }
217     gps_data_config.port = sstrdup (value);
218   }
219   if (strcasecmp (key, "Timeout") == 0)
220   {
221     gps_data_config.timeout = (int) ( strtod(value, &endptr) * 1000 );
222   DEBUG ("gps: will use pause %s - %d.\n", value, gps_data_config.timeout);
223   }
224   if (strcasecmp (key, "Pause") == 0)
225   {
226     gps_data_config.pause = (int) (strtod (value, &endptr));
227   DEBUG ("gps: will use pause %s -  %d.\n", value, gps_data_config.pause);
228   }
229   return (0);
230 }
231
232
233 /**
234  * Init.
235  */
236 static int gps_collectd_init (void)
237 {
238   int err = 0;
239
240   DEBUG ("gps: will use %s:%s, timeout %d ms, pause %d sec.\n", gps_data_config.host, gps_data_config.port, gps_data_config.timeout, gps_data_config.pause);
241
242   err = plugin_thread_create (&connector, NULL, gps_collectd_thread, NULL);
243
244   if (err != 0)
245   {
246     ERROR ("gps: pthread_create() failed.");
247     return (-1);
248   }
249
250   return (0);
251 }
252
253
254 /**
255  * Shutdown.
256  */
257 static int gps_collectd_shutdown (void)
258 {
259   if (connector != ((pthread_t) 0))
260   {
261     pthread_kill (connector, SIGTERM);
262     connector = (pthread_t) 0;
263   }
264
265   sfree (gps_data_config.port);
266   sfree (gps_data_config.host);
267
268   return (0);
269 }
270
271 /**
272  * Register the module.
273  */
274 void module_register (void)                                                                    
275 {
276   gps_data_config.host = sstrdup (GPS_DEFAULT_HOST);
277   gps_data_config.port = sstrdup (GPS_DEFAULT_PORT);
278   gps_data_config.timeout = GPS_DEFAULT_TIMEOUT;
279   gps_data_config.pause = GPS_DEFAULT_PAUSE;
280   gps_data_read.hdop = 0;
281   gps_data_read.vdop = 0;
282   gps_data_read.satellites = 0;
283
284   // Read the config params:
285   plugin_register_config ("gps", gps_collectd_config, config_keys, config_keys_num);
286   // Create the thread:
287   plugin_register_init ("gps", gps_collectd_init);
288   // Kill the thread and stop.
289   plugin_register_shutdown ("gps", gps_collectd_shutdown);
290   // Read plugin:
291   plugin_register_read ("gps", gps_collectd_read);
292 }
293