/* * 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 CTLP_LUA_REGISTER("txc-binding"); struct signal { const char *name; afb_event_t event; }; static int playing; static int stoping; static struct signal signals[] = { { .name = "START" }, { .name = "STOP" }, { .name = "accelerator_pedal_position" }, { .name = "brake_pedal_status" }, { .name = "button_event" }, { .name = "door_status" }, { .name = "engine_speed" }, { .name = "fuel_consumed_since_restart" }, { .name = "fuel_level" }, { .name = "headlamp_status" }, { .name = "high_beam_status" }, { .name = "ignition_status" }, { .name = "latitude" }, { .name = "longitude" }, { .name = "odometer" }, { .name = "parking_brake_status" }, { .name = "steering_wheel_angle" }, { .name = "torque_at_transmission" }, { .name = "transmission_gear_position" }, { .name = "vehicle_speed" }, { .name = "windshield_wiper_status" } }; static struct signal *getsig(const char *name) { int low, hig, mid, cmp; low = 0; hig = sizeof signals / sizeof * signals; while (low < hig) { mid = (low + hig) >> 1; cmp = strcmp(signals[mid].name, name); if (cmp == 0) { return &signals[mid]; } else { if (cmp > 0) hig = mid; else low = mid + 1; } } return NULL; } static void send_trace(const char *name, struct json_object *object) { struct signal *sig = getsig(name); if (sig && afb_event_is_valid(sig->event)) { afb_event_push(sig->event, json_object_get(object)); } } struct playTracesArgs { struct json_object* jArgs; afb_api_t apiHandle; }; static void *play_traces(void *opaque) { int len, fd; struct playTracesArgs *ptArgs = (struct playTracesArgs*)opaque; struct json_object *args = ptArgs->jArgs; afb_api_t apiHandle = ptArgs->apiHandle; struct json_tokener *tokener = NULL; struct json_object *object; char line[1024]; FILE *file = NULL; const char *info; double speed = 1.0; int started = 0; long double init = 0.0; long double prev = 0.0; long double base = 0.0; long double t, i, f; struct json_object *ots; struct json_object *on; int hasots; int hason; struct timespec ts; /* creates the tokener */ tokener = json_tokener_new(); if (tokener == NULL) { info = "can't allocate tokener"; goto end; } /* get the speed */ if (json_object_object_get_ex(args, "speed", &object)) { speed = json_object_get_double(object); speed = speed <= 0 ? 1.0 : 1.0 / speed; } /* open the file */ if (!json_object_object_get_ex(args, "filename", &object)) { info = "can't find filename"; goto end; } fd = afb_api_rootdir_open_locale(apiHandle, json_object_get_string(object), O_RDONLY, NULL); if (fd < 0) { info = "can't open the file"; goto end; } file = fdopen(fd, "r"); if (file == NULL) { close(fd); info = "can't instanciate the file"; goto end; } /* send the start signal */ send_trace("START", NULL); /* reads the file and send its content */ while(!stoping && fgets(line, (int)sizeof line, file)) { len = (int)strlen(line); if (len && line[len-1] == '\n') len--; if (len) { json_tokener_reset(tokener); object = json_tokener_parse_ex(tokener, line, len); /* silently ignore errors ?!! */ if (object != NULL) { hason = json_object_object_get_ex(object, "name", &on); hasots = json_object_object_get_ex(object, "timestamp", &ots); if (hasots && hason) { if (started) { t = speed * (json_object_get_double(ots) - init); clock_gettime(CLOCK_REALTIME, &ts); } else { init = json_object_get_double(ots); started = 1; clock_gettime(CLOCK_REALTIME, &ts); base = (long double)ts.tv_sec + ((long double)ts.tv_nsec / 1000000000.0); t = 0; } if (t > prev) { f = modfl(base + t, &i); ts.tv_sec = (time_t)i; ts.tv_nsec = (long)(1000000000.0 * f); prev = t; clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, NULL); } t = t + (long double)ts.tv_sec + ((long double)ts.tv_nsec / 1000000000.0); json_object_object_add(object, "timestamp", json_object_new_double(t)); send_trace(json_object_get_string(on), object); } else { json_object_put(object); } } } } info = NULL; end: /* send the stop signal */ send_trace("STOP", NULL); /* cleanup */ if (file) fclose(file); if (tokener) json_tokener_free(tokener); json_object_put(args); /* terminate */ stoping = 0; playing = 0; return NULL; } CTLP_CAPI (start, source, argsJ, eventJ) { struct json_object *args, *a; int fd; pthread_t tid; /* check filename argument */ args = afb_req_json(source->request); if (!json_object_object_get_ex(args, "filename", &a)) { afb_req_fail(source->request, "error", "argument 'filename' is missing"); return -1; } fd = afb_api_rootdir_open_locale(source->api, json_object_get_string(a), O_RDONLY, NULL); if (fd < 0) { afb_req_fail(source->request, "error", "argument 'filename' is not a readable file"); return -1; } close(fd); /* check speed argument */ if (json_object_object_get_ex(args, "speed", &a)) { if (json_object_get_double(a) <= 0) { afb_req_fail(source->request, "error", "argument 'speed' is not a valid positive number"); return -1; } } /* check the state */ if (playing) { afb_req_fail(source->request, "error", "already playing"); return -1; } /* valid then try to start */ playing = 1; stoping = 0; struct playTracesArgs ptArgs = { json_object_get(args), source->api}; if (pthread_create(&tid, NULL, play_traces, &ptArgs) != 0) { playing = 0; afb_req_fail(source->request, "error", "can't start to play"); return -1; } afb_req_success(source->request, NULL, NULL); return 0; } CTLP_CAPI (stop, source, argsJ, eventJ) { if (playing) stoping = 1; afb_req_success(source->request, NULL, NULL); return 0; } static int subscribe_unsubscribe_sig(afb_api_t apiHandle, afb_req_t request, int subscribe, struct signal *sig) { if (!afb_event_is_valid(sig->event)) { if (!subscribe) return 1; sig->event = afb_api_make_event(apiHandle, sig->name); if (!afb_event_is_valid(sig->event)) { return 0; } } if (((subscribe ? afb_req_subscribe : afb_req_unsubscribe)(request, sig->event)) < 0) { return 0; } return 1; } static int subscribe_unsubscribe_all(afb_api_t apiHandle, afb_req_t request, int subscribe) { int i, n, e; n = sizeof signals / sizeof * signals; e = 0; for (i = 0 ; i < n ; i++) e += !subscribe_unsubscribe_sig(apiHandle, request, subscribe, &signals[i]); return e == 0; } static int subscribe_unsubscribe_name(afb_api_t apiHandle, afb_req_t request, int subscribe, const char *name) { struct signal *sig; if (0 == strcmp(name, "*")) return subscribe_unsubscribe_all(apiHandle, request, subscribe); sig = getsig(name); if (sig == NULL) { return 0; } return subscribe_unsubscribe_sig(apiHandle, request, subscribe, sig); } static void subscribe_unsubscribe(afb_api_t apiHandle, afb_req_t request, int subscribe) { int ok, i; size_t n; struct json_object *args, *a, *x; /* makes the subscription/unsubscription */ args = afb_req_json(request); if (args == NULL || !json_object_object_get_ex(args, "event", &a)) { ok = subscribe_unsubscribe_all(apiHandle, request, subscribe); } else if (json_object_get_type(a) != json_type_array) { ok = subscribe_unsubscribe_name(apiHandle, request, subscribe, json_object_get_string(a)); } else { n = json_object_array_length(a); ok = 0; for (i = 0 ; i < n ; i++) { x = json_object_array_get_idx(a, i); if (subscribe_unsubscribe_name(apiHandle, request, subscribe, json_object_get_string(x))) ok++; } ok = (ok == n); } /* send the report */ if (ok) afb_req_success(request, NULL, NULL); else afb_req_fail(request, "error", NULL); } CTLP_CAPI (subscribe, source, argsJ, eventJ){ subscribe_unsubscribe(source->api, source->request, 1); return 0; } CTLP_CAPI (unsubscribe, source, argsJ, eventJ){ subscribe_unsubscribe(source->api, source->request, 0); return 0; }