From 94af7aa13b923fb96c152c4cb4251fe415271a91 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Thu, 10 Aug 2017 11:13:22 -0700 Subject: binding: gps: move build to cmake Enable cmake build system for building webpack wgt, and remove unnecessary files Change-Id: Ia00b1ba7a9d26e7f8933c0ccc97bf8b91dbfa012 Bug-AGL: SPEC-823 Signed-off-by: Matt Ranostay --- .gitmodules | 3 + CMakeLists.txt | 21 +- binding/CMakeLists.txt | 39 ++ binding/afm-gps-binding.c | 1051 ++++++++++++++++++++++++++++++++++++++ conf.d/app-templates | 1 + conf.d/autobuild/agl/autobuild | 60 +++ conf.d/autobuild/linux/autobuild | 60 +++ conf.d/cmake/config.cmake | 149 ++++++ conf.d/wgt/config.xml.in | 22 + src/CMakeLists.txt | 66 --- src/af-gps-binding.c | 1051 -------------------------------------- src/export.map | 1 - 12 files changed, 1389 insertions(+), 1135 deletions(-) create mode 100644 .gitmodules create mode 100644 binding/CMakeLists.txt create mode 100644 binding/afm-gps-binding.c create mode 160000 conf.d/app-templates create mode 100755 conf.d/autobuild/agl/autobuild create mode 100755 conf.d/autobuild/linux/autobuild create mode 100644 conf.d/cmake/config.cmake create mode 100644 conf.d/wgt/config.xml.in delete mode 100644 src/CMakeLists.txt delete mode 100644 src/af-gps-binding.c delete mode 100644 src/export.map diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b545da1 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "conf.d/app-templates"] + path = conf.d/app-templates + url = https://gerrit.automotivelinux.org/gerrit/apps/app-templates diff --git a/CMakeLists.txt b/CMakeLists.txt index c4a94b8..b485097 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ ########################################################################### -# Copyright 2016 IoT.bzh +# Copyright 2015, 2016, 2017 IoT.bzh # -# author: José Bollo +# author: Romain Forlot # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,19 +16,6 @@ # limitations under the License. ########################################################################### -project(af-gps-binding C) - -cmake_minimum_required(VERSION 3.3) - -include(GNUInstallDirs) - -set(PROJECT_NAME "af-gps-binding") -set(PROJECT_PRETTY_NAME "af binding for GPS") -set(PROJECT_DESCRIPTION "Binding for handling NMEA signalisation of GPS") -set(PROJECT_VERSION "1.0") - -set(CMAKE_BUILD_TYPE Debug) - -add_subdirectory(src) - +CMAKE_MINIMUM_REQUIRED(VERSION 3.3) +include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt new file mode 100644 index 0000000..ceb48d9 --- /dev/null +++ b/binding/CMakeLists.txt @@ -0,0 +1,39 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll +# contrib: Romain Forlot +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# Add target to project dependency list +PROJECT_TARGET_ADD(afm-gps-binding) + + # Define project Targets + add_library(afm-gps-binding MODULE afm-gps-binding.c) + + # Binder exposes a unique public entry point + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "BINDING" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME} + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} ${link_libraries}) + + # installation directory + INSTALL(TARGETS ${TARGET_NAME} + LIBRARY DESTINATION ${BINDINGS_INSTALL_DIR}) + diff --git a/binding/afm-gps-binding.c b/binding/afm-gps-binding.c new file mode 100644 index 0000000..080f1e5 --- /dev/null +++ b/binding/afm-gps-binding.c @@ -0,0 +1,1051 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#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; +}; + +/* the gps data converted */ +struct gps { + struct flags set; + + uint32_t time; + double latitude; + double longitude; + double altitude; + double speed; + double track; +}; + +/* + * 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) +{ + char buffer[50], pos; + double D, M; + + if (islat) { + if (a >= 0) + pos = 'N'; + else { + a = -a; + pos = 'S'; + } + } else { + if (a <= 180) + pos = 'E'; + else { + a = 360 - a; + pos = 'W'; + } + } + 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; + } + + /* get the result */ + result = positions[type]; + if (result == NULL) { + DEBUG(afbitf, "building position for type %s", type_NAMES[type]); + + /* 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; + } + + /* 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; + } + + /* 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; + } +} + +/***************************************************************************************/ +/***************************************************************************************/ +/** **/ +/** **/ +/** SECTION: HANDLING NMEA **/ +/** **/ +/** **/ +/***************************************************************************************/ +/***************************************************************************************/ +/* + * interprets a nmea time + */ +static int nmea_time(const char *text, uint32_t *result) +{ + uint32_t x; + + 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'; + } + } + } + } + } + + *result = x; + return 1; +} + +/* + * interprets a nmea angle having minutes + */ +static int nmea_angle(const char *text, double *result) +{ + 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 */ + + 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; + } + 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; + } + } + } + } +} + +/***************************************************************************************/ +/***************************************************************************************/ +/** **/ +/** **/ +/** 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) +{ + /* read available data */ + if ((revents & EPOLLIN) != 0) { + nmea_read(fd); + event_send(); + } + + /* check if error or hangup */ + if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0) { + sd_event_source_unref(s); + close(fd); + connection(fd); + } + + return 0; +} + +/* + * opens a socket to a host and a service (or port) + */ +static int open_socket_to(const char *host, const char *service) +{ + int rc, fd; + struct addrinfo hint, *rai, *iai; + + /* 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); + } + 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); + } + + /* 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; +} + +/* + * connection to nmea stream + */ +static int connection() +{ + const char *host; + const char *service; + int isgpsd; + + /* 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); +} + +/***************************************************************************************/ +/***************************************************************************************/ +/** **/ +/** **/ +/** SECTION: BINDING VERBS IMPLEMENTATION **/ +/** **/ +/** **/ +/***************************************************************************************/ +/***************************************************************************************/ +/* + * Returns the type corresponding to the given name + */ +static enum type type_of_name(const char *name) +{ + 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; +} + +/* + * 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); +} + +/* + * 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; + + 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); + } + } +} + +/* + * 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; + + 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); + } + } +} + +/* + * 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 */ +}; + +/* + * description of the binding for afb-daemon + */ +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 */ + } +}; + +/* + * 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(); +} diff --git a/conf.d/app-templates b/conf.d/app-templates new file mode 160000 index 0000000..8967162 --- /dev/null +++ b/conf.d/app-templates @@ -0,0 +1 @@ +Subproject commit 8967162dd12bce89f9ae27f5c9bce7b78624e3fd diff --git a/conf.d/autobuild/agl/autobuild b/conf.d/autobuild/agl/autobuild new file mode 100755 index 0000000..759f6be --- /dev/null +++ b/conf.d/autobuild/agl/autobuild @@ -0,0 +1,60 @@ +#!/usr/bin/make -f +# Copyright (C) 2015, 2016 "IoT.bzh" +# Author "Romain Forlot" +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +THISFILE := $(lastword $(MAKEFILE_LIST)) +BUILD_DIR := $(abspath $(dir $(THISFILE)/../../../../..)/build) +DEST := ${BUILD_DIR}/target + +.PHONY: all clean distclean configure build package help + +all: help + +help: + @echo "List of targets available:" + @echo "" + @echo "- all" + @echo "- clean" + @echo "- distclean" + @echo "- configure" + @echo "- build" + @echo "- package" + @echo "" + @echo "Usage: ./conf.d/autobuild/agl/autobuild package DEST=${HOME}/opt" + @echo "Don't use your build dir as DEST as wgt file is generated at this location" + +clean: + @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean) || echo Nothing to clean + +distclean: + @rm -rf ${BUILD_DIR} + +configure: ${BUILD_DIR}/Makefile + +build: configure + @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all + +package: build + @mkdir -p ${BUILD_DIR}/$@/bin + @mkdir -p ${BUILD_DIR}/$@/etc + @mkdir -p ${BUILD_DIR}/$@/lib + @mkdir -p ${BUILD_DIR}/$@/htdocs + @mkdir -p ${BUILD_DIR}/$@/data + @cmake --build ${BUILD_DIR} --target widget + @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST} + +${BUILD_DIR}/Makefile: + @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR} + @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..) diff --git a/conf.d/autobuild/linux/autobuild b/conf.d/autobuild/linux/autobuild new file mode 100755 index 0000000..759f6be --- /dev/null +++ b/conf.d/autobuild/linux/autobuild @@ -0,0 +1,60 @@ +#!/usr/bin/make -f +# Copyright (C) 2015, 2016 "IoT.bzh" +# Author "Romain Forlot" +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +THISFILE := $(lastword $(MAKEFILE_LIST)) +BUILD_DIR := $(abspath $(dir $(THISFILE)/../../../../..)/build) +DEST := ${BUILD_DIR}/target + +.PHONY: all clean distclean configure build package help + +all: help + +help: + @echo "List of targets available:" + @echo "" + @echo "- all" + @echo "- clean" + @echo "- distclean" + @echo "- configure" + @echo "- build" + @echo "- package" + @echo "" + @echo "Usage: ./conf.d/autobuild/agl/autobuild package DEST=${HOME}/opt" + @echo "Don't use your build dir as DEST as wgt file is generated at this location" + +clean: + @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean) || echo Nothing to clean + +distclean: + @rm -rf ${BUILD_DIR} + +configure: ${BUILD_DIR}/Makefile + +build: configure + @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all + +package: build + @mkdir -p ${BUILD_DIR}/$@/bin + @mkdir -p ${BUILD_DIR}/$@/etc + @mkdir -p ${BUILD_DIR}/$@/lib + @mkdir -p ${BUILD_DIR}/$@/htdocs + @mkdir -p ${BUILD_DIR}/$@/data + @cmake --build ${BUILD_DIR} --target widget + @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST} + +${BUILD_DIR}/Makefile: + @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR} + @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..) diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake new file mode 100644 index 0000000..861cb5b --- /dev/null +++ b/conf.d/cmake/config.cmake @@ -0,0 +1,149 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# Project Info +# ------------------ +set(PROJECT_NAME agl-service-gps) +set(PROJECT_PRETTY_NAME "AFM binding for GPS") +set(PROJECT_DESCRIPTION "Binding for handling NMEA signalisation of GPS") +set(PROJECT_VERSION "1.0") +set(PROJECT_ICON "icon.png") +set(PROJECT_LICENSE "APL2.0") +set(PROJECT_LANGUAGES,"C") + +# Where are stored default templates files from submodule or subtree app-templates in your project tree +# relative to the root project directory +set(PROJECT_APP_TEMPLATES_DIR "conf.d/app-templates") + +# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain +# but used and must be built and linked. +# set(PROJECT_LIBDIR "libs") + +# Where are stored data for your application. Pictures, static resources must be placed in that folder. +# set(PROJECT_RESOURCES "data") + +# Which directories inspect to find CMakeLists.txt target files +# set(PROJECT_SRC_DIR_PATTERN "*") + +# Compilation Mode (DEBUG, RELEASE) +# ---------------------------------- +set(CMAKE_BUILD_TYPE "DEBUG") + +# Kernel selection if needed. You can choose between a +# mandatory version to impose a minimal version. +# Or check Kernel minimal version and just print a Warning +# about missing features and define a preprocessor variable +# to be used as preprocessor condition in code to disable +# incompatibles features. Preprocessor define is named +# KERNEL_MINIMAL_VERSION_OK. +# +# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and +# Yocto SDK Kernel version. +# ----------------------------------------------- +#set(kernel_mandatory_version 4.8) +set(kernel_minimal_version 4.8) + +# Compiler selection if needed. Impose a minimal version. +# ----------------------------------------------- +set (gcc_minimal_version 4.9) + +# PKG_CONFIG required packages +# ----------------------------- +set (PKG_REQUIRED_LIST + json-c + libsystemd>=222 + afb-daemon +) + +# Customize link option +# ----------------------------- +list (APPEND link_libraries -pthread) + +# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] +# --------------------------------------------------------------------- +set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) +set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) +set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) + +# Optional location for config.xml.in +# ----------------------------------- +set(WIDGET_CONFIG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in) + +# Mandatory widget Mimetype specification of the main unit +# -------------------------------------------------------------------------- +# Choose between : +#- text/html : HTML application, +# content.src designates the home page of the application +# +#- application/vnd.agl.native : AGL compatible native, +# content.src designates the relative path of the binary. +# +# - application/vnd.agl.service: AGL service, content.src is not used. +# +#- ***application/x-executable***: Native application, +# content.src designates the relative path of the binary. +# For such application, only security setup is made. +# +set(WIDGET_TYPE application/vnd.agl.service) + +# Mandatory Widget entry point file of the main unit +# -------------------------------------------------------------- +# This is the file that will be executed, loaded, +# at launch time by the application framework. +# +set(WIDGET_ENTRY_POINT lib/libafm-gps-binding.so) + +# Print a helper message when every thing is finished +# ---------------------------------------------------- +set(CLOSING_MESSAGE "Test with: afb-daemon --rootdir=\$\$(pwd)/package --binding=\$\$(pwd)/package/${WIDGET_ENTRY_POINT} --port=1234 --tracereq=common --token=\"1\" --verbose") +set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") + +# Optional dependencies order +# --------------------------- +#set(EXTRA_DEPENDENCIES_ORDER) + +# Optional Extra global include path +# ----------------------------------- +#set(EXTRA_INCLUDE_DIRS) + +# Optional extra libraries +# ------------------------- +#set(EXTRA_LINK_LIBRARIES) + +# Optional force binding installation +# ------------------------------------ +# set(BINDINGS_INSTALL_PREFIX PrefixPath ) + +# Optional force binding Linking flag +# ------------------------------------ +# set(BINDINGS_LINK_FLAG LinkOptions ) + +# Optional force package prefix generation, like widget +# ----------------------------------------------------- +# set(PKG_PREFIX DestinationPath) + +# Optional Application Framework security token +# and port use for remote debugging. +#------------------------------------------------------------ +#set(AFB_TOKEN "" CACHE PATH "Default AFB_TOKEN") +#set(AFB_REMPORT "1234" CACHE PATH "Default AFB_TOKEN") + +# This include is mandatory and MUST happens at the end +# of this file, else you expose you to unexpected behavior +# ----------------------------------------------------------- +include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake) diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in new file mode 100644 index 0000000..ae68823 --- /dev/null +++ b/conf.d/wgt/config.xml.in @@ -0,0 +1,22 @@ + + + @PROJECT_NAME@ + + + @PROJECT_DESCRIPTION@ + @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> + @PROJECT_LICENSE@ + + + + + + + + + + + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 5badd01..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,66 +0,0 @@ -########################################################################### -# Copyright 2016 IoT.bzh -# -# author: José Bollo -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -########################################################################### - -cmake_minimum_required(VERSION 3.3) - -########################################################################### - -link_libraries(-Wl,--as-needed -Wl,--gc-sections) - -add_compile_options(-Wall -Wextra -Wconversion) -add_compile_options(-Wno-unused-parameter) # frankly not using a parameter does it care? -add_compile_options(-Werror=maybe-uninitialized) -add_compile_options(-Werror=implicit-function-declaration) -add_compile_options(-ffunction-sections -fdata-sections) -add_compile_options(-Wl,--as-needed -Wl,--gc-sections) -add_compile_options(-fPIC) - -set(CMAKE_C_FLAGS_PROFILING "-g -O0 -pg -Wp,-U_FORTIFY_SOURCE") -set(CMAKE_C_FLAGS_DEBUG "-g -O0 -ggdb -Wp,-U_FORTIFY_SOURCE") -set(CMAKE_C_FLAGS_RELEASE "-g -O2") -set(CMAKE_C_FLAGS_CCOV "-g -O2 --coverage") - -########################################################################### - -include(FindPkgConfig) - -pkg_check_modules(EXTRAS REQUIRED json-c libsystemd afb-daemon) -add_compile_options(${EXTRAS_CFLAGS}) -include_directories(${EXTRAS_INCLUDE_DIRS}) -link_libraries(${EXTRAS_LIBRARIES}) - -########################################################################### -# the binding for afb - -message(STATUS "Creation af-gps-binding for AFB-DAEMON") - -############################################################### -#pkg_get_variable(afb_binding_install_dir afb-daemon binding_install_dir) -execute_process( - COMMAND pkg-config --variable binding_install_dir afb-daemon - OUTPUT_VARIABLE afb_binding_install_dir OUTPUT_STRIP_TRAILING_WHITESPACE -) - -############################################################### -add_library(af-gps-binding MODULE af-gps-binding.c) -set_target_properties(af-gps-binding PROPERTIES - PREFIX "" - LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/export.map" -) -install(TARGETS af-gps-binding LIBRARY DESTINATION ${afb_binding_install_dir}) - diff --git a/src/af-gps-binding.c b/src/af-gps-binding.c deleted file mode 100644 index 080f1e5..0000000 --- a/src/af-gps-binding.c +++ /dev/null @@ -1,1051 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include - -#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; -}; - -/* the gps data converted */ -struct gps { - struct flags set; - - uint32_t time; - double latitude; - double longitude; - double altitude; - double speed; - double track; -}; - -/* - * 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) -{ - char buffer[50], pos; - double D, M; - - if (islat) { - if (a >= 0) - pos = 'N'; - else { - a = -a; - pos = 'S'; - } - } else { - if (a <= 180) - pos = 'E'; - else { - a = 360 - a; - pos = 'W'; - } - } - 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; - } - - /* get the result */ - result = positions[type]; - if (result == NULL) { - DEBUG(afbitf, "building position for type %s", type_NAMES[type]); - - /* 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; - } - - /* 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; - } - - /* 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; - } -} - -/***************************************************************************************/ -/***************************************************************************************/ -/** **/ -/** **/ -/** SECTION: HANDLING NMEA **/ -/** **/ -/** **/ -/***************************************************************************************/ -/***************************************************************************************/ -/* - * interprets a nmea time - */ -static int nmea_time(const char *text, uint32_t *result) -{ - uint32_t x; - - 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'; - } - } - } - } - } - - *result = x; - return 1; -} - -/* - * interprets a nmea angle having minutes - */ -static int nmea_angle(const char *text, double *result) -{ - 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 */ - - 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; - } - 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; - } - } - } - } -} - -/***************************************************************************************/ -/***************************************************************************************/ -/** **/ -/** **/ -/** 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) -{ - /* read available data */ - if ((revents & EPOLLIN) != 0) { - nmea_read(fd); - event_send(); - } - - /* check if error or hangup */ - if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0) { - sd_event_source_unref(s); - close(fd); - connection(fd); - } - - return 0; -} - -/* - * opens a socket to a host and a service (or port) - */ -static int open_socket_to(const char *host, const char *service) -{ - int rc, fd; - struct addrinfo hint, *rai, *iai; - - /* 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); - } - 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); - } - - /* 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; -} - -/* - * connection to nmea stream - */ -static int connection() -{ - const char *host; - const char *service; - int isgpsd; - - /* 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); -} - -/***************************************************************************************/ -/***************************************************************************************/ -/** **/ -/** **/ -/** SECTION: BINDING VERBS IMPLEMENTATION **/ -/** **/ -/** **/ -/***************************************************************************************/ -/***************************************************************************************/ -/* - * Returns the type corresponding to the given name - */ -static enum type type_of_name(const char *name) -{ - 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; -} - -/* - * 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); -} - -/* - * 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; - - 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); - } - } -} - -/* - * 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; - - 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); - } - } -} - -/* - * 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 */ -}; - -/* - * description of the binding for afb-daemon - */ -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 */ - } -}; - -/* - * 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(); -} diff --git a/src/export.map b/src/export.map deleted file mode 100644 index 52c1b4a..0000000 --- a/src/export.map +++ /dev/null @@ -1 +0,0 @@ -{ global: afbBindingV1*; local: *; }; -- cgit 1.2.3-korg