19913a1102954a2d74b9ded6d6067ef2112882d7
[apps/agl-service-navigation.git] / binding / navigation-api.c
1 /*
2  * Copyright (C) 2019 Konsulko Group
3  * Author: Matt Ranostay <matt.ranostay@konsulko.com>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/time.h>
24 #include <time.h>
25
26 #include <glib.h>
27 #include <glib/gstdio.h>
28 #include <gio/gio.h>
29 #include <json-c/json.h>
30
31 #define AFB_BINDING_VERSION 3
32 #include <afb/afb-binding.h>
33
34 #include "navigation-api.h"
35
36 struct navigation_state *navigation_get_userdata(afb_req_t request) {
37         afb_api_t api = afb_req_get_api(request);
38         return afb_api_get_userdata(api);
39 }
40
41 static afb_event_t get_event_from_value(struct navigation_state *ns,
42                         const char *value)
43 {
44         if (!g_strcmp0(value, "status"))
45                 return ns->status_event;
46
47         if (!g_strcmp0(value, "position"))
48                 return ns->position_event;
49
50         if (!g_strcmp0(value, "waypoints"))
51                 return ns->waypoints_event;
52
53         return NULL;
54 }
55
56 static json_object **get_storage_from_value(struct navigation_state *ns,
57                         const char *value)
58 {
59         if (!g_strcmp0(value, "status"))
60                 return &ns->status_storage;
61
62         if (!g_strcmp0(value, "position"))
63                 return &ns->position_storage;
64
65         if (!g_strcmp0(value, "waypoints"))
66                 return &ns->waypoints_storage;
67
68         return NULL;
69 }
70
71 static void navigation_subscribe_unsubscribe(afb_req_t request,
72                 gboolean unsub)
73 {
74         struct navigation_state *ns = navigation_get_userdata(request);
75         json_object *jresp = json_object_new_object();
76         const char *value;
77         afb_event_t event;
78         int rc;
79
80         value = afb_req_value(request, "value");
81         if (!value) {
82                 afb_req_fail_f(request, "failed", "Missing \"value\" event");
83                 return;
84         }
85
86         event = get_event_from_value(ns, value);
87         if (!event) {
88                 afb_req_fail_f(request, "failed", "Bad \"value\" event \"%s\"",
89                                 value);
90                 return;
91         }
92
93         if (!unsub) {
94                 json_object *storage;
95                 rc = afb_req_subscribe(request, event);
96
97                 g_rw_lock_reader_lock(&ns->rw_lock);
98                 storage = *get_storage_from_value(ns, value);
99                 if (storage) {
100                         // increment reference counter, and send out cached value
101                         json_object_get(storage);
102                         afb_event_push(event, storage);
103                 }
104                 g_rw_lock_reader_unlock(&ns->rw_lock);
105         } else {
106                 rc = afb_req_unsubscribe(request, event);
107         }
108         if (rc != 0) {
109                 afb_req_fail_f(request, "failed",
110                                         "%s error on \"value\" event \"%s\"",
111                                         !unsub ? "subscribe" : "unsubscribe",
112                                         value);
113                 return;
114         }
115
116         afb_req_success_f(request, jresp, "Navigation %s to event \"%s\"",
117                         !unsub ? "subscribed" : "unsubscribed",
118                         value);
119 }
120
121 static void subscribe(afb_req_t request)
122 {
123         navigation_subscribe_unsubscribe(request, FALSE);
124 }
125
126 static void unsubscribe(afb_req_t request)
127 {
128         navigation_subscribe_unsubscribe(request, TRUE);
129 }
130
131 static void broadcast(afb_req_t request, const char *name, gboolean cache)
132 {
133         struct navigation_state *ns = navigation_get_userdata(request);
134         afb_event_t event = get_event_from_value(ns, name);
135         json_object *jresp = afb_req_json(request);
136
137         if (cache) {
138                 json_object **storage = get_storage_from_value(ns, name);
139
140                 g_rw_lock_writer_lock(&ns->rw_lock);
141
142                 if (*storage)
143                         json_object_put(*storage);
144
145                 // increment reference for storage
146                 json_object_get(jresp);
147                 *storage = jresp;
148
149                 // increment reference for event
150                 json_object_get(jresp);
151                 afb_event_push(event, jresp);
152
153                 g_rw_lock_writer_unlock(&ns->rw_lock);
154
155                 return;
156         }
157
158         g_rw_lock_reader_lock(&ns->rw_lock);
159
160         // increment reference for event
161         json_object_get(jresp);
162         afb_event_push(event, jresp);
163
164         g_rw_lock_reader_unlock(&ns->rw_lock);
165 }
166
167 static void broadcast_status(afb_req_t request)
168 {
169         broadcast(request, "status", TRUE);
170
171         afb_req_success(request, NULL, "Broadcast status send");
172 }
173
174 static void broadcast_position(afb_req_t request)
175 {
176         const char *position = afb_req_value(request, "position");
177         gboolean cache = FALSE;
178
179         // only send out a car position event on subscribe
180         if (position && !g_strcmp0(position, "car"))
181                 cache = TRUE;
182
183         broadcast(request, "position", cache);
184
185         afb_req_success(request, NULL, "Broadcast position send");
186 }
187
188 static void broadcast_waypoints(afb_req_t request)
189 {
190         broadcast(request, "waypoints", TRUE);
191
192         afb_req_success(request, NULL, "Broadcast waypoints send");
193 }
194
195 static int init(afb_api_t api)
196 {
197         struct navigation_state *ns;
198
199         ns = g_try_malloc0(sizeof(*ns));
200         if (!ns) {
201                 AFB_ERROR("out of memory allocating navigation state");
202                 return -ENOMEM;
203         }
204
205         ns->status_event = afb_daemon_make_event("status");
206         ns->position_event = afb_daemon_make_event("position");
207         ns->waypoints_event = afb_daemon_make_event("waypoints");
208
209         if (!afb_event_is_valid(ns->status_event) ||
210             !afb_event_is_valid(ns->position_event) ||
211             !afb_event_is_valid(ns->waypoints_event)) {
212                 AFB_ERROR("Cannot create events");
213                 return -EINVAL;
214         }
215
216         afb_api_set_userdata(api, ns);
217
218         g_rw_lock_init(&ns->rw_lock);
219
220         return 0;
221 }
222
223 static const afb_verb_t binding_verbs[] = {
224         {
225                 .verb = "subscribe",
226                 .callback = subscribe,
227                 .info = "Subscribe to event"
228         }, {
229                 .verb = "unsubscribe",
230                 .callback = unsubscribe,
231                 .info = "Unsubscribe to event"
232         }, {
233                 .verb = "broadcast_status",
234                 .callback = broadcast_status,
235                 .info = "Allows clients to broadcast status events"
236         }, {
237                 .verb = "broadcast_position",
238                 .callback = broadcast_position,
239                 .info = "Broadcast out position event"
240         }, {
241                 .verb = "broadcast_waypoints",
242                 .callback = broadcast_waypoints,
243                 .info = "Broadcast out waypoint event"
244         },
245         {}
246 };
247
248 /*
249  * description of the binding for afb-daemon
250  */
251 const afb_binding_t afbBindingV3 = {
252         .api = "navigation",
253         .verbs = binding_verbs,
254         .init = init,
255 };