2 * Copyright (C) 2019 Konsulko Group
3 * Author: Matt Ranostay <matt.ranostay@konsulko.com>
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
27 #include <glib/gstdio.h>
29 #include <json-c/json.h>
31 #define AFB_BINDING_VERSION 3
32 #include <afb/afb-binding.h>
34 #include "navigation-api.h"
38 static const char *vshl_capabilities_events[] = {
44 struct navigation_state *navigation_get_userdata(void)
46 return afb_api_get_userdata(g_api);
49 static afb_event_t get_event_from_value(struct navigation_state *ns,
52 if (!g_strcmp0(value, "status"))
53 return ns->status_event;
55 if (!g_strcmp0(value, "position"))
56 return ns->position_event;
58 if (!g_strcmp0(value, "waypoints"))
59 return ns->waypoints_event;
64 static json_object **get_storage_from_value(struct navigation_state *ns,
67 if (!g_strcmp0(value, "status"))
68 return &ns->status_storage;
70 if (!g_strcmp0(value, "position"))
71 return &ns->position_storage;
73 if (!g_strcmp0(value, "waypoints"))
74 return &ns->waypoints_storage;
79 static void navigation_subscribe_unsubscribe(afb_req_t request,
82 struct navigation_state *ns = navigation_get_userdata();
83 json_object *jresp = json_object_new_object();
88 value = afb_req_value(request, "value");
90 afb_req_fail_f(request, "failed", "Missing \"value\" event");
94 event = get_event_from_value(ns, value);
96 afb_req_fail_f(request, "failed", "Bad \"value\" event \"%s\"",
102 json_object *storage;
103 rc = afb_req_subscribe(request, event);
105 g_rw_lock_reader_lock(&ns->rw_lock);
106 storage = *get_storage_from_value(ns, value);
108 // increment reference counter, and send out cached value
109 json_object_get(storage);
110 afb_event_push(event, storage);
112 g_rw_lock_reader_unlock(&ns->rw_lock);
114 rc = afb_req_unsubscribe(request, event);
117 afb_req_fail_f(request, "failed",
118 "%s error on \"value\" event \"%s\"",
119 !unsub ? "subscribe" : "unsubscribe",
124 afb_req_success_f(request, jresp, "Navigation %s to event \"%s\"",
125 !unsub ? "subscribed" : "unsubscribed",
129 static void subscribe(afb_req_t request)
131 navigation_subscribe_unsubscribe(request, FALSE);
134 static void unsubscribe(afb_req_t request)
136 navigation_subscribe_unsubscribe(request, TRUE);
139 static void broadcast(json_object *jresp, const char *name, gboolean cache)
141 struct navigation_state *ns = navigation_get_userdata();
142 afb_event_t event = get_event_from_value(ns, name);
143 json_object *tmp = NULL;
145 if (json_object_deep_copy(jresp, (json_object **) &tmp, NULL))
149 json_object **storage;
151 g_rw_lock_writer_lock(&ns->rw_lock);
153 storage = get_storage_from_value(ns, name);
156 json_object_put(*storage);
159 // increment reference for storage
160 json_object_get(tmp);
163 // increment reference for event
164 json_object_get(tmp);
165 afb_event_push(event, tmp);
167 g_rw_lock_writer_unlock(&ns->rw_lock);
172 afb_event_push(event, tmp);
175 static void broadcast_status(afb_req_t request)
177 json_object *jresp = afb_req_json(request);
178 broadcast(jresp, "status", TRUE);
180 afb_req_success(request, NULL, "Broadcast status send");
182 // NOTE: If the Alexa SDK API for pushing local navigation
183 // updates gets exposed, send update to vshl-capabilities
187 static void broadcast_position(afb_req_t request)
189 const char *position = afb_req_value(request, "position");
190 gboolean cache = FALSE;
192 // only send out a car position event on subscribe
193 if (position && !g_strcmp0(position, "car"))
196 json_object *jresp = afb_req_json(request);
197 broadcast(jresp, "position", cache);
199 afb_req_success(request, NULL, "Broadcast position send");
201 // NOTE: If the Alexa SDK API for pushing local navigation
202 // updates gets exposed, send update to vshl-capabilities
206 static void broadcast_waypoints(afb_req_t request)
208 json_object *jresp = afb_req_json(request);
209 broadcast(jresp, "waypoints", TRUE);
211 afb_req_success(request, NULL, "Broadcast waypoints send");
213 // NOTE: If the Alexa SDK API for pushing local navigation
214 // updates gets exposed, send update to vshl-capabilities
218 static void handle_setDestination_event(struct json_object *object)
220 json_object *jdest = NULL;
221 json_object_object_get_ex(object, "destination", &jdest);
223 AFB_WARNING("setDestination event missing destination element");
227 json_object *jcoord = NULL;
228 json_object_object_get_ex(jdest, "coordinate", &jcoord);
230 AFB_WARNING("setDestination event missing coordinate element");
234 json_object *jlat = NULL;
235 json_object_object_get_ex(jcoord, "latitudeInDegrees", &jlat);
239 double lat = json_object_get_double(jlat);
243 json_object *jlon = NULL;
244 json_object_object_get_ex(jcoord, "longitudeInDegrees", &jlon);
247 double lon = json_object_get_double(jlon);
251 json_object *jobj = json_object_new_object();
252 json_object *jpoints = json_object_new_array();
253 json_object *jpoint = json_object_new_object();
254 jlat = json_object_new_double(lat);
255 jlon = json_object_new_double(lon);
256 json_object_object_add(jpoint, "latitude", jlat);
257 json_object_object_add(jpoint, "longitude", jlon);
258 json_object_array_add(jpoints, jpoint);
259 json_object_object_add(jobj, "points", jpoints);
260 broadcast(jobj, "waypoints", TRUE);
263 static void handle_cancelNavigation_event(struct json_object *object)
265 json_object *jobj = json_object_new_object();
266 json_object *jstate = json_object_new_string("stop");
267 json_object_object_add(jobj, "state", jstate);
268 broadcast(jobj, "status", TRUE);
271 static void onevent(afb_api_t api, const char *event, struct json_object *object)
276 if(strcmp(event, "vshl-capabilities/setDestination") == 0) {
277 handle_setDestination_event(object);
278 } else if(strcmp(event, "vshl-capabilities/cancelNavigation") == 0) {
279 handle_cancelNavigation_event(object);
281 AFB_WARNING("Unhandled vshl-capabilities event");
285 static int init(afb_api_t api)
287 struct navigation_state *ns;
290 ns = g_try_malloc0(sizeof(*ns));
292 AFB_ERROR("out of memory allocating navigation state");
296 rc = afb_daemon_require_api("vshl-capabilities", 1);
298 const char **tmp = vshl_capabilities_events;
299 json_object *args = json_object_new_object();
300 json_object *actions = json_object_new_array();
303 json_object_array_add(actions, json_object_new_string(*tmp++));
305 json_object_object_add(args, "actions", actions);
306 if(json_object_array_length(actions)) {
307 rc = afb_api_call_sync(api, "vshl-capabilities", "navigation/subscribe",
308 args, NULL, NULL, NULL);
310 AFB_WARNING("afb_api_call_sync returned %d", rc);
312 json_object_put(args);
315 AFB_WARNING("unable to initialize vshl-capabilities binding");
318 ns->status_event = afb_daemon_make_event("status");
319 ns->position_event = afb_daemon_make_event("position");
320 ns->waypoints_event = afb_daemon_make_event("waypoints");
322 if (!afb_event_is_valid(ns->status_event) ||
323 !afb_event_is_valid(ns->position_event) ||
324 !afb_event_is_valid(ns->waypoints_event)) {
325 AFB_ERROR("Cannot create events");
329 afb_api_set_userdata(api, ns);
332 g_rw_lock_init(&ns->rw_lock);
337 static const afb_verb_t binding_verbs[] = {
340 .callback = subscribe,
341 .info = "Subscribe to event"
343 .verb = "unsubscribe",
344 .callback = unsubscribe,
345 .info = "Unsubscribe to event"
347 .verb = "broadcast_status",
348 .callback = broadcast_status,
349 .info = "Allows clients to broadcast status events"
351 .verb = "broadcast_position",
352 .callback = broadcast_position,
353 .info = "Broadcast out position event"
355 .verb = "broadcast_waypoints",
356 .callback = broadcast_waypoints,
357 .info = "Broadcast out waypoint event"
363 * description of the binding for afb-daemon
365 const afb_binding_t afbBindingV3 = {
367 .verbs = binding_verbs,