Add GPS controller. Not enabled yet
[azimuth] / src / position-publisher.c
1 /*
2  * position-publisher.c - Source for PositionPublisher
3  * Copyright (C) 2010 Guillaume Desmottes
4  * @author Guillaume Desmottes <gdesmott@gnome.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/dbus.h>
26 #include <telepathy-glib/interfaces.h>
27
28 #include <location/location-gps-device.h>
29 #include <location/location-gpsd-control.h>
30
31 #include "connection-watcher.h"
32 #include "position-publisher.h"
33
34 G_DEFINE_TYPE(PositionPublisher, position_publisher, G_TYPE_OBJECT)
35
36 /* Minimum time before 2 publishing (in seconds) */
37 #define PUBLISH_THROTTLE 10
38
39 /* private structure */
40 typedef struct _PositionPublisherPrivate PositionPublisherPrivate;
41
42 struct _PositionPublisherPrivate
43 {
44   ConnectionWatcher *watcher;
45   LocationGPSDControl *gps_control;
46   LocationGPSDevice *gps_device;
47   GSList *connections;
48   GHashTable *location;
49   /* If not 0, we are waiting before publishing again */
50   guint throttle_timeout;
51   /* TRUE if location has been modified while we were waiting */
52   gboolean modified;
53
54   gboolean dispose_has_run;
55 };
56
57 #define POSITION_PUBLISHER_GET_PRIVATE(o)     (G_TYPE_INSTANCE_GET_PRIVATE ((o), POSITION_PUBLISHER_TYPE, PositionPublisherPrivate))
58
59 static void conn_invalidated_cb (TpProxy *conn,
60     guint domain,
61     gint code,
62     gchar *message,
63     PositionPublisher *self);
64
65 static void publish_to_all (PositionPublisher *self);
66
67 static void
68 remove_connection (PositionPublisher *self,
69     TpConnection *conn)
70 {
71   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
72
73   g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (conn_invalidated_cb),
74       self);
75   priv->connections = g_slist_remove (priv->connections, conn);
76   g_object_unref (conn);
77 }
78
79 static void
80 set_location_cb (TpConnection *conn,
81     const GError *error,
82     gpointer user_data,
83     GObject *weak_object)
84 {
85   PositionPublisher *self = POSITION_PUBLISHER (weak_object);
86
87   if (error != NULL)
88     {
89       g_print ("SetLocation failed (%s): %s\n", tp_proxy_get_object_path (conn),
90           error->message);
91
92       if (error->code == TP_ERROR_NOT_IMPLEMENTED)
93         {
94           g_print ("remove connection\n");
95           remove_connection (self, conn);
96         }
97
98       return;
99     }
100
101   g_print ("SetLocation succeed (%s)\n", tp_proxy_get_object_path (conn));
102 }
103
104 static gboolean
105 publish_throttle_timeout_cb (gpointer data)
106 {
107   PositionPublisher *self = data;
108   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
109
110   priv->throttle_timeout = 0;
111
112   if (priv->modified)
113     {
114       publish_to_all (self);
115       priv->modified = FALSE;
116     }
117
118   return FALSE;
119 }
120
121 static void
122 publish_to_conn (PositionPublisher *self,
123     TpConnection *conn)
124 {
125   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
126
127   if (priv->location == NULL)
128     return;
129
130   if (priv->throttle_timeout != 0)
131     /* We are waiting */
132     return;
133
134   tp_cli_connection_interface_location_call_set_location (conn, -1,
135       priv->location, set_location_cb, NULL, NULL, G_OBJECT (self));
136
137   /* We won't publish during the next PUBLISH_THROTTLE seconds */
138   priv->throttle_timeout = g_timeout_add_seconds (PUBLISH_THROTTLE,
139       publish_throttle_timeout_cb, self);
140 }
141
142 static void
143 publish_to_all (PositionPublisher *self)
144 {
145   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
146   GSList *l;
147
148   for (l = priv->connections; l != NULL; l = g_slist_next (l))
149     {
150       TpConnection *conn = l->data;
151
152       publish_to_conn (self, conn);
153     }
154 }
155
156 static void
157 update_position (PositionPublisher *self,
158     gdouble lat,
159     gdouble lon,
160     gdouble alt,
161     gdouble accuracy)
162 {
163   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
164
165   g_print ("update position: lat: %f  lon:  %f  alt: %f accuracy: %f\n",
166       lat, lon, alt, accuracy);
167
168   if (priv->location != NULL)
169     g_hash_table_unref (priv->location);
170
171   priv->location = tp_asv_new (
172       "timestamp", G_TYPE_INT64, (gint64) time (NULL),
173       "lat", G_TYPE_DOUBLE, lat,
174       "lon", G_TYPE_DOUBLE, lon,
175       "alt", G_TYPE_DOUBLE, alt,
176       "accuracy", G_TYPE_DOUBLE, accuracy,
177       NULL);
178
179   priv->modified = TRUE;
180
181   publish_to_all (self);
182 }
183
184 static void
185 location_changed_cb (LocationGPSDevice *device,
186     PositionPublisher *self)
187 {
188   if (device == NULL)
189     return;
190
191   if (device->fix == NULL)
192     return;
193
194   if (!(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET))
195     return;
196
197   update_position (self, device->fix->latitude, device->fix->longitude,
198       device->fix->altitude, device->fix->eph / 100.0);
199 }
200
201 static void
202 conn_invalidated_cb (TpProxy *conn,
203     guint domain,
204     gint code,
205     gchar *message,
206     PositionPublisher *self)
207 {
208   g_print ("connection %s invalidated; removing\n", tp_proxy_get_object_path (
209         conn));
210
211   remove_connection (self, TP_CONNECTION (conn));
212 }
213
214 static void
215 connection_added_cb (ConnectionWatcher *watcher,
216     TpConnection *conn,
217     PositionPublisher *self)
218 {
219   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
220
221   if (g_slist_find (priv->connections, conn) != NULL)
222     return;
223
224   if (!tp_proxy_has_interface_by_id (conn,
225         TP_IFACE_QUARK_CONNECTION_INTERFACE_LOCATION))
226     return;
227
228   priv->connections = g_slist_prepend (priv->connections, g_object_ref (conn));
229
230   g_signal_connect (conn, "invalidated",
231       G_CALLBACK (conn_invalidated_cb), self);
232
233   publish_to_conn (self, conn);
234 }
235
236 static void
237 position_publisher_init (PositionPublisher *obj)
238 {
239   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (obj);
240
241   priv->watcher = connection_watcher_new ();
242
243   g_signal_connect (priv->watcher, "connection-added",
244       G_CALLBACK (connection_added_cb), obj);
245
246   priv->gps_control = location_gpsd_control_get_default();
247
248   g_object_set (G_OBJECT(priv->gps_control),
249     "preferred-method", LOCATION_METHOD_USER_SELECTED,
250     "preferred-interval", LOCATION_INTERVAL_120S,
251     NULL);
252
253   priv->gps_device = g_object_new (LOCATION_TYPE_GPS_DEVICE, NULL);
254
255   g_signal_connect (priv->gps_device, "changed",
256       G_CALLBACK (location_changed_cb), obj);
257
258   priv->connections = NULL;
259 }
260
261 static void
262 position_publisher_constructed (GObject *object)
263 {
264   PositionPublisher *self = POSITION_PUBLISHER (object);
265   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
266
267   connection_watcher_start (priv->watcher);
268
269   if (G_OBJECT_CLASS (position_publisher_parent_class)->constructed)
270     G_OBJECT_CLASS (position_publisher_parent_class)->constructed (object);
271 }
272
273 static void position_publisher_dispose (GObject *object);
274 static void position_publisher_finalize (GObject *object);
275
276 static void
277 position_publisher_class_init (PositionPublisherClass *position_publisher_class)
278 {
279   GObjectClass *object_class = G_OBJECT_CLASS (position_publisher_class);
280
281   g_type_class_add_private (position_publisher_class, sizeof (PositionPublisherPrivate));
282
283   object_class->dispose = position_publisher_dispose;
284   object_class->finalize = position_publisher_finalize;
285
286   object_class->constructed = position_publisher_constructed;
287 }
288
289 void
290 position_publisher_dispose (GObject *object)
291 {
292   PositionPublisher *self = POSITION_PUBLISHER (object);
293   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
294   GSList *l;
295
296   if (priv->dispose_has_run)
297     return;
298
299   priv->dispose_has_run = TRUE;
300
301   g_object_unref (priv->watcher);
302   g_object_unref (priv->gps_control);
303   g_object_unref (priv->gps_device);
304
305   for (l = priv->connections; l != NULL; l = g_slist_next (l))
306     {
307       g_object_unref (l->data);
308     }
309
310   g_hash_table_unref (priv->location);
311
312   if (priv->throttle_timeout != 0)
313     g_source_remove (priv->throttle_timeout);
314
315   if (G_OBJECT_CLASS (position_publisher_parent_class)->dispose)
316     G_OBJECT_CLASS (position_publisher_parent_class)->dispose (object);
317 }
318
319 void
320 position_publisher_finalize (GObject *object)
321 {
322   PositionPublisher *self = POSITION_PUBLISHER (object);
323   PositionPublisherPrivate *priv = POSITION_PUBLISHER_GET_PRIVATE (self);
324
325   g_slist_free (priv->connections);
326
327   G_OBJECT_CLASS (position_publisher_parent_class)->finalize (object);
328 }
329
330 PositionPublisher *
331 position_publisher_new (void)
332 {
333   return g_object_new (POSITION_PUBLISHER_TYPE,
334       NULL);
335 }