mirror of
https://github.com/owntracks/recorder.git
synced 2026-02-13 20:49:51 +00:00
NEW: support for geo fences (waypoints)
WARNING: you must run --initialize to create the new sub db see documentation in doc/FENCES.md
This commit is contained in:
4
Makefile
4
Makefile
@@ -13,6 +13,7 @@ OTR_OBJS = json.o \
|
||||
misc.o \
|
||||
util.o \
|
||||
storage.o \
|
||||
fences.o \
|
||||
listsort.o
|
||||
OTR_EXTRA_OBJS =
|
||||
|
||||
@@ -82,7 +83,7 @@ ocat: ocat.o $(OTR_OBJS)
|
||||
|
||||
$(OTR_OBJS): config.mk Makefile
|
||||
|
||||
recorder.o: recorder.c storage.h util.h Makefile geo.h udata.h json.h http.h gcache.h config.mk hooks.h base64.h recorder.h version.h
|
||||
recorder.o: recorder.c storage.h util.h Makefile geo.h udata.h json.h http.h gcache.h config.mk hooks.h base64.h recorder.h version.h fences.h
|
||||
geo.o: geo.h geo.c udata.h
|
||||
geohash.o: geohash.h geohash.c udata.h
|
||||
base64.o: base64.h base64.c
|
||||
@@ -96,6 +97,7 @@ ocat.o: ocat.c storage.h util.h version.h config.mk Makefile
|
||||
storage.o: storage.c storage.h util.h gcache.h listsort.h
|
||||
hooks.o: hooks.c udata.h hooks.h util.h version.h gcache.h
|
||||
listsort.o: listsort.c listsort.h
|
||||
fences.o: fences.c fences.h util.h json.h udata.h gcache.h hooks.h
|
||||
|
||||
|
||||
clean:
|
||||
|
||||
51
doc/FENCES.md
Normal file
51
doc/FENCES.md
Normal file
@@ -0,0 +1,51 @@
|
||||
## Geo fences
|
||||
|
||||
Since a version > 0.6.9, Recorder has support for Geofences (irrespective of their use on the OwnTracks devices). In particular, Recorder can read a list of fences from `.otrw` files, and it will monitor a user's position to determine whether the user is transitioning in to or out of a fence, in which case Recorder will invoke a Lua hook (called `transition`).
|
||||
|
||||
### `.otrw`
|
||||
|
||||
Recorder reads `.otrw` files from `<store>/waypoints/user/device/user-device.otrw` upon startup and loads these into an internal LMDB database. Each waypoint (geo fence) is keyed by `user-device-geohash(lat,lon)` in the LMDB sub table.
|
||||
|
||||
For example, the following otrw
|
||||
|
||||
```json
|
||||
{
|
||||
"_type": "waypoints",
|
||||
"waypoints": [
|
||||
{
|
||||
"_type": "waypoint",
|
||||
"tst": 9999,
|
||||
"lat": 48.85833,
|
||||
"lon": 3.29513,
|
||||
"rad": 1000,
|
||||
"desc": "chez Madelaine"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
is read in as
|
||||
|
||||
```
|
||||
$ ocat -S jp --dump=wp
|
||||
uno-lua-u0dmfyrkqh {"lat":48.85833,"lon":3.29513,"rad":1000,"desc":"chez Madelaine","io":false}
|
||||
```
|
||||
|
||||
Note how the `io` (in / out) element in the JSON indicates whether the last position reported was in or out of the fence.
|
||||
|
||||
### Transition hook
|
||||
|
||||
When Recorder determines that the user's device has entered or left the geo fence, it invokes a user-provided Lua function called `transition`:
|
||||
|
||||
```lua
|
||||
function otr_init()
|
||||
end
|
||||
|
||||
function otr_exit()
|
||||
end
|
||||
|
||||
function transition(topic, _type, data)
|
||||
print("IN TRANSITION " .. _type .. " " .. topic)
|
||||
otr.publish('special/topic', data['event'] .. " " .. data['desc'])
|
||||
end
|
||||
```
|
||||
@@ -86,6 +86,10 @@ non-zero value, the Recorder will *not* write the REC file for this publish.
|
||||
|
||||
An optional function you provide is called `otr_httpobject(u, d, t, data)` where `u` is the username used by the client (`?u=`), `d` is the device name (`&d=` in the URI), `t` is the OwnTracks JSON `_type` and `data` a Lua table built from the OwnTracks JSON payload of `_type`. If it exists, this function is called whenever a POST is received in httpmode and the Recorder is gathering data to return to the client app. The function *must* return a Lua table containing any number of string, number, or boolean values which are converted to a JSON object and appended to the JSON array returned to the client. An [example](etc/example.lua) shows how, say, a transition event can be used to open the Featured content tab in the app.
|
||||
|
||||
## `transition`
|
||||
|
||||
See [geo fences](FENCES.md).
|
||||
|
||||
## Hooklets
|
||||
|
||||
After running `otr_hook()`, the Recorder attempts to invoke a Lua function for each of the elements in the extended JSON. If, say, your Lua script contains a function called `hooklet_lat`, it will be invoked every time a `lat` is received as part of the JSON payload. Similarly with `hooklet_addr`, `hooklet_cc`, `hooklet_tst`, etc. These _hooklets_ are invoked with the same parameters as `otr_hook()`.
|
||||
|
||||
94
fences.c
Normal file
94
fences.c
Normal file
@@ -0,0 +1,94 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "json.h"
|
||||
#include "udata.h"
|
||||
#include "fences.h"
|
||||
#include "gcache.h"
|
||||
#include "util.h"
|
||||
#ifdef WITH_LUA
|
||||
# include "hooks.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* OwnTracks Recorder
|
||||
* Copyright (C) 2015-2016 Jan-Piet Mens <jpmens@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* lat/lon are the position that has been reported by the device as it is at
|
||||
* now, and wp contains the point read from the list of waypoints. Calculate
|
||||
* distance betwee the two, and if that's less than waypoint radius, user is
|
||||
* now IN the geofence, else OUT of the geofence. Indicate transition by
|
||||
* changing IO in wp and telling caller to store.
|
||||
*/
|
||||
|
||||
static int check_a_waypoint(char *key, wpoint *wp, double lat, double lon)
|
||||
{
|
||||
double d;
|
||||
bool rewrite = false;
|
||||
|
||||
d = haversine_dist(wp->lat, wp->lon, lat, lon);
|
||||
// printf("WP= rad=%ld, io=%d, dist=%lf %s\n", wp->rad, wp->io, d, wp->desc);
|
||||
|
||||
if (d < wp->rad) {
|
||||
// printf("YEAH: key(%s)\n", key);
|
||||
|
||||
if (wp->io == false) {
|
||||
wp->event = ENTER;
|
||||
wp->io = true;
|
||||
rewrite = true;
|
||||
}
|
||||
} else if (wp->io == true) {
|
||||
wp->event = LEAVE;
|
||||
wp->io = false;
|
||||
rewrite = true;
|
||||
}
|
||||
|
||||
if (rewrite) {
|
||||
// printf("%s - %s: EVENT == %s\n", wp->user, wp->device, wp->event == 0 ? "ENTER" : "LEAVE");
|
||||
#ifdef WITH_LUA
|
||||
if (wp->ud->luadata) {
|
||||
hooks_transition(wp->ud, wp->user, wp->device, wp->event, wp->desc, wp->lat, wp->lon, lat, lon);
|
||||
}
|
||||
#endif /* WITH_LUA */
|
||||
}
|
||||
|
||||
return (rewrite);
|
||||
}
|
||||
|
||||
/*
|
||||
* Every time a position is obtained, calculate the distance to the center
|
||||
* of each geofence and check whether that distance is less than the radius
|
||||
* of the geofence. If the distance is <= to the radius, the obtained position
|
||||
* is considered to be inside the geofence. This position is calculated using
|
||||
* the Haversine formula.
|
||||
*/
|
||||
|
||||
void check_fences(struct udata *ud, char *username, char *device, double lat, double lon, JsonNode *json)
|
||||
{
|
||||
static UT_string *userdev;
|
||||
|
||||
utstring_renew(userdev);
|
||||
utstring_printf(userdev, "%s-%s", username, device);
|
||||
|
||||
/*
|
||||
* For each of this user's geofences (username-device-*) obtain lat/lon/rad
|
||||
* and do as described above.
|
||||
*/
|
||||
|
||||
gcache_enum(username, device, ud->wpdb, UB(userdev), check_a_waypoint, lat, lon, ud);
|
||||
}
|
||||
22
fences.h
Normal file
22
fences.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef FENCES_H_INCLUDED
|
||||
# define FENCES_H_INCLUDED
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
double lat;
|
||||
double lon;
|
||||
long rad;
|
||||
char *desc;
|
||||
bool io; /* true: is IN region. false: is OUT of region */
|
||||
char *user;
|
||||
char *device;
|
||||
enum { ENTER, LEAVE } event;
|
||||
struct udata *ud;
|
||||
} wpoint;
|
||||
|
||||
void check_fences(struct udata *ud, char *username, char *device, double lat, double lon, JsonNode *json);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
93
gcache.c
93
gcache.c
@@ -21,6 +21,8 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include "udata.h"
|
||||
#include "fences.h"
|
||||
#include "gcache.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -345,3 +347,94 @@ void gcache_load(char *path, char *lmdbname)
|
||||
|
||||
gcache_close(gc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enumerate (list) keys in lmdb `gc` and invoke func() on each. If func() returns true
|
||||
* update the data.
|
||||
*/
|
||||
|
||||
bool gcache_enum(char *user, char *device, struct gcache *gc, char *key_part, int (*func)(char *key, wpoint *wp, double lat, double lon), double lat, double lon, struct udata *ud)
|
||||
{
|
||||
MDB_val key, data;
|
||||
MDB_txn *txn;
|
||||
MDB_cursor *cursor;
|
||||
int rc, op;
|
||||
static UT_string *ks; /* key string */
|
||||
wpoint wp;
|
||||
|
||||
if (gc == NULL)
|
||||
return (NULL);
|
||||
|
||||
rc = mdb_txn_begin(gc->env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) {
|
||||
olog(LOG_ERR, "gcache_enum: mdb_txn_begin: (%d) %s", rc, mdb_strerror(rc));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
key.mv_data = key_part;
|
||||
key.mv_size = strlen(key_part);
|
||||
|
||||
rc = mdb_cursor_open(txn, gc->dbi, &cursor);
|
||||
|
||||
op = MDB_SET_RANGE;
|
||||
do {
|
||||
JsonNode *json, *jlat, *jlon, *jrad, *jio, *jdesc;
|
||||
|
||||
rc = mdb_cursor_get(cursor, &key, &data, op);
|
||||
if (rc != 0)
|
||||
break;
|
||||
|
||||
/* FIXME: strlen??? */
|
||||
if (memcmp(key_part, key.mv_data, strlen(key_part)) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* -1 because we 0-terminate strings */
|
||||
//printf("ENUM---- %*.*s %*.*s\n",
|
||||
// (int)key.mv_size, (int)key.mv_size, (char *)key.mv_data,
|
||||
// (int)data.mv_size - 1, (int)data.mv_size - 1, (char *)data.mv_data);
|
||||
|
||||
utstring_renew(ks);
|
||||
utstring_printf(ks, "%*.*s",
|
||||
(int)key.mv_size, (int)key.mv_size, (char *)key.mv_data);
|
||||
|
||||
if ((json = json_decode(data.mv_data)) == NULL)
|
||||
continue;
|
||||
|
||||
if ((jlat = json_find_member(json, "lat")) == NULL) continue;
|
||||
if ((jlon = json_find_member(json, "lon")) == NULL) continue;
|
||||
if ((jrad = json_find_member(json, "rad")) == NULL) continue;
|
||||
if ((jdesc = json_find_member(json, "desc")) == NULL) continue;
|
||||
if ((jio = json_find_member(json, "io")) == NULL) {
|
||||
json_append_member(json, "io", json_mkbool(false));
|
||||
jio = json_find_member(json, "io");
|
||||
}
|
||||
|
||||
wp.lat = jlat->number_;
|
||||
wp.lon = jlon->number_;
|
||||
wp.rad = (long)jrad->number_;
|
||||
wp.io = jio->bool_;
|
||||
wp.desc = strdup(jdesc->string_);
|
||||
|
||||
wp.ud = ud;
|
||||
wp.user = user;
|
||||
wp.device = device;
|
||||
|
||||
if (func && func(UB(ks), &wp, lat, lon) == true) {
|
||||
json_remove_from_parent(jio);
|
||||
json_append_member(json, "io", json_mkbool(wp.io));
|
||||
if (gcache_json_put(gc, UB(ks), json) != 0) {
|
||||
olog(LOG_ERR, "gcache_enum: cannot rewrite key %s", UB(ks));
|
||||
}
|
||||
}
|
||||
free(wp.desc);
|
||||
json_delete(json);
|
||||
|
||||
op = MDB_NEXT;
|
||||
} while (rc == 0);
|
||||
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_commit(txn);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
1
gcache.h
1
gcache.h
@@ -20,5 +20,6 @@ JsonNode *gcache_json_get(struct gcache *, char *key);
|
||||
void gcache_dump(char *path, char *lmdbname);
|
||||
void gcache_load(char *path, char *lmdbname);
|
||||
int gcache_del(struct gcache *gc, char *keystr);
|
||||
bool gcache_enum(char *user, char *device, struct gcache *gc, char *key_part, int (*func)(char *key, wpoint *wp, double lat, double lon), double lat, double lon, struct udata *ud);
|
||||
|
||||
#endif
|
||||
|
||||
31
hooks.c
31
hooks.c
@@ -32,9 +32,11 @@
|
||||
# include <lua.h>
|
||||
# include <lualib.h>
|
||||
# include <lauxlib.h>
|
||||
# include "fences.h"
|
||||
# include "gcache.h"
|
||||
# include "json.h"
|
||||
# include "version.h"
|
||||
# include "fences.h"
|
||||
|
||||
static int otr_log(lua_State *lua);
|
||||
static int otr_strftime(lua_State *lua);
|
||||
@@ -377,6 +379,35 @@ void hooks_hook(struct udata *ud, char *topic, JsonNode *fullo)
|
||||
hooks_hooklet(ud, topic, fullo);
|
||||
}
|
||||
|
||||
/*
|
||||
* This hook is invoked through fences.c when we determine that a movement
|
||||
* into or out of a geofence has caused a transition.
|
||||
*/
|
||||
|
||||
void hooks_transition(struct udata *ud, char *user, char *device, int event, char *desc, double wplat, double wplon, double lat, double lon)
|
||||
{
|
||||
JsonNode *json = json_mkobject();
|
||||
char *topic = "transition";
|
||||
|
||||
json_append_member(json, "_type", json_mkstring("event"));
|
||||
json_append_member(json, "user", json_mkstring(user));
|
||||
json_append_member(json, "device", json_mkstring(device));
|
||||
json_append_member(json, "desc", json_mkstring(desc));
|
||||
json_append_member(json, "event",
|
||||
event == ENTER ? json_mkstring("enter") : json_mkstring("leave"));
|
||||
json_append_member(json, "wplat", json_mknumber(wplat));
|
||||
json_append_member(json, "wplon", json_mknumber(wplon));
|
||||
json_append_member(json, "lat", json_mknumber(lat));
|
||||
json_append_member(json, "lon", json_mknumber(lon));
|
||||
|
||||
olog(LOG_DEBUG, "**** Lua hook for %s %s\n",
|
||||
event == ENTER ? "ENTER" : "LEAVE", desc);
|
||||
|
||||
do_hook("transition", ud, topic, json);
|
||||
json_delete(json);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* --- Here come the functions we provide to Lua scripts.
|
||||
*/
|
||||
|
||||
1
hooks.h
1
hooks.h
@@ -15,6 +15,7 @@ void hooks_exit(struct luadata *, char *reason);
|
||||
void hooks_hook(struct udata *ud, char *topic, JsonNode *obj);
|
||||
int hooks_norec(struct udata *ud, char *user, char *device, char *payload);
|
||||
JsonNode *hooks_http(struct udata *ud, char *user, char *device, char *payload);
|
||||
void hooks_transition(struct udata *ud, char *user, char *device, int event, char *desc, double wplat, double wplon, double lat, double lon);
|
||||
|
||||
#endif /* WITH_LUA */
|
||||
|
||||
|
||||
2
http.c
2
http.c
@@ -27,6 +27,8 @@
|
||||
#include "json.h"
|
||||
#include "util.h"
|
||||
#include "misc.h"
|
||||
#include "fences.h"
|
||||
#include "gcache.h"
|
||||
#include "storage.h"
|
||||
#include "geohash.h"
|
||||
#include "udata.h"
|
||||
|
||||
3
ocat.c
3
ocat.c
@@ -27,6 +27,9 @@
|
||||
# include <mosquitto.h>
|
||||
#endif
|
||||
#include "json.h"
|
||||
#include "udata.h"
|
||||
#include "fences.h"
|
||||
#include "gcache.h"
|
||||
#include "storage.h"
|
||||
#include "util.h"
|
||||
#include "misc.h"
|
||||
|
||||
24
recorder.c
24
recorder.c
@@ -34,6 +34,7 @@
|
||||
#include <sys/utsname.h>
|
||||
#include <regex.h>
|
||||
#include "recorder.h"
|
||||
#include "udata.h"
|
||||
#include "utstring.h"
|
||||
#include "geo.h"
|
||||
#include "geohash.h"
|
||||
@@ -41,6 +42,7 @@
|
||||
#include "misc.h"
|
||||
#include "util.h"
|
||||
#include "storage.h"
|
||||
#include "fences.h"
|
||||
#include "gcache.h"
|
||||
#ifdef WITH_HTTP
|
||||
# include "http.h"
|
||||
@@ -388,6 +390,8 @@ void waypoints_dump(struct udata *ud, UT_string *username, UT_string *device, ch
|
||||
}
|
||||
|
||||
xx_dump(ud, username, device, (js) ? js : payloadstring, "waypoints", "otrw");
|
||||
load_otrw_from_string(ud, UB(username), UB(device), (js) ? js : payloadstring);
|
||||
|
||||
if (js)
|
||||
free(js);
|
||||
}
|
||||
@@ -837,14 +841,6 @@ void handle_message(void *userdata, char *topic, char *payload, size_t payloadle
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Haversine */
|
||||
|
||||
{
|
||||
double d = haversine_dist(lat, lon, 52.03431, 8.47654);
|
||||
printf("*** d=%lf meters\n", d);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the topic we are handling is in topic2tid, replace the TID
|
||||
@@ -1055,7 +1051,8 @@ void handle_message(void *userdata, char *topic, char *payload, size_t payloadle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
check_fences(ud, UB(username), UB(device), lat, lon, json);
|
||||
|
||||
cleanup:
|
||||
if (geo) json_delete(geo);
|
||||
@@ -1481,6 +1478,12 @@ int main(int argc, char **argv)
|
||||
exit(2);
|
||||
}
|
||||
gcache_close(gt);
|
||||
|
||||
if ((gt = gcache_open(path, "wp", FALSE)) == NULL) {
|
||||
fprintf(stderr, "Cannot lmdb-open `wp'\n");
|
||||
exit(2);
|
||||
}
|
||||
gcache_close(gt);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -1552,7 +1555,9 @@ int main(int argc, char **argv)
|
||||
ud->keydb = gcache_open(err, "keys", TRUE);
|
||||
# endif
|
||||
ud->httpfriends = gcache_open(err, "friends", TRUE);
|
||||
ud->wpdb = gcache_open(err, "wp", FALSE);
|
||||
|
||||
load_fences(ud);
|
||||
|
||||
#if WITH_ENCRYPT
|
||||
if (sodium_init() == -1) {
|
||||
@@ -1716,6 +1721,7 @@ int main(int argc, char **argv)
|
||||
gcache_close(ud->gc);
|
||||
gcache_close(ud->t2t);
|
||||
gcache_close(ud->httpfriends);
|
||||
gcache_close(ud->wpdb);
|
||||
#ifdef WITH_LUA
|
||||
if (ud->luadb)
|
||||
gcache_close(ud->luadb);
|
||||
|
||||
172
storage.c
172
storage.c
@@ -32,8 +32,9 @@
|
||||
#include "utstring.h"
|
||||
#include "storage.h"
|
||||
#include "geohash.h"
|
||||
#include "fences.h"
|
||||
#include "gcache.h"
|
||||
#include "util.h"
|
||||
#include "udata.h"
|
||||
#include "listsort.h"
|
||||
|
||||
char STORAGEDIR[BUFSIZ] = STORAGEDEFAULT;
|
||||
@@ -405,8 +406,8 @@ int make_times(char *time_from, time_t *s_lo, char *time_to, time_t *s_hi, int h
|
||||
|
||||
static void ls(char *path, JsonNode *obj)
|
||||
{
|
||||
DIR *dirp;
|
||||
struct dirent *dp;
|
||||
DIR *dirp;
|
||||
struct dirent *dp;
|
||||
JsonNode *jarr = json_mkarray();
|
||||
|
||||
if (obj == NULL || obj->tag != JSON_OBJECT) {
|
||||
@@ -414,20 +415,20 @@ static void ls(char *path, JsonNode *obj)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((dirp = opendir(path)) == NULL) {
|
||||
if ((dirp = opendir(path)) == NULL) {
|
||||
json_append_member(obj, "error", json_mkstring("Cannot open requested directory"));
|
||||
json_delete(jarr);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while ((dp = readdir(dirp)) != NULL) {
|
||||
if ((*dp->d_name != '.') && (dp->d_type == DT_DIR)) {
|
||||
while ((dp = readdir(dirp)) != NULL) {
|
||||
if ((*dp->d_name != '.') && (dp->d_type == DT_DIR)) {
|
||||
json_append_element(jarr, json_mkstring(dp->d_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_append_member(obj, "results", jarr);
|
||||
closedir(dirp);
|
||||
closedir(dirp);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -503,7 +504,7 @@ static void lsscan(char *pathpat, time_t s_lo, time_t s_hi, JsonNode *obj, int r
|
||||
|
||||
if ((n = scandir(pathpat, &namelist, filter_filename, cmp)) < 0) {
|
||||
json_append_member(obj, "error", json_mkstring("Cannot lsscan requested directory"));
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If our obj contains the "results" array, use that
|
||||
@@ -926,7 +927,7 @@ static void append_to_feature_array(JsonNode *features, double lat, double lon,
|
||||
JsonNode *geo_json(JsonNode *location_array)
|
||||
{
|
||||
JsonNode *one, *j;
|
||||
JsonNode *feature_array, *fcollection;
|
||||
JsonNode *feature_array, *fcollection;
|
||||
|
||||
if ((fcollection = json_mkobject()) == NULL)
|
||||
return (NULL);
|
||||
@@ -1031,7 +1032,7 @@ JsonNode *geo_linestring(JsonNode *location_array)
|
||||
char *gpx_string(JsonNode *location_array)
|
||||
{
|
||||
JsonNode *one;
|
||||
static UT_string *xml = NULL;
|
||||
static UT_string *xml = NULL;
|
||||
|
||||
if (location_array->tag != JSON_ARRAY)
|
||||
return (NULL);
|
||||
@@ -1043,7 +1044,7 @@ char *gpx_string(JsonNode *location_array)
|
||||
<trk>\n\
|
||||
<trkseg>\n");
|
||||
|
||||
// <trkpt lat="xx.xxx" lon="yy.yyy"> <!-- Attribute des Trackpunkts --> </trkpt>
|
||||
// <trkpt lat="xx.xxx" lon="yy.yyy"> <!-- Attribute des Trackpunkts --> </trkpt>
|
||||
|
||||
json_foreach(one, location_array) {
|
||||
JsonNode *jlat, *jlon, *jisotst, *j;
|
||||
@@ -1108,7 +1109,7 @@ JsonNode *kill_datastore(char *user, char *device)
|
||||
json_append_member(obj, "status", json_mkstring("ERROR"));
|
||||
json_append_member(obj, "error", json_mkstring(strerror(errno)));
|
||||
json_append_member(obj, "reason", json_mkstring("cannot scandir"));
|
||||
return (obj);
|
||||
return (obj);
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
@@ -1516,3 +1517,142 @@ void extra_http_json(JsonNode *array, char *user, char *device)
|
||||
}
|
||||
free(js_string);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process an array of waypoints as read from an .otrw file. If
|
||||
* rad is positive and lat/lon exist, store in LMDB database for
|
||||
* this user.
|
||||
*/
|
||||
|
||||
static bool load_otrw_waypoints(struct udata *ud, JsonNode *wplist, char *user, char *device)
|
||||
{
|
||||
JsonNode *n;
|
||||
static UT_string *key;
|
||||
long len;
|
||||
char buf[20];
|
||||
|
||||
json_foreach(n, wplist) {
|
||||
JsonNode *rad, *lat, *lon, *desc, *tst, *type;
|
||||
|
||||
if ((type = json_find_member(n, "_type")) == NULL)
|
||||
return (false);
|
||||
|
||||
if (strcmp(type->string_, "waypoint") != 0)
|
||||
return (false);
|
||||
|
||||
json_remove_from_parent(type);
|
||||
|
||||
if ((rad = json_find_member(n, "rad")) == NULL)
|
||||
continue;
|
||||
if (rad->number_ <= 0)
|
||||
continue;
|
||||
|
||||
lat = json_find_member(n, "lat");
|
||||
lon = json_find_member(n, "lon");
|
||||
tst = json_find_member(n, "tst");
|
||||
desc = json_find_member(n, "desc");
|
||||
|
||||
/* It turns out tst was a key, but it breaks on iOS when dumped
|
||||
* waypoints are re-imported. we'll use the geohash of lat/lon here */
|
||||
|
||||
json_remove_from_parent(tst);
|
||||
|
||||
utstring_renew(key);
|
||||
utstring_printf(key, "%s-%s-%s", user, device,
|
||||
geohash_encode(lat->number_, lon->number_, 10));
|
||||
|
||||
printf("--> %s: %s\t(%lf, %lf) (%ld)\n", UB(key), desc->string_, lat->number_, lon->number_, (long)rad->number_);
|
||||
|
||||
/*
|
||||
* Just before initially storing in LMDB, we need to determine whether
|
||||
* device is currently within or without a waypoint... FIXME
|
||||
*/
|
||||
|
||||
/* Note: we don't need buf -- just checking if key exists */
|
||||
len = gcache_get(ud->wpdb, UB(key), buf, sizeof(buf));
|
||||
if (len == -1) {
|
||||
gcache_json_put(ud->wpdb, UB(key), n);
|
||||
}
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
void load_otrw_from_string(struct udata *ud, char *username, char *device, char *js_string)
|
||||
{
|
||||
JsonNode *node, *wplist;
|
||||
|
||||
if ((node = json_decode(js_string)) == NULL) {
|
||||
olog(LOG_ERR, "load_otrw_from_string: can't decode JSON from string for %s-%s\n", username, device);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((wplist = json_find_member(node, "waypoints"))) {
|
||||
load_otrw_waypoints(ud, wplist, username, device);
|
||||
}
|
||||
|
||||
json_delete(node);
|
||||
}
|
||||
|
||||
static int load_otrw_file(struct udata *ud, char *filename)
|
||||
{
|
||||
char *js_string, *bp, *parts[20], *user, *device;
|
||||
JsonNode *node, *wplist;
|
||||
|
||||
if ((js_string = slurp_file(filename, TRUE)) == NULL) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
if ((node = json_decode(js_string)) == NULL) {
|
||||
olog(LOG_ERR, "load_otrw_file: can't decode JSON from file s\n", filename);
|
||||
free(js_string);
|
||||
return (false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find username / device name from filename component of
|
||||
* path (i.e. "/user-device.otrw").
|
||||
*/
|
||||
|
||||
if ((bp = strrchr(filename, '/')) == NULL)
|
||||
return (false);
|
||||
if (splitter(bp+1, "-.", parts) != 3)
|
||||
return (false);
|
||||
|
||||
user = parts[0];
|
||||
device = parts[1];
|
||||
|
||||
if ((wplist = json_find_member(node, "waypoints"))) {
|
||||
load_otrw_waypoints(ud, wplist, user, device);
|
||||
}
|
||||
|
||||
splitterfree(parts);
|
||||
free(js_string);
|
||||
json_delete(node);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool load_fences(struct udata *ud)
|
||||
{
|
||||
static UT_string *path = NULL;
|
||||
int n, rc;
|
||||
glob_t results;
|
||||
|
||||
/* Get list of waypoint files */
|
||||
utstring_renew(path);
|
||||
utstring_printf(path, "%s/waypoints/*/*/*.otrw", STORAGEDIR);
|
||||
rc = glob(UB(path), 0, 0, &results);
|
||||
if (rc == 0) {
|
||||
|
||||
for (n = 0; n < results.gl_pathc; n++) {
|
||||
char *f = results.gl_pathv[n];
|
||||
|
||||
// puts(f);
|
||||
load_otrw_file(ud, f);
|
||||
}
|
||||
}
|
||||
globfree(&results);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
# define _STORAGE_H_INCL_
|
||||
|
||||
#include <time.h>
|
||||
#include "gcache.h"
|
||||
#include "json.h"
|
||||
#include "udata.h"
|
||||
|
||||
#define DEFAULT_HISTORY_HOURS 6
|
||||
|
||||
@@ -55,5 +55,7 @@ void csv_output(JsonNode *json, output_type otype, JsonNode *fields, void (*func
|
||||
char *storage_userphoto(char *username);
|
||||
void append_card_to_object(JsonNode *obj, char *user, char *device);
|
||||
void extra_http_json(JsonNode *array, char *user, char *device);
|
||||
void load_otrw_from_string(struct udata *ud, char *username, char *device, char *js);
|
||||
bool load_fences(struct udata *ud);
|
||||
|
||||
#endif
|
||||
|
||||
3
udata.h
3
udata.h
@@ -7,7 +7,7 @@
|
||||
# include <stdarg.h>
|
||||
# include "mongoose.h"
|
||||
#endif
|
||||
#include "gcache.h"
|
||||
// #include "gcache.h"
|
||||
|
||||
|
||||
struct udata {
|
||||
@@ -49,6 +49,7 @@ struct udata {
|
||||
char *geokey; /* Google reverse-geo API key */
|
||||
int debug; /* enable for debugging */
|
||||
struct gcache *httpfriends; /* lmdb named database 'friends' */
|
||||
struct gcache *wpdb; /* lmdb named database 'wp' (waypoints) */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user