diff options
author | Matt Ranostay <matt.ranostay@konsulko.com> | 2017-08-25 09:54:17 -0700 |
---|---|---|
committer | Matt Ranostay <matt.ranostay@konsulko.com> | 2017-08-28 10:50:57 -0700 |
commit | 344f115511abc18c0156c2784c2fcc50e0d8306d (patch) | |
tree | dd34b988e31bb3d2d53e2199b022163e60dcfda9 /binding/afm-gps-binding.c | |
parent | 94af7aa13b923fb96c152c4cb4251fe415271a91 (diff) |
binding: gps: rewrite binding to use libgps
Rewrite gps binding to use libgps native functionality to
communicate with gps over SOCK_STREAM socket.
Bug-AGL: SPEC-844
Change-Id: I78d2d14900d69b542e8bd456df577ee9ecdcb103
Signed-off-by: Matt Ranostay <matt.ranostay@konsulko.com>
Diffstat (limited to 'binding/afm-gps-binding.c')
-rw-r--r-- | binding/afm-gps-binding.c | 1066 |
1 files changed, 95 insertions, 971 deletions
diff --git a/binding/afm-gps-binding.c b/binding/afm-gps-binding.c index 080f1e5..e7cbf8f 100644 --- a/binding/afm-gps-binding.c +++ b/binding/afm-gps-binding.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2016 "IoT.bzh" - * Author José Bollo <jose.bollo@iot.bzh> + * Copyright (C) 2017 Konsulko Group + * Author: Matt Ranostay <matt.ranostay@konsulko.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,1036 +16,160 @@ */ #define _GNU_SOURCE +#include <errno.h> #include <stdio.h> #include <string.h> +#include <stdlib.h> #include <unistd.h> -#include <errno.h> -#include <netdb.h> -#include <fcntl.h> -#include <math.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/socket.h> - +#include <gps.h> +#include <pthread.h> #include <json-c/json.h> -#include <systemd/sd-event.h> - +#define AFB_BINDING_VERSION 2 #include <afb/afb-binding.h> -#include <afb/afb-service-itf.h> - -#define NAUTICAL_MILE_IN_METER 1852 -#define MILE_IN_METER 1609.344 -#define KNOT_TO_METER_PER_SECOND 0.5144444444 /* 1852 / 3600 */ -#define METER_PER_SECOND_TO_KNOT 1.943844492 /* 3600 / 1852 */ -#define METER_PER_SECOND_TO_KILOMETER_PER_HOUR 3.6 /* 3600 / 1000 */ -#define METER_PER_SECOND_TO_MILE_PER_HOUR 2.236936292 /* 3600 / 1609.344 */ - -#define DEFAULT_PERIOD 2000 /* 2 seconds */ - -/* - * references: - * - * https://www.w3.org/TR/geolocation-API/ - * http://www.gpsinformation.org/dale/nmea.htm - */ -/* flags for recording what field is set */ -struct flags { - unsigned time: 1; - unsigned latitude: 1; - unsigned longitude: 1; - unsigned altitude: 1; - unsigned speed: 1; - unsigned track: 1; -}; +static struct gps_data_t data; +static struct afb_event location_event; -/* the gps data converted */ -struct gps { - struct flags set; +static pthread_t thread; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - uint32_t time; - double latitude; - double longitude; - double altitude; - double speed; - double track; -}; +#define MSECS_TO_USECS(x) (x * 1000) -/* - * the type of position expected - * - * here, this type is mainly the selection of units - */ -enum type { - type_wgs84, /* longitude, latitude, track: degre, altitude: m, speed: m/s */ - type_dms_kmh, /* longitude, latitude: degre°minute'second.xxx"X, track: degre, altitude: m, speed: km/h */ - type_dms_mph, /* longitude, latitude: degre°minute'second.xxx"X, track: degre, altitude: m, speed: mph */ - type_dms_kn, /* longitude, latitude: degre°minute'second.xxx"X, track: degre, altitude: m, speed: kn */ - type_COUNT, - type_DEFAULT = type_wgs84, - type_INVALID = -1 -}; - -struct event; - -/* - * for each expected period - */ -struct period { - struct period *next; /* link to the next other period */ - struct event *events; /* events for the period */ - uint32_t period; /* value of the period in ms */ - uint32_t last; /* last update of the period */ -}; - -/* - * each generated event - */ -struct event { - struct event *next; /* link for the same period */ - const char *name; /* name of the event */ - struct afb_event event; /* the event for the binder */ - enum type type; /* the type of data expected */ - int id; /* id of the event for unsubscribe */ -}; - -/* - * names of the types - */ -static const char * const type_NAMES[type_COUNT] = { - "WGS84", - "DMS.km/h", - "DMS.mph", - "DMS.kn" -}; - -/* - * the interface to afb-daemon - */ -const struct afb_binding_interface *afbitf; - -/* - * records the raw frames - */ -static struct gps frames[10]; /* a short memory for further computation if needed */ -static int frameidx; /* index of the last frame (frames are in the reverse order) */ -static int newframes; /* boolean indication of wether new frames are availables */ - -/* - * records the JSON object for sending positions - */ -static struct json_object *time_ms; /* time as double in millisecond */ -static struct json_object *latitude_wgs; /* latitude as double in degree */ -static struct json_object *longitude_wgs; /* longitude as double in degree */ -static struct json_object *latitude_dms; /* latitude as string in d°m's.s"X */ -static struct json_object *longitude_dms; /* longitude as string in d°m's.s"X */ -static struct json_object *altitude_m; /* altitude as double in meter */ -static struct json_object *speed_ms; /* speed as double in m/s */ -static struct json_object *speed_kmh; /* speed as double in km/h */ -static struct json_object *speed_mph; /* speed as double in mph */ -static struct json_object *speed_kn; /* speed as double in kn */ -static struct json_object *track_d; /* heading track as double in degree */ - -static struct json_object *positions[type_COUNT]; /* computed positions by type */ - -/* head of the list of periods */ -static struct period *list_of_periods; - -/***************************************************************************************/ -/***************************************************************************************/ -/** **/ -/** **/ -/** SECTION: FORMATING JSON POSITIONS **/ -/** **/ -/** **/ -/***************************************************************************************/ -/***************************************************************************************/ -/* - * Creates the JSON representation for Degree Minute Second representation of coordinates - */ -static struct json_object *new_dms(double a, int islat) +static json_object *populate_json_data(json_object *jresp) { - char buffer[50], pos; - double D, M; + json_object *value = NULL; - if (islat) { - if (a >= 0) - pos = 'N'; - else { - a = -a; - pos = 'S'; - } - } else { - if (a <= 180) - pos = 'E'; - else { - a = 360 - a; - pos = 'W'; - } + if (data.fix.mode != MODE_3D) { + json_object_put(jresp); + return NULL; } - D = floor(a); - a = (a - D) * 60; - M = floor(a); - a = (a - M) * 60; - sprintf(buffer, "%d°%d'%.3f\"%c", (int)D, (int)M, a, pos); - return json_object_new_string(buffer); -} - -/* - * adds the value (with reference count increment) if not null - */ -static void addif(struct json_object *obj, const char *name, struct json_object *val) -{ - if (val != NULL) - json_object_object_add(obj, name, json_object_get(val)); -} - -/* - * release the object (put) and reset the pointer to null - */ -static void clear(struct json_object **obj) -{ - json_object_put(*obj); - *obj = NULL; -} - -/* - * get the last/current position of type - */ -static struct json_object *position(enum type type) -{ - struct json_object *result; - struct gps *g0; - /* clean on new frame */ - if (newframes) { - clear(&time_ms); - clear(&latitude_wgs); - clear(&longitude_wgs); - clear(&latitude_dms); - clear(&longitude_dms); - clear(&altitude_m); - clear(&speed_ms); - clear(&speed_kmh); - clear(&speed_mph); - clear(&speed_kn); - clear(&track_d); - clear(&positions[type_wgs84]); - clear(&positions[type_dms_kmh]); - clear(&positions[type_dms_mph]); - clear(&positions[type_dms_kn]); - newframes = 0; + if (data.set & ALTITUDE_SET) { + value = json_object_new_double(data.fix.altitude); + json_object_object_add(jresp, "altitude", value); } - /* get the result */ - result = positions[type]; - if (result == NULL) { - DEBUG(afbitf, "building position for type %s", type_NAMES[type]); + if (data.set & LATLON_SET) { + value = json_object_new_double(data.fix.latitude); + json_object_object_add(jresp, "latitude", value); - /* should build the result */ - g0 = &frames[frameidx]; - result = json_object_new_object(); - if (result == NULL) - return NULL; - positions[type] = result; - - /* set the result type */ - json_object_object_add(result, "type", json_object_new_string(type_NAMES[type])); - - /* build time, altitude and track */ - if (time_ms == NULL && g0->set.time) - time_ms = json_object_new_double (g0->time); - addif(result, "time", time_ms); - if (altitude_m == NULL && g0->set.altitude) - altitude_m = json_object_new_double (g0->altitude); - addif(result, "altitude", altitude_m); - if (track_d == NULL && g0->set.track) - track_d = json_object_new_double (g0->track); - addif(result, "track", track_d); - - /* build position */ - switch (type) { - default: - case type_wgs84: - if (latitude_wgs == NULL && g0->set.latitude) - latitude_wgs = json_object_new_double (g0->latitude); - addif(result, "latitude", latitude_wgs); - if (longitude_wgs == NULL && g0->set.longitude) - longitude_wgs = json_object_new_double (g0->longitude); - addif(result, "longitude", longitude_wgs); - break; - case type_dms_kmh: - case type_dms_mph: - case type_dms_kn: - if (latitude_dms == NULL && g0->set.latitude) - latitude_dms = new_dms (g0->latitude, 1); - addif(result, "latitude", latitude_dms); - if (longitude_dms == NULL && g0->set.longitude) - longitude_dms = new_dms (g0->longitude, 0); - addif(result, "longitude", longitude_dms); - break; - } - - /* build speed */ - switch (type) { - default: - case type_wgs84: - if (speed_ms == NULL && g0->set.speed) - speed_ms = json_object_new_double (g0->speed); - addif(result, "speed", speed_ms); - break; - case type_dms_kmh: - if (speed_kmh == NULL && g0->set.speed) - speed_kmh = json_object_new_double (g0->speed * METER_PER_SECOND_TO_KILOMETER_PER_HOUR); - addif(result, "speed", speed_kmh); - break; - case type_dms_mph: - if (speed_mph == NULL && g0->set.speed) - speed_mph = json_object_new_double (g0->speed * METER_PER_SECOND_TO_MILE_PER_HOUR); - addif(result, "speed", speed_mph); - break; - case type_dms_kn: - if (speed_kn == NULL && g0->set.speed) - speed_kn = json_object_new_double (g0->speed * METER_PER_SECOND_TO_KNOT); - addif(result, "speed", speed_kn); - break; - } - } - - return json_object_get(result); -} - -/***************************************************************************************/ -/***************************************************************************************/ -/** **/ -/** **/ -/** SECTION: MANAGING EVENTS **/ -/** **/ -/** **/ -/***************************************************************************************/ -/***************************************************************************************/ -/* - * get the event handler of given id - */ -static struct event *event_of_id(int id) -{ - struct period *p; - struct event *e; - - p = list_of_periods; - while(p != NULL) { - e = p->events; - p = p->next; - while(e != NULL) { - if (e->id == id) - return e; - e = e->next; - } - } - return NULL; -} - -/* - * get the event handler for the type and the period - */ -static struct event *event_get(enum type type, int period) -{ - static int id; - int shift; - uint32_t perio; - struct period *p, **pp, *np; - struct event *e; - - /* normalize the period */ - period = period <= 100 ? 1 : period > 60000 ? 600 : (period / 100); - shift = 0; - while((period >> shift) > 31) - shift++; - perio = (uint32_t)(100 * (((period >> shift) & 31) << shift)); - - /* search for the period */ - pp = &list_of_periods; - p = *pp; - while(p != NULL && p->period < perio) { - pp = &p->next; - p = *pp; + value = json_object_new_double(data.fix.longitude); + json_object_object_add(jresp, "longitude", value); } - /* create the period if it misses */ - if (p == NULL || p->period != perio) { - np = calloc(1, sizeof *p); - if (np == NULL) - return NULL; - np->next = p; - np->period = perio; - *pp = np; - p = np; + if (data.set & SPEED_SET) { + value = json_object_new_double(data.fix.speed); + json_object_object_add(jresp, "speed", value); } - /* search the type */ - e = p->events; - while(e != NULL && e->type != type) - e = e->next; - - /* creates the type if needed */ - if (e == NULL) { - e = calloc(1, sizeof *e); - if (e == NULL) - return NULL; - - e->name = "GPS"; /* TODO */ - e->event = afb_daemon_make_event(afbitf->daemon, e->name); - if (e->event.itf == NULL) { - free(e); - return NULL; - } - - e->next = p->events; - e->type = type; - do { - id++; - if (id < 0) - id = 1; - } while(event_of_id(id) != NULL); - e->id = id; - p->events = e; - } - - return e; -} - -/* - * Sends the events if needed - */ -static void event_send() -{ - struct period *p, **pp; - struct event *e, **pe; - struct timeval tv; - uint32_t now; - - /* skip if nothing is new */ - if (!newframes) - return; - - /* computes now */ - gettimeofday(&tv, NULL); - now = (uint32_t)(tv.tv_sec * 1000) + (uint32_t)(tv.tv_usec / 1000); - - /* iterates over the periods */ - pp = &list_of_periods; - p = *pp; - while (p != NULL) { - if (p->events == NULL) { - /* no event for the period, frees it */ - *pp = p->next; - free(p); - } else { - if (p->period <= now - p->last) { - /* its time to refresh */ - p->last = now; - pe = &p->events; - e = *pe; - while (e != NULL) { - /* sends the event */ - if (afb_event_push(e->event, position(e->type)) != 0) - pe = &e->next; - else { - /* no more listeners, free the event */ - *pe = e->next; - afb_event_drop(e->event); - free(e); - } - e = *pe; - } - } - pp = &p->next; - } - p = *pp; - } + return jresp; } -/***************************************************************************************/ -/***************************************************************************************/ -/** **/ -/** **/ -/** SECTION: HANDLING NMEA **/ -/** **/ -/** **/ -/***************************************************************************************/ -/***************************************************************************************/ -/* - * interprets a nmea time - */ -static int nmea_time(const char *text, uint32_t *result) +static void get_data(struct afb_req request) { - uint32_t x; + json_object *jresp = NULL; + pthread_mutex_lock(&mutex); - if (text[0] < '0' || text[0] > '2' - || text[1] < '0' || text[1] > (text[0] == '2' ? '3' : '9') - || text[2] < '0' || text[2] > '5' - || text[3] < '0' || text[3] > '9' - || text[4] < '0' || text[4] > '5' - || text[5] < '0' || text[5] > '9' - || (text[6] != 0 && text[6] != '.')) - return 0; - - x = (uint32_t)(text[0] - '0'); - x = x * 10 + (uint32_t)(text[1]-'0'); - x = x * 6 + (uint32_t)(text[2]-'0'); - x = x * 10 + (uint32_t)(text[3]-'0'); - x = x * 6 + (uint32_t)(text[4]-'0'); - x = x * 10 + (uint32_t)(text[5]-'0'); - x = x * 1000; - if (text[6] == '.') { - if (text[7] != 0) { - if (text[7] < '0' || text[7] > '9') return 0; - x += (uint32_t)(text[7]-'0') * 100; - if (text[8] != 0) { - if (text[8] < '0' || text[8] > '9') return 0; - x += (uint32_t)(text[8]-'0') * 10; - if (text[9] != 0) { - if (text[9] < '0' || text[9] > '9') return 0; - x += (uint32_t)(text[9]-'0'); - if (text[10] != 0) { - if (text[10] < '0' || text[10] > '9') return 0; - x += text[10] > '5'; - } - } - } - } + jresp = populate_json_data(json_object_new_object()); + if (jresp != NULL) { + afb_req_success(request, jresp, "GNSS location data"); + } else { + afb_req_fail(request, "failed", "No 3D GNSS fix"); } - *result = x; - return 1; + pthread_mutex_unlock(&mutex); } -/* - * interprets a nmea angle having minutes - */ -static int nmea_angle(const char *text, double *result) +static void subscribe(struct afb_req request) { - uint32_t x = 0; - double v; - int dotidx = (int)(strchrnul(text, '.') - text); - - switch(dotidx) { - case 5: - if (text[dotidx - 5] < '0' || text[dotidx - 5] > '9') - return 0; - x = x * 10 + (uint32_t)(text[dotidx - 5] - '0'); - case 4: - if (text[dotidx - 4] < '0' || text[dotidx - 4] > '9') - return 0; - x = x * 10 + (uint32_t)(text[dotidx - 4] - '0'); - case 3: - if (text[dotidx - 3] < '0' || text[dotidx - 3] > '9') - return 0; - x = x * 10 + (uint32_t)(text[dotidx - 3] - '0'); - case 2: - v = atof(&text[dotidx - 2]); - break; - case 1: - if (text[dotidx - 1] < '0' || text[dotidx - 1] > '9') - return 0; - case 0: - v = atof(text); - break; - default: - return 0; - } - - *result = (double)x + v * 0.01666666666666666666666; /* 1 / 60 */ + const char *value = afb_req_value(request, "value"); - return 1; -} - -/* - * creates a new position for the given optionnal fields - * returns 1 if correct or 0 if a format error exists - */ -static int nmea_set( - const char *tim, - const char *lat, const char *latu, - const char *lon, const char *lonu, - const char *alt, const char *altu, - const char *spe, - const char *tra, - const char *dat -) -{ - struct gps gps; - - DEBUG(afbitf, "time=%s latitude=%s%s longitude=%s%s altitude=%s%s speed=%s track=%s date=%s", - tim, lat, latu, lon, lonu, alt, altu, spe, tra, dat); - - /* get the time in milliseconds */ - if (tim == NULL) - gps.set.time = 0; - else { - if (!nmea_time(tim, &gps.time)) - return 0; - gps.set.time = 1; - } - - /* get the latitude */ - if (lat == NULL || latu == NULL) - gps.set.latitude = 0; - else { - if ((latu[0] != 'N' && latu[0] != 'S') || latu[1] != 0) - return 0; - if (!nmea_angle(lat, &gps.latitude)) - return 0; - if (latu[0] == 'S') - gps.latitude = -gps.latitude; - gps.set.latitude = 1; - } - - /* get the longitude */ - if (lon == NULL || lonu == NULL) - gps.set.longitude = 0; - else { - if ((lonu[0] != 'E' && lonu[0] != 'W') || lonu[1] != 0) - return 0; - if (!nmea_angle(lon, &gps.longitude)) - return 0; - if (lonu[0] == 'W') - gps.longitude = 360.0 - gps.longitude; - gps.set.longitude = 1; - } - - /* get the altitude */ - if (alt == NULL || altu == NULL) - gps.set.altitude = 0; - else { - if (altu[0] != 'M' || altu[1] != 0) - return 0; - gps.altitude = atof(alt); - gps.set.altitude = 1; - } - - /* get the speed */ - if (spe == NULL) - gps.set.speed = 0; - else { - gps.speed = atof(spe) * KNOT_TO_METER_PER_SECOND; - gps.set.speed = 1; - } - - /* get the track */ - if (tra != NULL) - gps.set.track = 0; - else { - gps.track = atof(tra); - gps.set.track = 1; - } - - /* push the frame */ - frameidx = (frameidx ? : (int)(sizeof frames / sizeof *frames)) - 1; - frames[frameidx] = gps; - newframes++; - - DEBUG(afbitf, "time:%d=%d latitude:%d=%g longitude:%d=%g altitude:%d=%g speed:%d=%g track:%d=%g", - (int)gps.set.time, gps.set.time ? (int)gps.time : 0, - (int)gps.set.latitude, gps.set.latitude ? gps.latitude : 0, - (int)gps.set.longitude, gps.set.longitude ? gps.longitude : 0, - (int)gps.set.altitude, gps.set.altitude ? gps.altitude : 0, - (int)gps.set.speed, gps.set.speed ? gps.speed : 0, - (int)gps.set.track, gps.set.track ? gps.track : 0 - ); - - return 1; -} - -/* - * Splits the nmea sentences in its fields - */ -static int nmea_split(char *s, char *fields[], int count) -{ - int index = 0; - while (*s && index < count) { - fields[index++] = s; - while (*s && *s != ',') - s++; - if (*s == ',') - *s++ = 0; + if (value && !strcasecmp(value, "location")) { + afb_req_subscribe(request, location_event); + afb_req_success(request, NULL, NULL); + return; } - return !*s && index == count; -} - -/* - * interprete one sentence GGA - Fix information - */ -static int nmea_gga(char *s) -{ - char *f[14]; - - return nmea_split(s, f, (int)(sizeof f / sizeof *f)) - && *f[5] != '0' - && nmea_set(f[0], f[1], f[2], f[3], f[4], f[6], f[7], NULL, NULL, NULL); -} - -/* - * interprete one sentence RMC - Recommended Minimum - */ -static int nmea_rmc(char *s) -{ - char *f[12]; - - return nmea_split(s, f, (int)(sizeof f / sizeof *f)) - && *f[1] == 'A' - && nmea_set(f[0], f[2], f[3], f[4], f[5], NULL, NULL, f[6], f[7], f[8]); -} - - -/* - * interprete one NMEA sentence - */ -static int nmea_sentence(char *s) -{ - if (!s[0] || !s[1]) - return 0; - - if (s[2] == 'G' && s[3] == 'G' && s[4] == 'A' && s[5] == ',') - return nmea_gga(&s[6]); - - if (s[2] == 'R' && s[3] == 'M' && s[4] == 'C' && s[5] == ',') - return nmea_rmc(&s[6]); - - return 0; -} - -/* - * reads the NMEA stream - */ -static int nmea_read(int fd) -{ - static char buffer[160]; - static int pos = 0; - static int overflow = 0; - - int rc; - for(;;) { - rc = (int)read(fd, &buffer[pos], sizeof buffer - (size_t)pos); - if (rc < 0) { - /* its an error if not interrupted */ - if (errno != EINTR) - return rc; - } else if (rc == 0) { - /* nothing more to be read */ - return 0; - } else { - /* scan the buffer */ - while (pos != rc) { - if (buffer[pos] != '\n') { - pos++; - if (pos == rc) { - if (pos == (int)(sizeof buffer)) { - overflow = 1; - pos = 0; - rc = 0; - } - } - } else { - if (buffer[0] == '$' && pos > 0 && buffer[pos-1] == '\r' && !overflow) { - if (pos > 3 && buffer[pos-4] == '*') { - /* TODO: check the cheksum */ - buffer[pos-4] = 0; - } else { - buffer[pos-1] = 0; - } - nmea_sentence(&buffer[1]); - } - pos++; - rc -= pos; - if (rc > 0) - memmove(buffer, buffer+pos, (size_t)rc); - pos = 0; - overflow = 0; - } - } - } - } + afb_req_fail(request, "failed", "Invalid event"); } -/***************************************************************************************/ -/***************************************************************************************/ -/** **/ -/** **/ -/** SECTION: HANDLING OF CONNECTION **/ -/** **/ -/** **/ -/***************************************************************************************/ -/***************************************************************************************/ -/* declare the connection routine */ -static int connection(); - -/* - * called on an event on the NMEA stream - */ -static int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) +static void unsubscribe(struct afb_req request) { - /* read available data */ - if ((revents & EPOLLIN) != 0) { - nmea_read(fd); - event_send(); - } + const char *value = afb_req_value(request, "value"); - /* check if error or hangup */ - if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0) { - sd_event_source_unref(s); - close(fd); - connection(fd); + if (value && !strcasecmp(value, "location")) { + afb_req_unsubscribe(request, location_event); + afb_req_success(request, NULL, NULL); + return; } - return 0; + afb_req_fail(request, "failed", "Invalid event"); } -/* - * opens a socket to a host and a service (or port) - */ -static int open_socket_to(const char *host, const char *service) +static void *data_poll(void *ptr) { - int rc, fd; - struct addrinfo hint, *rai, *iai; + /* + * keep reading till an error condition happens + */ + while (gps_waiting(&data, MSECS_TO_USECS(2000)) && !errno) + { + json_object *jresp = NULL; - /* get addr */ - memset(&hint, 0, sizeof hint); - hint.ai_family = AF_INET; - hint.ai_socktype = SOCK_STREAM; - rc = getaddrinfo(host, service, &hint, &rai); - if (rc != 0) - return -1; - - /* get the socket */ - iai = rai; - while (iai != NULL) { - fd = socket(iai->ai_family, iai->ai_socktype, iai->ai_protocol); - if (fd >= 0) { - rc = connect(fd, iai->ai_addr, iai->ai_addrlen); - if (rc == 0) { - fcntl(fd, F_SETFL, O_NONBLOCK); - freeaddrinfo(rai); - return fd; - } - close(fd); + pthread_mutex_lock(&mutex); + if (gps_read(&data) == -1) { + AFB_ERROR("Cannot read from GPS daemon.\n"); + pthread_mutex_unlock(&mutex); + break; } - iai = iai->ai_next; - } - freeaddrinfo(rai); - return -1; -} - -/* - * connection to nmea stream for the host and the port - */ -static int connect_to(const char *host, const char *service, int isgpsd) -{ - sd_event_source *source; - int rc, fd; - /* TODO connect to somewhere else */ - fd = open_socket_to(host, service); - if (fd < 0) { - ERROR(afbitf, "can't connect to host %s, service %s", host, service); - return fd; - } - if (isgpsd) { - static const char gpsdsetup[] = "?WATCH={\"enable\":true,\"nmea\":true};\r\n"; - write(fd, gpsdsetup, sizeof gpsdsetup - 1); + jresp = populate_json_data(json_object_new_object()); + if (jresp != NULL) + afb_event_push(location_event, jresp); + pthread_mutex_unlock(&mutex); } - /* adds to the event loop */ - rc = sd_event_add_io(afb_daemon_get_event_loop(afbitf->daemon), &source, fd, EPOLLIN, on_event, NULL); - if (rc < 0) { - close(fd); - ERROR(afbitf, "can't coonect host %s, service %s to the event loop", host, service); - } else { - NOTICE(afbitf, "Connected to host %s, service %s", host, service); - } - return rc; -} + AFB_INFO("Closing GPS daemon connection.\n"); + gps_stream(&data, WATCH_DISABLE, NULL); + gps_close(&data); -/* - * connection to nmea stream - */ -static int connection() -{ - const char *host; - const char *service; - int isgpsd; + abort(); - /* TODO connect to somewhere else */ - host = getenv("AFBGPS_HOST") ? : "sinagot.net"; - service = getenv("AFBGPS_SERVICE") ? : "5001"; - isgpsd = getenv("AFBGPS_ISNMEA") ? 0 : 1; - return connect_to(host, service, isgpsd); + return NULL; } -/***************************************************************************************/ -/***************************************************************************************/ -/** **/ -/** **/ -/** SECTION: BINDING VERBS IMPLEMENTATION **/ -/** **/ -/** **/ -/***************************************************************************************/ -/***************************************************************************************/ -/* - * Returns the type corresponding to the given name - */ -static enum type type_of_name(const char *name) +static int init() { - enum type result; - if (name == NULL) - return type_DEFAULT; - for (result = 0 ; result != type_COUNT ; result++) - if (strcmp(type_NAMES[result], name) == 0) - return result; - return type_INVALID; -} + const char *host, *port; + int ret; -/* - * extract a valid type from the request - */ -static int get_type_for_req(struct afb_req req, enum type *type) -{ - if ((*type = type_of_name(afb_req_value(req, "type"))) != type_INVALID) - return 1; - afb_req_fail(req, "unknown-type", NULL); - return 0; -} - -/* - * Get the last known position - * - * parameter of the get are: - * - * type: string: the type of position expected (defaults to "WGS84" if not present) - * - * returns the position - * - * The valid types are: - * - * +==========+=======================+=======+==========+=======+ - * | type | longitude & latitude | speed | altitude | track | - * +==========+=======================+=======+==========+=======+ - * | WGS84 | degre | m/s | | | - * +----------+-----------------------+-------+ | | - * | DMS.km/h | | km/h | | | - * +----------+ +-------+ meter | degre | - * | DMS.mph | deg°min'sec"X | mph | | | - * +----------+ +-------+ | | - * | DMS.kn | | kn | | | - * +==========+=======================+=======+==========+=======+ - */ -static void get(struct afb_req req) -{ - enum type type; - if (get_type_for_req(req, &type)) - afb_req_success(req, position(type), NULL); -} + location_event = afb_daemon_make_event("location"); -/* - * subscribe to notification of position - * - * parameters of the subscription are: - * - * type: string: the type of position expected (defaults to WCS84 if not present) - * see the list above (get) - * period: integer: the expected period in milliseconds (defaults to 2000 if not present) - * - * returns an object with 2 fields: - * - * name: string: the name of the event without its prefix - * id: integer: a numeric identifier of the event to be used for unsubscribing - */ -static void subscribe(struct afb_req req) -{ - enum type type; - const char *period; - struct event *event; - struct json_object *json; + host = getenv("AFBGPS_HOST") ? : "localhost"; + port = getenv("AFBGPS_SERVICE") ? : "2947"; - if (get_type_for_req(req, &type)) { - period = afb_req_value(req, "period"); - event = event_get(type, period == NULL ? DEFAULT_PERIOD : atoi(period)); - if (event == NULL) - afb_req_fail(req, "out-of-memory", NULL); - else if (afb_req_subscribe(req, event->event) != 0) - afb_req_fail_f(req, "failed", "afb_req_subscribe returned an error: %m"); - else { - json = json_object_new_object(); - json_object_object_add(json, "name", json_object_new_string(event->name)); - json_object_object_add(json, "id", json_object_new_int(event->id)); - afb_req_success(req, json, NULL); - } - } -} + ret = gps_open(host, port, &data); + if (ret < 0) + return ret; -/* - * unsubscribe a previous subscription - * - * parameters of the unsubscription are: - * - * id: integer: the numeric identifier of the event as returned when subscribing - */ -static void unsubscribe(struct afb_req req) -{ - const char *id; - struct event *event; + gps_stream(&data, WATCH_ENABLE | WATCH_JSON, NULL); - id = afb_req_value(req, "id"); - if (id == NULL) - afb_req_fail(req, "missing-id", NULL); - else { - event = event_of_id(atoi(id)); - if (event == NULL) - afb_req_fail(req, "bad-id", NULL); - else { - afb_req_unsubscribe(req, event->event); - afb_req_success(req, NULL, NULL); - } - } + return pthread_create(&thread, NULL, &data_poll, NULL); } -/* - * array of the verbs exported to afb-daemon - */ -static const struct afb_verb_desc_v1 binding_verbs[] = { - /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */ - { .name= "get", .session= AFB_SESSION_NONE, .callback= get, .info= "get the last known data" }, - { .name= "subscribe", .session= AFB_SESSION_NONE, .callback= subscribe, .info= "subscribe to notification of position" }, - { .name= "unsubscribe", .session= AFB_SESSION_NONE, .callback= unsubscribe, .info= "unsubscribe a previous subscription" }, - { .name= NULL } /* marker for end of the array */ +static const struct afb_verb_v2 binding_verbs[] = { + { .verb = "location", .callback = get_data, .info = "Get GNSS data" }, + { .verb = "subscribe", .callback = subscribe, .info = "Subscribe to GNSS events" }, + { .verb = "unsubscribe", .callback = unsubscribe, .info = "Unsubscribe to GNSS events" }, + { } }; /* - * description of the binding for afb-daemon + * binder API description */ -static const struct afb_binding binding_description = -{ - /* description conforms to VERSION 1 */ - .type= AFB_BINDING_VERSION_1, - .v1= { /* fills the v1 field of the union when AFB_BINDING_VERSION_1 */ - .prefix= "gps", /* the API name (or binding name or prefix) */ - .info= "Access to the GPS data", /* short description of of the binding */ - .verbs = binding_verbs /* the array describing the verbs of the API */ - } +const struct afb_binding_v2 afbBindingV2 = { + .api = "gps", + .specification = "GNSS/GPS API", + .verbs = binding_verbs, + .init = init, }; - -/* - * activation function for registering the binding called by afb-daemon - */ -const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf) -{ - afbitf = itf; /* records the interface for accessing afb-daemon */ - return &binding_description; /* returns the description of the binding */ -} - -int afbBindingV1ServiceInit(struct afb_service service) -{ - return connection(); -} |