1 |
/** @file vehicle_gypsy.c
|
2 |
* @brief gypsy uses dbus signals
|
3 |
*
|
4 |
* Navit, a modular navigation system.
|
5 |
* Copyright (C) 2005-2008 Navit Team
|
6 |
*
|
7 |
* This program is free software; you can redistribute it and/or
|
8 |
* modify it under the terms of the GNU General Public License
|
9 |
* version 2 as published by the Free Software Foundation.
|
10 |
*
|
11 |
* This program is distributed in the hope that it will be useful,
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14 |
* GNU General Public License for more details.
|
15 |
*
|
16 |
* You should have received a copy of the GNU General Public License
|
17 |
* along with this program; if not, write to the
|
18 |
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
19 |
* Boston, MA 02110-1301, USA.
|
20 |
*
|
21 |
* @Author Tim Niemeyer <reddog@mastersword.de>
|
22 |
* @date 2008-2009
|
23 |
*/
|
24 |
|
25 |
#include <config.h>
|
26 |
#include <gypsy/gypsy-device.h>
|
27 |
#include <gypsy/gypsy-control.h>
|
28 |
#include <gypsy/gypsy-course.h>
|
29 |
#include <gypsy/gypsy-position.h>
|
30 |
#include <gypsy/gypsy-satellite.h>
|
31 |
#ifdef USE_BINDING_DBUS
|
32 |
#include <dbus/dbus.h>
|
33 |
#include <dbus/dbus-glib.h>
|
34 |
#include <dbus/dbus-glib-lowlevel.h>
|
35 |
#endif
|
36 |
#ifdef HAVE_UNISTD_H
|
37 |
#include <sys/types.h>
|
38 |
#include <unistd.h>
|
39 |
#endif
|
40 |
#include <string.h>
|
41 |
#include <glib.h>
|
42 |
#include <math.h>
|
43 |
#include "debug.h"
|
44 |
#include "callback.h"
|
45 |
#include "plugin.h"
|
46 |
#include "coord.h"
|
47 |
#include "item.h"
|
48 |
#include "vehicle.h"
|
49 |
|
50 |
static struct vehicle_priv {
|
51 |
char *source;
|
52 |
GypsyControl *control;
|
53 |
GypsyPosition *position;
|
54 |
GypsyDevice *device;
|
55 |
GypsyCourse *course;
|
56 |
GypsySatellite *satellite;
|
57 |
char *path;
|
58 |
struct callback_list *cbl;
|
59 |
guint retry_interval;
|
60 |
struct coord_geo geo;
|
61 |
double speed;
|
62 |
double direction;
|
63 |
double height;
|
64 |
int fix_type;
|
65 |
time_t fix_time;
|
66 |
char fixiso8601[128];
|
67 |
int sats;
|
68 |
int sats_used;
|
69 |
guint retry_timer;
|
70 |
struct attr ** attrs;
|
71 |
int have_cords;
|
72 |
} *vehicle_last;
|
73 |
|
74 |
#define DEFAULT_RETRY_INTERVAL 10 // seconds
|
75 |
#define MIN_RETRY_INTERVAL 1 // seconds
|
76 |
|
77 |
/**
|
78 |
* @brief When the fixstatus has changed, this function get called
|
79 |
*
|
80 |
* fixstatus can be one of this:
|
81 |
* GYPSY_DEVICE_FIX_STATUS_INVALID = 0
|
82 |
* GYPSY_DEVICE_FIX_STATUS_NONE = 1
|
83 |
* GYPSY_DEVICE_FIX_STATUS_2D = 2
|
84 |
* GYPSY_DEVICE_FIX_STATUS_3D = 3
|
85 |
*
|
86 |
* Anytime this functions get called, we have to call the global
|
87 |
* callback.
|
88 |
*
|
89 |
* @param device The GypsyDevice
|
90 |
* @param fixstatus The fisstatus 0, 1, 2 or 3
|
91 |
* @param userdata
|
92 |
* @returns nothing
|
93 |
*/
|
94 |
static void
|
95 |
vehicle_gypsy_fixstatus_changed(GypsyDevice *device,
|
96 |
gint fixstatus,
|
97 |
gpointer userdata)
|
98 |
{
|
99 |
struct vehicle_priv *priv = vehicle_last;
|
100 |
|
101 |
if (fixstatus==GYPSY_DEVICE_FIX_STATUS_3D)
|
102 |
priv->fix_type = 3;
|
103 |
else if (fixstatus==GYPSY_DEVICE_FIX_STATUS_2D)
|
104 |
priv->fix_type = 1;
|
105 |
else
|
106 |
priv->fix_type = 0;
|
107 |
|
108 |
callback_list_call_attr_0(priv->cbl, attr_position_coord_geo);
|
109 |
}
|
110 |
|
111 |
/**
|
112 |
* @brief When the position has changed, this function get called
|
113 |
*
|
114 |
* The fields_set can hold:
|
115 |
* GYPSY_POSITION_FIELDS_NONE = 1 << 0,
|
116 |
* GYPSY_POSITION_FIELDS_LATITUDE = 1 << 1,
|
117 |
* GYPSY_POSITION_FIELDS_LONGITUDE = 1 << 2,
|
118 |
* GYPSY_POSITION_FIELDS_ALTITUDE = 1 << 3
|
119 |
*
|
120 |
* If we get any new information, we have to call the global
|
121 |
* callback.
|
122 |
*
|
123 |
* @param position The GypsyPosition
|
124 |
* @param fields_set Bitmask indicating what field was set
|
125 |
* @param timestamp the time since Unix Epoch
|
126 |
* @param latitude
|
127 |
* @param longitude
|
128 |
* @param altitude
|
129 |
* @param userdata
|
130 |
* @returns nothing
|
131 |
*/
|
132 |
static void
|
133 |
vehicle_gypsy_position_changed(GypsyPosition *position,
|
134 |
GypsyPositionFields fields_set, int timestamp,
|
135 |
double latitude, double longitude, double altitude,
|
136 |
gpointer userdata)
|
137 |
{
|
138 |
struct vehicle_priv *priv = vehicle_last;
|
139 |
int cb = FALSE;
|
140 |
|
141 |
if (timestamp > 0)
|
142 |
priv->fix_time = timestamp;
|
143 |
|
144 |
if (fields_set & GYPSY_POSITION_FIELDS_LATITUDE)
|
145 |
{
|
146 |
cb = TRUE;
|
147 |
priv->geo.lat = latitude;
|
148 |
}
|
149 |
if (fields_set & GYPSY_POSITION_FIELDS_LONGITUDE)
|
150 |
{
|
151 |
cb = TRUE;
|
152 |
priv->geo.lng = longitude;
|
153 |
}
|
154 |
if (fields_set & GYPSY_POSITION_FIELDS_ALTITUDE)
|
155 |
{
|
156 |
cb = TRUE;
|
157 |
priv->height = altitude;
|
158 |
}
|
159 |
|
160 |
if (cb)
|
161 |
{
|
162 |
priv->have_cords = 1;
|
163 |
callback_list_call_attr_0(priv->cbl, attr_position_coord_geo);
|
164 |
}
|
165 |
}
|
166 |
|
167 |
/**
|
168 |
* @brief Everytime any Sat-Details are changed, this function get called
|
169 |
*
|
170 |
* Going through all Sats, counting those wich are in use.
|
171 |
*
|
172 |
* Anytime this functions get called, we have to call the global
|
173 |
* callback.
|
174 |
*
|
175 |
* @param satellite The GypsySatellite
|
176 |
* @param satellites An GPtrArray wich hold information of all sats
|
177 |
* @param userdata
|
178 |
* @returns nothing
|
179 |
*/
|
180 |
static void
|
181 |
vehicle_gypsy_satellite_changed(GypsySatellite *satellite,
|
182 |
GPtrArray *satellites,
|
183 |
gpointer userdata)
|
184 |
{
|
185 |
struct vehicle_priv *priv = vehicle_last;
|
186 |
|
187 |
int i, sats, used=0;
|
188 |
|
189 |
sats = satellites->len;
|
190 |
for (i = 0; i < sats; i++) {
|
191 |
GypsySatelliteDetails *details = satellites->pdata[i];
|
192 |
if (details->in_use)
|
193 |
used++;
|
194 |
}
|
195 |
|
196 |
priv->sats_used = used;
|
197 |
priv->sats = sats;
|
198 |
|
199 |
callback_list_call_attr_0(priv->cbl, attr_position_coord_geo);
|
200 |
}
|
201 |
|
202 |
/**
|
203 |
* @brief When the course or speed has changed, this function get called
|
204 |
*
|
205 |
* Only speed and direction are used!
|
206 |
*
|
207 |
* If we get any new information, we have to call the global
|
208 |
* callback.
|
209 |
*
|
210 |
* @param course The GypsyCourse
|
211 |
* @param fields Bitmask indicating what field was set
|
212 |
* @param timestamp the time since Unix Epoch
|
213 |
* @param speed
|
214 |
* @param direction
|
215 |
* @param climb
|
216 |
* @param userdata
|
217 |
* @returns nothing
|
218 |
*/
|
219 |
static void
|
220 |
vehicle_gypsy_course_changed (GypsyCourse *course,
|
221 |
GypsyCourseFields fields,
|
222 |
int timestamp,
|
223 |
double speed,
|
224 |
double direction,
|
225 |
double climb,
|
226 |
gpointer userdata)
|
227 |
{
|
228 |
struct vehicle_priv *priv = vehicle_last;
|
229 |
int cb = FALSE;
|
230 |
|
231 |
if (fields & GYPSY_COURSE_FIELDS_SPEED)
|
232 |
{
|
233 |
priv->speed = speed;
|
234 |
cb = TRUE;
|
235 |
}
|
236 |
if (fields & GYPSY_COURSE_FIELDS_DIRECTION)
|
237 |
{
|
238 |
priv->direction = direction;
|
239 |
cb = TRUE;
|
240 |
}
|
241 |
|
242 |
if (cb)
|
243 |
callback_list_call_attr_0(priv->cbl, attr_position_coord_geo);
|
244 |
}
|
245 |
|
246 |
/**
|
247 |
* @brief Attempt to open the gypsy device.
|
248 |
*
|
249 |
* Tells gypsy wich functions to call when anything occours.
|
250 |
*
|
251 |
* @param data
|
252 |
* @returns TRUE to try again; FALSE if retry not required
|
253 |
*/
|
254 |
static gboolean
|
255 |
vehicle_gypsy_try_open(gpointer *data)
|
256 |
{
|
257 |
struct vehicle_priv *priv = (struct vehicle_priv *)data;
|
258 |
char *source = g_strdup(priv->source);
|
259 |
|
260 |
GError *error = NULL;
|
261 |
|
262 |
g_type_init();
|
263 |
priv->control = gypsy_control_get_default();
|
264 |
priv->path = gypsy_control_create(priv->control, source+8, &error);
|
265 |
if (priv->path == NULL) {
|
266 |
g_warning ("Error creating gypsy conrtol path for %s: %s", source+8, error->message);
|
267 |
return TRUE;
|
268 |
}
|
269 |
|
270 |
priv->position = gypsy_position_new(priv->path);
|
271 |
g_signal_connect(priv->position, "position-changed", G_CALLBACK (vehicle_gypsy_position_changed), NULL);
|
272 |
|
273 |
priv->satellite = gypsy_satellite_new(priv->path);
|
274 |
g_signal_connect(priv->satellite, "satellites-changed", G_CALLBACK (vehicle_gypsy_satellite_changed), NULL);
|
275 |
|
276 |
priv->course = gypsy_course_new(priv->path);
|
277 |
g_signal_connect(priv->course, "course-changed", G_CALLBACK (vehicle_gypsy_course_changed), NULL);
|
278 |
|
279 |
priv->device = gypsy_device_new(priv->path);
|
280 |
g_signal_connect(priv->device, "fix-status-changed", G_CALLBACK (vehicle_gypsy_fixstatus_changed), NULL);
|
281 |
|
282 |
gypsy_device_start(priv->device, &error);
|
283 |
if (error != NULL) {
|
284 |
g_warning ("Error starting gypsy for %s: %s", source+8, error->message);
|
285 |
return TRUE;
|
286 |
}
|
287 |
|
288 |
vehicle_last = priv;
|
289 |
dbg(0,"gypsy connected to %d\n", source+8);
|
290 |
g_free(source);
|
291 |
return FALSE;
|
292 |
}
|
293 |
|
294 |
/**
|
295 |
* @brief Open a connection to gypsy. Will re-try the connection if it fails
|
296 |
*
|
297 |
* @param priv
|
298 |
* @returns nothing
|
299 |
*/
|
300 |
static void
|
301 |
vehicle_gypsy_open(struct vehicle_priv *priv)
|
302 |
{
|
303 |
priv->retry_timer=0;
|
304 |
if (vehicle_gypsy_try_open((gpointer *)priv)) {
|
305 |
priv->retry_timer = g_timeout_add(priv->retry_interval*1000, (GSourceFunc)vehicle_gypsy_try_open, (gpointer *)priv);
|
306 |
}
|
307 |
}
|
308 |
|
309 |
/**
|
310 |
* @brief Stop retry timer; Free alloced memory
|
311 |
*
|
312 |
* @param priv
|
313 |
* @returns nothing
|
314 |
*/
|
315 |
static void
|
316 |
vehicle_gypsy_close(struct vehicle_priv *priv)
|
317 |
{
|
318 |
if (priv->retry_timer) {
|
319 |
g_source_remove(priv->retry_timer);
|
320 |
priv->retry_timer=0;
|
321 |
}
|
322 |
if (priv->path)
|
323 |
g_free(priv->path);
|
324 |
|
325 |
if (priv->position)
|
326 |
g_free(priv->position);
|
327 |
|
328 |
if (priv->satellite)
|
329 |
g_free(priv->satellite);
|
330 |
|
331 |
if (priv->course)
|
332 |
g_free(priv->course);
|
333 |
|
334 |
if (priv->device)
|
335 |
g_free(priv->device);
|
336 |
|
337 |
if (priv->control)
|
338 |
g_object_unref(G_OBJECT (priv->control));
|
339 |
}
|
340 |
|
341 |
/**
|
342 |
* @brief Free the gypsy_vehicle
|
343 |
*
|
344 |
* @param priv
|
345 |
* @returns nothing
|
346 |
*/
|
347 |
static void
|
348 |
vehicle_gypsy_destroy(struct vehicle_priv *priv)
|
349 |
{
|
350 |
vehicle_gypsy_close(priv);
|
351 |
if (priv->source)
|
352 |
g_free(priv->source);
|
353 |
g_free(priv);
|
354 |
}
|
355 |
|
356 |
/**
|
357 |
* @brief Provide the outside with information
|
358 |
*
|
359 |
* @param priv
|
360 |
* @param type TODO: What can this be?
|
361 |
* @param attr
|
362 |
* @returns true/false
|
363 |
*/
|
364 |
static int
|
365 |
vehicle_gypsy_position_attr_get(struct vehicle_priv *priv,
|
366 |
enum attr_type type, struct attr *attr)
|
367 |
{
|
368 |
struct attr * active=NULL;
|
369 |
switch (type) {
|
370 |
case attr_position_fix_type:
|
371 |
attr->u.num = priv->fix_type;
|
372 |
break;
|
373 |
case attr_position_height:
|
374 |
attr->u.numd = &priv->height;
|
375 |
break;
|
376 |
case attr_position_speed:
|
377 |
attr->u.numd = &priv->speed;
|
378 |
break;
|
379 |
case attr_position_direction:
|
380 |
attr->u.numd = &priv->direction;
|
381 |
break;
|
382 |
case attr_position_qual:
|
383 |
attr->u.num = priv->sats;
|
384 |
break;
|
385 |
case attr_position_sats_used:
|
386 |
attr->u.num = priv->sats_used;
|
387 |
break;
|
388 |
case attr_position_coord_geo:
|
389 |
attr->u.coord_geo = &priv->geo;
|
390 |
if (!priv->have_cords)
|
391 |
return 0;
|
392 |
break;
|
393 |
case attr_position_time_iso8601:
|
394 |
{
|
395 |
struct tm tm;
|
396 |
if (!priv->fix_time)
|
397 |
return 0;
|
398 |
if (gmtime_r(&priv->fix_time, &tm)) {
|
399 |
strftime(priv->fixiso8601, sizeof(priv->fixiso8601),
|
400 |
"%Y-%m-%dT%TZ", &tm);
|
401 |
attr->u.str=priv->fixiso8601;
|
402 |
} else
|
403 |
return 0;
|
404 |
}
|
405 |
case attr_active:
|
406 |
active = attr_search(priv->attrs,NULL,attr_active);
|
407 |
if(active != NULL && active->u.num == 1)
|
408 |
return 1;
|
409 |
else
|
410 |
return 0;
|
411 |
break;
|
412 |
|
413 |
default:
|
414 |
return 0;
|
415 |
}
|
416 |
attr->type = type;
|
417 |
return 1;
|
418 |
}
|
419 |
|
420 |
struct vehicle_methods vehicle_gypsy_methods = {
|
421 |
vehicle_gypsy_destroy,
|
422 |
vehicle_gypsy_position_attr_get,
|
423 |
};
|
424 |
|
425 |
/**
|
426 |
* @brief Create gypsy_vehicle
|
427 |
*
|
428 |
* @param meth
|
429 |
* @param cbl
|
430 |
* @param attrs
|
431 |
* @returns vehicle_priv
|
432 |
*/
|
433 |
static struct vehicle_priv *
|
434 |
vehicle_gypsy_new_gypsy(struct vehicle_methods *meth,
|
435 |
struct callback_list *cbl,
|
436 |
struct attr **attrs)
|
437 |
{
|
438 |
struct vehicle_priv *ret;
|
439 |
struct attr *source, *retry_int;
|
440 |
|
441 |
#if defined(USE_BINDING_DBUS) && defined(HAVE_UNISTD_H)
|
442 |
DBusConnection *conn;
|
443 |
DBusMessage *message;
|
444 |
dbus_uint32_t serial,pid=getpid();
|
445 |
struct attr *destination,*path,*interface,*method;
|
446 |
|
447 |
destination=attr_search(attrs, NULL, attr_dbus_destination);
|
448 |
path=attr_search(attrs, NULL, attr_dbus_path);
|
449 |
interface=attr_search(attrs, NULL, attr_dbus_interface);
|
450 |
method=attr_search(attrs, NULL, attr_dbus_method);
|
451 |
if (destination && path && interface && method) {
|
452 |
conn=dbus_bus_get(DBUS_BUS_SESSION, NULL);
|
453 |
if (conn) {
|
454 |
message=dbus_message_new_method_call(destination->u.str,path->u.str,interface->u.str,method->u.str);
|
455 |
dbus_message_append_args(message, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID);
|
456 |
dbus_connection_send(conn, message, &serial);
|
457 |
dbus_message_unref(message);
|
458 |
dbus_connection_unref(conn);
|
459 |
} else {
|
460 |
dbg(0,"failed to connect to session bus\n");
|
461 |
}
|
462 |
}
|
463 |
#endif
|
464 |
dbg(1, "enter\n");
|
465 |
source = attr_search(attrs, NULL, attr_source);
|
466 |
ret = g_new0(struct vehicle_priv, 1);
|
467 |
ret->have_cords = 0;
|
468 |
ret->source = g_strdup(source->u.str);
|
469 |
ret->attrs = attrs;
|
470 |
retry_int = attr_search(attrs, NULL, attr_retry_interval);
|
471 |
if (retry_int) {
|
472 |
ret->retry_interval = retry_int->u.num;
|
473 |
if (ret->retry_interval < MIN_RETRY_INTERVAL) {
|
474 |
dbg(0, "Retry interval %d too small, setting to %d\n", ret->retry_interval, MIN_RETRY_INTERVAL);
|
475 |
ret->retry_interval = MIN_RETRY_INTERVAL;
|
476 |
}
|
477 |
} else {
|
478 |
dbg(0, "Retry interval not defined, setting to %d\n", DEFAULT_RETRY_INTERVAL);
|
479 |
ret->retry_interval = DEFAULT_RETRY_INTERVAL;
|
480 |
}
|
481 |
ret->cbl = cbl;
|
482 |
*meth = vehicle_gypsy_methods;
|
483 |
vehicle_gypsy_open(ret);
|
484 |
return ret;
|
485 |
}
|
486 |
|
487 |
/**
|
488 |
* @brief register vehicle_gypsy
|
489 |
*
|
490 |
* @returns nothing
|
491 |
*/
|
492 |
void
|
493 |
plugin_init(void)
|
494 |
{
|
495 |
dbg(1, "enter\n");
|
496 |
plugin_register_vehicle_type("gypsy", vehicle_gypsy_new_gypsy);
|
497 |
}
|