Initial code check-in 19/21119/1 halibut 7.99.1 7.99.2 7.99.3 8.0.0 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.0.6 8.99.1 8.99.2 8.99.3 8.99.4 8.99.5 halibut/7.99.1 halibut/7.99.2 halibut/7.99.3 halibut/8.0.0 halibut/8.0.1 halibut/8.0.2 halibut/8.0.3 halibut/8.0.4 halibut/8.0.5 halibut/8.0.6 halibut_7.99.1 halibut_7.99.2 halibut_7.99.3 halibut_8.0.0 halibut_8.0.1 halibut_8.0.2 halibut_8.0.3 halibut_8.0.4 halibut_8.0.5 halibut_8.0.6 icefish/8.99.1 icefish/8.99.2 icefish/8.99.3 icefish/8.99.4 icefish/8.99.5 icefish_8.99.1 icefish_8.99.2 icefish_8.99.3 icefish_8.99.4 icefish_8.99.5
authorScott Murray <scott.murray@konsulko.com>
Tue, 23 Apr 2019 01:38:18 +0000 (21:38 -0400)
committerScott Murray <scott.murray@konsulko.com>
Tue, 23 Apr 2019 02:42:06 +0000 (22:42 -0400)
A simple telematics demo application for AGL.  It reads vehicle and
engine speed from the CAN low-level binding and publishes them via
MQTT.

Change-Id: Ib85904e87919053cad1215b3f53cee81db25c94a
Signed-off-by: Scott Murray <scott.murray@konsulko.com>
23 files changed:
.gitignore [new file with mode: 0644]
.gitreview [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
app/CMakeLists.txt [new file with mode: 0644]
app/afbclient.cpp [new file with mode: 0644]
app/afbclient.h [new file with mode: 0644]
app/configuration.cpp [new file with mode: 0644]
app/configuration.h [new file with mode: 0644]
app/event.cpp [new file with mode: 0644]
app/event.h [new file with mode: 0644]
app/gps.cpp [new file with mode: 0644]
app/gps.h [new file with mode: 0644]
app/main.cpp [new file with mode: 0644]
app/mqttclient.cpp [new file with mode: 0644]
app/mqttclient.h [new file with mode: 0644]
app/network.cpp [new file with mode: 0644]
app/network.h [new file with mode: 0644]
autobuild/agl/autobuild [new file with mode: 0755]
autobuild/linux/autobuild [new file with mode: 0755]
conf.d/cmake/config.cmake [new file with mode: 0644]
conf.d/wgt/config.xml.in [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..b25c15b
--- /dev/null
@@ -0,0 +1 @@
+*~
diff --git a/.gitreview b/.gitreview
new file mode 100644 (file)
index 0000000..3a6dfb5
--- /dev/null
@@ -0,0 +1,5 @@
+[gerrit]
+host=gerrit.automotivelinux.org
+port=29418
+project=apps/agl-telematics-demo-recorder
+defaultbranch=master
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..bf284f7
--- /dev/null
@@ -0,0 +1,21 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# Author: romain Forlot <romain.forlot@iot.bzh>
+#
+# 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)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake)
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..b11c843
--- /dev/null
+++ b/README.md
@@ -0,0 +1,105 @@
+# Telematics Recorder
+
+## Overview
+
+The telematics recorder demonstation application reads the OBD-II vehicle speed and
+engine speed (RPM) messages using the low-level CAN binding, and sends them as JSON
+messages to a MQTT broker for clients to use.
+
+## Configuration
+
+The application has several configurable parameters, which can be configured via the
+INI style file /etc/xdg/telematics-recorder.conf. The parameters are described in the
+following sections.
+
+### General
+
+General configuration consists of the following parameters in the "[General]" section:
+* log_level
+* cellular_enabled
+* gps_enabled
+* check_online
+* device_uuid
+
+#### log_level
+
+Sets the debug logging level, acceptable values are 1 or higher. If the parameter is not
+present, logging is disabled. The default value is no debug logging.
+
+#### cellular_enabled
+
+Enables or disables cellular modem usage, acceptable values are "true" and "false" (case-insensitive).
+The logic enabled by this parameter blocks on a check for cellular modem presence at start up, and
+sets the modem state to online when detected. The default value is disabled ("false").
+
+#### gps_enabled
+
+Enables or disables GPS usage, acceptable values are "true" and "false" (case-insensitive).
+When enabled, the current GPS position will be populated in the events sent to the MQTT broker.
+The default value is disabled ("false").
+
+#### check_online
+
+Enables or disables checking for online status, acceptable values are "true" and "false" (case-insensitive).
+When enabled, the the current network online status will be checked before attempting to send messages to
+the MQTT broker to potentially prevent filling the transmit queue of the MQTT client.
+The default value is disabled ("false").
+
+#### device_uuid
+
+A string representing a unique device identifier. It is used to identify the device in the MQTT messages.
+If not specified, the default value is "e4bbc0a8-f435-4326-9769-d4a2c9f3c18d".
+
+### MQTT
+
+MQTT protocol configuration consists of the following parameters in the "[MQTT]" section:
+* broker
+* port
+* keepalive
+* qos
+* retain
+* username
+* password
+
+#### broker
+
+MQTT broker hostname. Default value is "iot.eclipse.org".
+
+#### port
+
+MQTT broker port. Default value is 1883.
+
+#### keepalive
+
+MQTT protocol keepalive time. Default value is 60 seconds.
+
+#### qos
+
+MQTT protocol quality of service (QoS) parameter, acceptable values are 0, 1, or 2.
+Default value is 0. See "Quality of Service" in [https://mosquitto.org/man/mqtt-7.html](https://mosquitto.org/man/mqtt-7.html)
+for a description of the QoS settings.
+
+#### retain
+
+MQTT protocol message retain parameter, acceptable values are "true" and "false" (case-insensitive). 
+Default value is "true". See "Retained Messages" in [https://mosquitto.org/man/mqtt-7.html](https://mosquitto.org/man/mqtt-7.html)
+for a description of the message retention mechanism.
+
+#### username
+
+MQTT broker username string if required. Default value is none.
+
+#### password
+
+MQTT broker password string if required. Default value is none, and only relevant in conjunction
+with the username parameter.
+
+### Event
+
+Event configuration consists of the following parameters in the "[Event]" section:
+* update_period
+
+#### update_period
+
+The period in seconds for sending messages to the MQTT broker, acceptable values are 0 or higher.
+A value of 0 means send a message to the broker for every CAN event that comes in. The default value is 10.
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
new file mode 100644 (file)
index 0000000..73470d5
--- /dev/null
@@ -0,0 +1,57 @@
+###########################################################################
+# Copyright 2019 Konsulko Group
+#
+# Author: Scott Murray <scott.murray@konsulko.com>
+#
+# 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.
+###########################################################################
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_CXX_STANDARD 14)
+
+find_package(PkgConfig REQUIRED)
+
+PROJECT_TARGET_ADD(telematics-recorder)
+
+add_executable(${TARGET_NAME}
+       main.cpp
+       configuration.cpp
+       afbclient.cpp
+       mqttclient.cpp
+       network.cpp
+       event.cpp
+       gps.cpp
+       ${RESOURCES}
+)
+
+pkg_check_modules(LIBAFBWSC REQUIRED libafbwsc)
+
+include_directories(
+         "${LIBAFBWSC_INCLUDE_DIRS}"
+)
+
+set_target_properties(${TARGET_NAME} PROPERTIES
+       LABELS "EXECUTABLE"
+       PREFIX ""
+       COMPILE_FLAGS "${EXTRAS_CFLAGS} -DFOR_AFB_BINDING"
+       LINK_FLAGS "${BINDINGS_LINK_FLAG}"
+       LINK_LIBRARIES "${EXTRAS_LIBRARIES}"
+       OUTPUT_NAME "${TARGET_NAME}"
+)
+
+target_link_libraries(${TARGET_NAME}
+       ${LIBAFBWSC_LIBRARIES}
+       -lmosquitto
+       -lpthread
+)
diff --git a/app/afbclient.cpp b/app/afbclient.cpp
new file mode 100644 (file)
index 0000000..a40f6c9
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#include "afbclient.h"
+#include <string>
+#include <cstring>
+#include <iostream>
+#include <mutex>
+#include <condition_variable>
+
+#undef DEBUG
+//#define DEBUG
+
+struct call_data
+{
+       bool sync;
+       std::mutex mutex;
+       std::condition_variable cv;
+       bool ready;
+       std::function<void(json_object*)> cb;
+       json_object *resp;
+};
+
+static void on_hangup_cb(void *closure, struct afb_wsj1 *wsj)
+{
+}
+
+static void on_call_cb(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg)
+{
+}
+
+static void on_reply_cb(void *closure, struct afb_wsj1_msg *msg)
+{
+       call_data *data = (call_data*) closure;
+       struct json_object* reply;
+
+       if(!data)
+               goto reply_done;
+
+       reply = afb_wsj1_msg_object_j(msg);
+       if(reply) {
+#ifdef DEBUG
+               std::cerr << __FUNCTION__ << ": reply = " << \
+                       json_object_to_json_string_ext(reply, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) << \
+                       std::endl;
+#endif
+               if(data->sync) {
+                       data->resp = reply;
+
+                       // Increase reference count since we are going to use
+                       // reply after this callback returns, caller must do a
+                       // put.
+                       json_object_get(reply);
+               } else if(data->cb != nullptr) {
+                       data->cb(reply);
+               }
+       }
+reply_done:
+       if(data->sync) {
+               // Signal reply is done
+               {
+                       std::lock_guard<std::mutex> lk(data->mutex);
+                       data->ready = true;
+               }
+               data->cv.notify_one();
+       }
+}
+
+//
+// on_event_cb is inline in afbclient.h
+//
+
+static void *afb_loop_thread(struct sd_event* loop)
+{
+       for(;;)
+               sd_event_run(loop, 30000000);
+}
+
+AfbClient::AfbClient(const int port, const std::string &token)
+{
+       std::string uri;
+
+       if(sd_event_new(&m_afb_loop) < 0) {
+               std::cerr << __FUNCTION__ << ": Failed to create event loop" << std::endl;
+               return;
+       }
+
+       // Initialize interface for websocket
+       m_itf.on_hangup = on_hangup_cb;
+       m_itf.on_call = on_call_cb;
+       m_itf.on_event = on_event_cb;
+
+       uri = "ws://localhost:" + std::to_string(port) + "/api?token=" + token;
+#ifdef DEBUG
+       std::cerr << "Using URI: " << uri << std::endl;
+#endif
+       m_ws = afb_ws_client_connect_wsj1(m_afb_loop, uri.c_str(), &m_itf, this);
+       if(m_ws) {
+               m_afb_thread = std::thread(afb_loop_thread, m_afb_loop);
+       } else {
+               std::cerr << __FUNCTION__ << ": Failed to create websocket connection" << std::endl;
+               goto error;
+       }
+
+       m_valid = true;
+       return;
+error:
+       if(m_afb_loop) {
+               sd_event_unref(m_afb_loop);
+               m_afb_loop = nullptr;
+       }
+       return;
+}
+
+AfbClient::~AfbClient(void)
+{
+       sd_event_unref(m_afb_loop);
+       afb_wsj1_unref(m_ws);
+}
+
+int AfbClient::call(const std::string &api, const std::string &verb, struct json_object *arg, callback_fn cb)
+{
+       if(!m_valid)
+               return -1;
+
+       call_data data;
+       data.sync = false;
+       data.cb = cb;
+       int rc = afb_wsj1_call_j(m_ws, api.c_str(), verb.c_str(), arg, on_reply_cb, (void*) &data);
+       if(rc < 0) {
+               std::cerr << __FUNCTION__ << \
+                       ": Failed to call " << \
+                       api.c_str() << \
+                       "/" << \
+                       verb.c_str() << \
+                       std::endl;
+       }
+       return rc;
+}
+
+int AfbClient::call_sync(const std::string &api, const std::string &verb, struct json_object *arg, struct json_object **resp)
+{
+       if(!m_valid)
+               return -1;
+
+       call_data data;
+       data.sync = true;
+       data.ready = false;
+       data.cb = nullptr;
+       int rc = afb_wsj1_call_j(m_ws, api.c_str(), verb.c_str(), arg, on_reply_cb, (void*) &data);
+       if(rc >= 0) {
+               // Wait for response
+               std::unique_lock<std::mutex> lk(data.mutex);
+               data.cv.wait(lk, [&]{ return data.ready; });
+
+               if(resp && data.resp)
+                       *resp = data.resp;
+       } else {
+               std::cerr << __FUNCTION__ << \
+                       ": Failed to call " << \
+                       api.c_str() << \
+                       "/" << \
+                       verb.c_str() << \
+                       std::endl;
+       }
+       return rc;
+}
+
+int AfbClient::subscribe(const std::string &api, const std::string &event, const std::string &eventString, callback_fn cb, const std::string &eventValueString)
+{
+       if(!m_valid)
+               return -1;
+
+       // For now, simply let the user over-write the callback if they
+       // specify the same eventString.  Avoiding that and keeping track
+       // of the duplicate eventStrings/callbacks will complicate things
+       // quite a bit.
+
+        struct json_object *j_obj = json_object_new_object();
+        json_object_object_add(j_obj, eventValueString.c_str(), json_object_new_string(event.c_str()));
+        int rc = call_sync(api, std::string("subscribe"), j_obj);
+       if(rc >= 0 && cb != nullptr) {
+               m_event_handlers[eventString] = cb;
+       }
+       return rc;
+}
+
+int AfbClient::unsubscribe(const std::string &api, const std::string &eventString, const std::string &eventValueString)
+{
+       if(!m_valid)
+               return -1;
+
+       if(m_event_handlers.find(eventString) == m_event_handlers.end())
+               return -1;
+
+        struct json_object *j_obj = json_object_new_object();
+        json_object_object_add(j_obj, eventValueString.c_str(), json_object_new_string(eventString.c_str()));
+        int rc = call_sync(api, std::string("unsubscribe"), j_obj);
+       if(rc >= 0) {
+               m_event_handlers.erase(eventString);
+       }
+       return rc;
+}
+
+void AfbClient::on_event(const char* event, struct afb_wsj1_msg *msg)
+{
+#if 0
+       std::cerr << __FUNCTION__ << ": event = " << event << std::endl;
+#endif
+       struct json_object *contents = afb_wsj1_msg_object_j(msg);
+#ifdef DEBUG
+       std::cerr << __FUNCTION__ << ": contents = " << \
+               json_object_to_json_string_ext(contents, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) << \
+               std::endl;
+#endif
+       struct json_object *data;
+       if(json_object_object_get_ex(contents, "data", &data)) {
+               auto i = m_event_handlers.find(std::string(event));
+               if (i != m_event_handlers.end() && i->second != nullptr) {
+                       i->second(data);
+               }
+       }
+}
diff --git a/app/afbclient.h b/app/afbclient.h
new file mode 100644 (file)
index 0000000..9a36581
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef AFBCLIENT_H
+#define AFBCLIENT_H
+
+#include <string>
+#include <thread>
+#include <map>
+#include <functional>
+#include <systemd/sd-event.h>
+#include <json-c/json.h>
+
+extern "C"
+{
+#include <afb/afb-wsj1.h>
+#include <afb/afb-ws-client.h>
+}
+
+class AfbClient
+{
+public:
+       AfbClient(int port, const std::string &token);
+       ~AfbClient();
+
+       using callback_fn = std::function<void(json_object*)>;
+
+       int call(const std::string &api, const std::string &verb, struct json_object* arg, callback_fn cb = nullptr);
+       int call_sync(const std::string &api, const std::string &verb, struct json_object* arg, struct json_object **resp = NULL);
+       int subscribe(const std::string &api, const std::string &event, const std::string &eventString, callback_fn cb, const std::string &eventValueString = "event");
+       int unsubscribe(const std::string &api, const std::string &eventString, const std::string &eventValueString = "event");
+
+       static void on_event_cb(void *closure, const char* event, struct afb_wsj1_msg *msg) {
+               if(closure)
+                       static_cast<AfbClient*>(closure)->on_event(event, msg);
+       }
+
+private:
+       struct afb_wsj1 *m_ws = nullptr;
+       struct afb_wsj1_itf m_itf;
+       std::thread m_afb_thread;
+       sd_event *m_afb_loop = nullptr;
+       bool m_valid = false;
+
+       std::map<const std::string, callback_fn> m_event_handlers;
+
+       void on_event(const char* event, struct afb_wsj1_msg *msg);
+};
+
+#endif // AFBCLIENT_H
diff --git a/app/configuration.cpp b/app/configuration.cpp
new file mode 100644 (file)
index 0000000..0762a4a
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#include "configuration.h"
+#include <glib.h>
+#include <strings.h>
+
+Configuration::Configuration(const std::string &filename, const std::string &device_uuid):
+       m_filename(filename),
+       m_device_uuid(device_uuid)
+{
+       read();
+}
+
+void Configuration::read(void)
+{
+       GKeyFile* conf_file;
+       GError *error = NULL;
+       char *value_str;
+       int n;
+
+       // Load settings from configuration file if it exists
+       conf_file = g_key_file_new();
+       if(!conf_file) {
+               return;
+       }
+
+       if(g_key_file_load_from_dirs(conf_file,
+                                    m_filename.c_str(),
+                                    (const gchar**) g_get_system_config_dirs(),
+                                    NULL,
+                                    G_KEY_FILE_KEEP_COMMENTS,
+                                    NULL) != TRUE) {
+               g_key_file_free(conf_file);
+               return;
+       }
+
+       //
+       // General
+       //
+
+       // Set log level if it is specified
+       error = NULL;
+       n = g_key_file_get_integer(conf_file, "General", "log_level", &error);
+       if(!error && n > 0) {
+               m_log_level = n;
+       }
+
+       value_str = g_key_file_get_string(conf_file, "General", "cellular_enabled", NULL);
+       if(value_str) {
+               if(!strcasecmp(value_str, "true")) {
+                       m_cellular_enabled = true;
+               } else if(!strcasecmp(value_str, "false")) {
+                       m_cellular_enabled = false;
+               }
+       }
+
+       value_str = g_key_file_get_string(conf_file, "General", "gps_enabled", NULL);
+       if(value_str) {
+               if(!strcasecmp(value_str, "true")) {
+                       m_gps_enabled = true;
+               } else if(!strcasecmp(value_str, "false")) {
+                       m_gps_enabled = false;
+               }
+       }
+
+       value_str = g_key_file_get_string(conf_file, "General", "check_online", NULL);
+       if(value_str) {
+               if(!strcasecmp(value_str, "true")) {
+                       m_check_online = true;
+               } else if(!strcasecmp(value_str, "false")) {
+                       m_check_online = false;
+               }
+       }
+
+       value_str = g_key_file_get_string(conf_file, "General", "device_uuid", NULL);
+       if(value_str) {
+               m_device_uuid = value_str;
+       }
+
+       //
+       // MQTT
+       //
+
+       value_str = g_key_file_get_string(conf_file, "MQTT", "broker", NULL);
+       if(value_str) {
+               m_mqtt_broker = value_str;
+       }
+
+       error = NULL;
+       n = g_key_file_get_integer(conf_file, "MQTT", "port", &error);
+       if(!error && n > 0) {
+               m_mqtt_port = n;
+       }
+
+       error = NULL;
+       n = g_key_file_get_integer(conf_file, "MQTT", "keepalive", &error);
+       if(!error && n >= 0) {
+               m_mqtt_keepalive = n;
+       }
+
+       error = NULL;
+       n = g_key_file_get_integer(conf_file, "MQTT", "qos", &error);
+       if(!error && n >= 0 && n < 3) {
+               m_mqtt_qos = n;
+       }
+
+       value_str = g_key_file_get_string(conf_file, "MQTT", "retain", NULL);
+       if(value_str) {
+               if(!strcasecmp(value_str, "true")) {
+                       m_mqtt_retain = true;
+               } else if(!strcasecmp(value_str, "false")) {
+                       m_mqtt_retain = false;
+               }
+       }
+
+       value_str = g_key_file_get_string(conf_file, "MQTT", "username", NULL);
+       if(value_str) {
+               m_mqtt_username = value_str;
+       }
+
+       value_str = g_key_file_get_string(conf_file, "MQTT", "password", NULL);
+       if(value_str) {
+               m_mqtt_password = value_str;
+       }
+
+       //
+       // Event
+       //
+
+       error = NULL;
+       n = g_key_file_get_integer(conf_file, "Event", "update_period", &error);
+       if(!error && n >= 0) {
+               m_update_period = n;
+       }
+
+       g_key_file_free(conf_file);
+}
diff --git a/app/configuration.h b/app/configuration.h
new file mode 100644 (file)
index 0000000..0e7a780
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef CONFIGURATION_H
+#define CONFIGURATION_H
+
+#include <string>
+
+class Configuration
+{
+public:
+       Configuration(const std::string &file, const std::string &device_uuid = "");
+       ~Configuration() {};
+
+       uint32_t getLogLevel() { return m_log_level; }
+       bool isCellularEnabled() { return m_cellular_enabled; }
+       bool isGpsEnabled() { return m_gps_enabled; }
+       bool getCheckOnline() { return m_check_online; }
+       std::string getDeviceUUID() { return std::string(m_device_uuid); }
+
+       std::string getMqttClientId() { return std::string(m_mqtt_client_id); }
+       std::string getMqttBroker() { return std::string(m_mqtt_broker); }
+       uint32_t getMqttPort() { return m_mqtt_port; }
+       uint32_t getMqttKeepalive() { return m_mqtt_keepalive; }
+       uint32_t getMqttQos() { return m_mqtt_qos; }
+       bool getMqttRetain() { return m_mqtt_retain; }
+       std::string getMqttUsername() { return std::string(m_mqtt_username); }
+       std::string getMqttPassword() { return std::string(m_mqtt_password); }
+
+       uint32_t getUpdatePeriod() { return m_update_period; }
+
+private:
+       void read();
+
+       std::string m_filename;
+
+       // General
+       uint32_t m_log_level = 0;
+       bool m_cellular_enabled = false;
+       bool m_gps_enabled = false;
+       bool m_check_online = false;
+       std::string m_device_uuid;
+
+       // MQTT broker
+       std::string m_mqtt_client_id = "";
+       std::string m_mqtt_broker = "iot.eclipse.org";
+       uint32_t m_mqtt_port = 1883;
+       uint32_t m_mqtt_keepalive = 60;
+       uint32_t m_mqtt_qos = 0;
+       bool m_mqtt_retain = true;
+       std::string m_mqtt_username = "";
+       std::string m_mqtt_password = "";
+
+       // Event
+       uint32_t m_update_period = 10;
+};
+
+#endif // CONFIGURATION_H
diff --git a/app/event.cpp b/app/event.cpp
new file mode 100644 (file)
index 0000000..8b2d5b6
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#include "event.h"
+#include "gps.h"
+#include "network.h"
+#include <string>
+#include <iostream>
+#include <json-c/json.h>
+#include <time.h>
+
+static uint64_t last_vehicle_speed_update_usecs;
+static uint64_t last_engine_speed_update_usecs;
+
+void process_event(Configuration &config, AfbClient &afbclient, MqttClient &mqttclient, event_data *event)
+{
+       std::string topic("agl-telematics-demo/");
+       topic += event->name;
+       struct json_object *j_obj = json_object_new_object();
+       struct json_object *j_device = json_object_new_string(config.getDeviceUUID().c_str());
+       json_object_object_add(j_obj, "device", j_device);
+       struct json_object *j_val = json_object_new_int(event->value);
+       json_object_object_add(j_obj, "value", j_val);
+       struct json_object *j_ts = json_object_new_int64(event->timestamp);
+       json_object_object_add(j_obj, "timestamp", j_ts);
+
+       location_data location;
+       struct json_object *j_location = json_object_new_object();
+       if(config.isGpsEnabled() && get_location(config, afbclient, &location)) {
+               struct json_object *j_latitude = json_object_new_double(location.latitude);
+               json_object_object_add(j_location, "latitude", j_latitude);
+               struct json_object *j_longitude = json_object_new_double(location.longitude);
+               json_object_object_add(j_location, "longitude", j_longitude);
+               struct json_object *j_speed = json_object_new_int(location.speed);
+               json_object_object_add(j_location, "speed", j_speed);
+               struct json_object *j_track = json_object_new_int(location.track);
+               json_object_object_add(j_location, "track", j_track);
+               struct json_object *j_timestamp = json_object_new_string(location.timestamp.c_str());
+               json_object_object_add(j_location, "timestamp", j_timestamp);
+       }
+       json_object_object_add(j_obj, "location", j_location);
+
+       std::string msg(json_object_to_json_string_ext(j_obj, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY));
+       if(config.getLogLevel() > 1) {
+               std::cerr << __FUNCTION__ << ": topic = " << topic << ", msg = " << msg << std::endl;
+       }
+
+       struct timespec now;
+       clock_gettime(CLOCK_MONOTONIC_RAW, &now);
+       uint64_t now_usecs = now.tv_sec * 1000000 + now.tv_nsec / 1000;
+       uint64_t *past_usecs = NULL;
+       if(event->name == "vehicle.speed")
+               past_usecs = &last_vehicle_speed_update_usecs;
+       else if(event->name == "engine.speed")
+               past_usecs = &last_engine_speed_update_usecs;
+       if(!config.getUpdatePeriod() ||
+          past_usecs &&
+          (!*past_usecs ||
+           ((now_usecs - *past_usecs) > (config.getUpdatePeriod() * 1000000)))) {
+               int rc = -1;
+               if(config.getCheckOnline() && check_online(afbclient)) {
+                       goto skip_update;
+               }
+               rc = mqttclient.publish(topic, msg, config.getMqttQos(), config.getMqttRetain());
+               if(rc != MOSQ_ERR_SUCCESS) {
+                       std::cerr << __FUNCTION__ << ": MQTT publish failed, rc = " << rc << std::endl;
+               } else if(config.getLogLevel() > 0) {
+                       std::cerr << __FUNCTION__ << ": MQTT publish succeeded" << std::endl;
+               }
+skip_update:
+               *past_usecs = now_usecs;
+       }
+}
diff --git a/app/event.h b/app/event.h
new file mode 100644 (file)
index 0000000..e3d8fec
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef EVENT_H
+#define EVENT_H
+
+#include "configuration.h"
+#include "afbclient.h"
+#include "mqttclient.h"
+
+struct event_data {
+       std::string name;
+       int32_t value;
+       uint64_t timestamp;
+};
+
+void process_event(Configuration &config, AfbClient &afbclient, MqttClient &mqttclient, event_data *event);
+
+#endif // EVENT_H
diff --git a/app/gps.cpp b/app/gps.cpp
new file mode 100644 (file)
index 0000000..79e97b4
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#include "gps.h"
+#include <string>
+#include <iostream>
+#include <json-c/json.h>
+
+bool get_location(Configuration &config, AfbClient &afbclient, location_data *location)
+{
+       bool rc;
+       struct json_object *j_resp = NULL;
+       std::string status;
+
+       if(!location)
+               return false;
+
+       if(afbclient.call_sync(std::string("gps"), std::string("location"), NULL, &j_resp) < 0)
+               return false;
+
+       if(config.getLogLevel() > 1) {
+               std::cerr << __FUNCTION__ << ": j_resp = " <<           \
+                       json_object_to_json_string_ext(j_resp, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) << \
+                       std::endl;
+       }
+
+       // Check status
+       rc = false;
+       struct json_object *j_request;
+       if(!json_object_object_get_ex(j_resp, "request", &j_request))
+               goto location_error;
+
+       struct json_object *j_status;
+       if(!json_object_object_get_ex(j_request, "status", &j_status))
+               goto location_error;
+
+       status = json_object_get_string(j_status);
+       if(status == "failed")
+               goto location_error;
+
+       // Get location data
+       struct json_object *j_response;
+       if(!json_object_object_get_ex(j_resp, "response", &j_response))
+               goto location_error;
+
+       struct json_object *j_latitude;
+       if(!json_object_object_get_ex(j_response, "latitude", &j_latitude))
+               goto location_error;
+
+       struct json_object *j_longitude;
+       if(!json_object_object_get_ex(j_response, "longitude", &j_longitude))
+               goto location_error;
+
+       struct json_object *j_speed;
+       if(!json_object_object_get_ex(j_response, "speed", &j_speed))
+               goto location_error;
+
+       struct json_object *j_track;
+       if(!json_object_object_get_ex(j_response, "track", &j_track))
+               goto location_error;
+
+       struct json_object *j_timestamp;
+       if(!json_object_object_get_ex(j_response, "timestamp", &j_timestamp))
+               goto location_error;
+
+       location->latitude = json_object_get_double(j_latitude);
+       location->longitude = json_object_get_double(j_longitude);
+       location->speed = json_object_get_int(j_speed);
+       location->track = json_object_get_int(j_track);
+       location->timestamp = json_object_get_string(j_timestamp);
+       rc = true;
+
+location_error:
+       json_object_put(j_resp);
+       return rc;
+}
diff --git a/app/gps.h b/app/gps.h
new file mode 100644 (file)
index 0000000..4d1badd
--- /dev/null
+++ b/app/gps.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef GPS_H
+#define GPS_H
+
+#include "configuration.h"
+#include "afbclient.h"
+
+struct location_data {
+       double latitude;
+       double longitude;
+       int32_t speed;
+       int32_t track;
+       std::string timestamp;
+};
+
+bool get_location(Configuration &config, AfbClient &afbclient, location_data *location);
+
+#endif // GPS_H
diff --git a/app/main.cpp b/app/main.cpp
new file mode 100644 (file)
index 0000000..2b07cc1
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#include <string>
+#include <iostream>
+#include <cstring>
+#include <deque>
+#include <mutex>
+#include <condition_variable>
+#include <json-c/json.h>
+#include "configuration.h"
+#include "afbclient.h"
+#include "mqttclient.h"
+#include "network.h"
+#include "event.h"
+
+#define CONFIGURATION_FILE     "telematics-recorder.conf"
+#define DEVICE_UUID            "e4bbc0a8-f435-4326-9769-d4a2c9f3c18d"
+
+static std::deque<event_data*> g_event_queue;
+static std::mutex g_event_queue_mutex;
+static bool g_event_queue_ready = false;
+static std::condition_variable g_event_queue_cv;
+
+static Configuration g_config(CONFIGURATION_FILE, DEVICE_UUID);
+
+void diagnostic_message_cb(json_object *data)
+{
+       if(!data)
+               return;
+
+       if(g_config.getLogLevel() > 2) {
+               std::cerr << __FUNCTION__ << ": data = " <<             \
+                       json_object_to_json_string_ext(data, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) << \
+                       std::endl;
+       }
+
+       struct json_object *j_name;
+       if(!json_object_object_get_ex(data, "name", &j_name))
+               return;
+       struct json_object *j_value;
+       if(!json_object_object_get_ex(data, "value", &j_value))
+               return;
+       struct json_object *j_timestamp;
+       if(!json_object_object_get_ex(data, "timestamp", &j_timestamp))
+               return;
+       uint64_t timestamp = json_object_get_int64(j_timestamp);
+
+       std::string name(json_object_get_string(j_name));
+       int32_t value = json_object_get_int(j_value);
+       if(name == "diagnostic_messages.vehicle.speed" ||
+          name == "diagnostic_messages.engine.speed") {
+               if(g_config.getLogLevel() > 1) {
+                       std::cerr << __FUNCTION__ << ": " << name << \
+                         ", value = " << value << \
+                         ", timestamp = " << timestamp << \
+                         std::endl;
+               }
+
+               name.erase(0, 20);
+               event_data* event = new event_data{ name, value, timestamp };
+               {
+                       std::lock_guard<std::mutex> lk(g_event_queue_mutex);
+                       g_event_queue.push_back(event);
+                       g_event_queue_ready = true;
+               }
+               g_event_queue_cv.notify_one();
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       int port = 0;
+       std::string token;
+
+       try {
+               port = std::stol(argv[1]);
+               token = argv[2];
+       } catch (const std::invalid_argument& e) {
+               std::cerr << "Invalid argument" << std::endl;
+               exit(1);
+       } catch (const std::out_of_range& e) {
+               std::cerr << "Port out of range" << std::endl;
+               exit(1);
+       }
+
+       AfbClient afbclient(port, token);
+
+       if(g_config.isCellularEnabled()) {
+               // Wait for modem to appear, and enable it if not already
+               enable_modem(g_config, afbclient);
+       }
+
+       std::string client_id = g_config.getMqttClientId();
+       if(client_id.empty())
+               client_id = std::string("AGL-telematics-recorder") + DEVICE_UUID;
+       MqttClient mqttclient(client_id,
+                             g_config.getMqttBroker(),
+                             g_config.getMqttPort(),
+                             g_config.getMqttKeepalive(),
+                             g_config.getMqttUsername(),
+                             g_config.getMqttPassword());
+
+       afbclient.subscribe(std::string("low-can"),
+                           std::string("diagnostic_messages.vehicle.speed"),
+                           std::string("low-can/diagnostic_messages"),
+                           diagnostic_message_cb);
+       afbclient.subscribe(std::string("low-can"),
+                           std::string("diagnostic_messages.engine.speed"),
+                           std::string("low-can/diagnostic_messages"),
+                           diagnostic_message_cb);
+
+       std::deque<event_data*> event_queue;
+       while(true) {
+               // Wait until event callback sends data
+               std::unique_lock<std::mutex> lk(g_event_queue_mutex);
+               g_event_queue_cv.wait(lk, []{ return g_event_queue_ready; });
+               if(!g_event_queue.empty()) {
+                       // copy out the events
+                       event_queue = g_event_queue;
+                       g_event_queue.clear();
+               }
+               g_event_queue_ready = false;
+               lk.unlock();
+
+               for(event_data *event : event_queue) {
+                       if(!event)
+                               continue;
+
+                       if(g_config.getLogLevel() > 0) {
+                               std::cerr << __FUNCTION__ << ": " << \
+                                       event->name << ", value = " << event->value << \
+                                       ", timestamp = " << event->timestamp << \
+                                       std::endl;
+                       }
+
+                       process_event(g_config, afbclient, mqttclient, event);
+
+                       delete event;
+               }
+               // Clear out processed events
+               event_queue.clear();
+       }
+       return 0;
+}
diff --git a/app/mqttclient.cpp b/app/mqttclient.cpp
new file mode 100644 (file)
index 0000000..f072013
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#include "mqttclient.h"
+#include <iostream>
+
+#undef DEBUG
+
+#ifdef DEBUG
+static void on_connect(struct mosquitto *mosq, void *obj, int rc)
+{
+       std::cerr << " MQTT Connected, rc = " << rc << std::endl;
+}
+
+static void on_disconnect(struct mosquitto *mosq, void *obj, int rc)
+{
+       std::cerr << " MQTT Disconnected, rc = " << rc << std::endl;
+}
+#endif
+
+MqttClient::MqttClient(const std::string &id, const std::string &host, const int port, const int keepalive, const std::string &username, const std::string &password)
+{
+       mosquitto_lib_init();
+       m_mosq = mosquitto_new(id.c_str(), true, NULL);
+
+#ifdef DEBUG
+       mosquitto_connect_callback_set(m_mosq, on_connect);
+       mosquitto_disconnect_callback_set(m_mosq, on_disconnect);
+#endif
+
+       if(username.length())
+               mosquitto_username_pw_set(m_mosq, username.c_str(), password.c_str());
+
+       if(mosquitto_connect_async(m_mosq, host.c_str(), port, keepalive)) {
+               std::cerr << __FUNCTION__ << ": Unable to connect to " << host << std::endl;
+       }
+
+       int loop = mosquitto_loop_start(m_mosq);
+       if(loop != MOSQ_ERR_SUCCESS){
+               std::cerr << __FUNCTION__ << ": Unable to start loop, error = " << loop << std::endl;
+       }
+}
+
+MqttClient::~MqttClient(void)
+{
+       mosquitto_disconnect(m_mosq);
+       mosquitto_loop_stop(m_mosq, true);
+       mosquitto_destroy(m_mosq);
+       mosquitto_lib_cleanup();
+}
+
+int MqttClient::publish(const std::string &topic, const std::string &msg, const int qos, const bool retain)
+{
+       return mosquitto_publish(m_mosq, NULL, topic.c_str(), msg.length(), msg.c_str(), qos, retain);
+}
diff --git a/app/mqttclient.h b/app/mqttclient.h
new file mode 100644 (file)
index 0000000..b7b893e
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef MQTTCLIENT_H
+#define MQTTCLIENT_H
+
+#include <string>
+#include <mosquitto.h>
+#include <json-c/json.h>
+
+class MqttClient
+{
+public:
+       MqttClient(const std::string &id, const std::string &host, const int port, const int keepalive = 60, const std::string &username = "", const std::string &password = "");
+         ~MqttClient();
+
+       int publish(const std::string &topic, const std::string &msg, const int qos, const bool retain);
+
+private:
+       mosquitto *m_mosq;
+};
+
+#endif // MQTTCLIENT_H
diff --git a/app/network.cpp b/app/network.cpp
new file mode 100644 (file)
index 0000000..d163ef5
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#include "network.h"
+#include <string>
+#include <iostream>
+#include <unistd.h>
+#include <json-c/json.h>
+
+int enable_modem(Configuration &config, AfbClient &afbclient)
+{
+       int rc;
+       struct json_object *j_resp = NULL;
+
+       bool cellular_enabled = false;
+       bool cellular_found = false;
+       while(!cellular_found) {
+               // Check current state
+               rc = afbclient.call_sync(std::string("network-manager"), std::string("technologies"), NULL, &j_resp);
+               if(rc < 0 || !j_resp)
+                       continue;
+               if(config.getLogLevel() > 1) {
+                       std::cerr << __FUNCTION__ << ": j_resp = " <<   \
+                               json_object_to_json_string_ext(j_resp, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) << \
+                               std::endl;
+               }
+
+               struct json_object *j_response;
+               if(!json_object_object_get_ex(j_resp, "response", &j_response))
+                       continue;
+
+               struct json_object *j_values;
+               if(!json_object_object_get_ex(j_response, "values", &j_values))
+                       continue;
+
+               for(int i = 0; i < json_object_array_length(j_values); i++) {
+                       struct json_object *j_value = json_object_array_get_idx(j_values, i);
+                       if(!j_value)
+                               break;
+
+                       struct json_object *j_technology;
+                       if(!json_object_object_get_ex(j_value, "technology", &j_technology))
+                               break;
+
+                       std::string technology(json_object_get_string(j_technology));
+                       if(technology == "cellular") {
+                               struct json_object *j_properties;
+                               if(!json_object_object_get_ex(j_value, "properties", &j_properties))
+                                       break;
+
+                               struct json_object *j_powered;
+                               if(!json_object_object_get_ex(j_properties, "powered", &j_powered))
+                                       break;
+
+                               if(json_object_get_boolean(j_powered)) {
+                                       if(config.getLogLevel() > 0) {
+                                               std::cerr << __FUNCTION__ << ": cellular enabled!" << std::endl;
+                                               cellular_enabled = true;
+                                       }
+                               }
+
+                               std::cerr << __FUNCTION__ << ": cellular found!" << std::endl;
+                               cellular_found = true;
+                               rc = 0;
+                               break;
+                       }
+                       
+               }
+               json_object_put(j_resp);
+
+               sleep(1);
+       }
+
+       if(!cellular_enabled) {
+               if(config.getLogLevel() > 0) {
+                       std::cerr << __FUNCTION__ << ": enabling cellular" << std::endl;
+               }
+               struct json_object *j_obj = json_object_new_object();
+               struct json_object *j_val = json_object_new_string("cellular");
+               json_object_object_add(j_obj, "technology", j_val);
+               rc = afbclient.call_sync(std::string("network-manager"), std::string("enable_technology"), j_obj);
+       }
+       return rc;
+}
+
+bool check_online(AfbClient &afbclient)
+{
+       int rc;
+       struct json_object *j_resp = NULL;
+
+       // Check current state
+       rc = afbclient.call_sync(std::string("network-manager"), std::string("state"), NULL, &j_resp);
+       if(rc < 0 || !j_resp)
+               return false;
+
+       struct json_object *j_response;
+       if(!json_object_object_get_ex(j_resp, "response", &j_response))
+               return false;
+
+       bool online = false;
+       std::string response(json_object_get_string(j_response));
+       if(response == "online")
+               online = true;
+       json_object_put(j_resp);
+       return online;
+}
diff --git a/app/network.h b/app/network.h
new file mode 100644 (file)
index 0000000..65d3330
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 Konsulko Group
+ *
+ * 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.
+ */
+
+#ifndef NETWORK_H
+#define NETWORK_H
+
+#include "configuration.h"
+#include "afbclient.h"
+
+int enable_modem(Configuration &config, AfbClient &afbclient);
+
+bool check_online(AfbClient &afbclient);
+
+#endif // NETWORK_H
diff --git a/autobuild/agl/autobuild b/autobuild/agl/autobuild
new file mode 100755 (executable)
index 0000000..85ddaec
--- /dev/null
@@ -0,0 +1,77 @@
+#!/usr/bin/make -f
+# Copyright (C) 2015 - 2018 "IoT.bzh"
+# Author "Romain Forlot" <romain.forlot@iot.bzh>
+#
+# 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}
+
+.PHONY: all clean distclean configure build package help update
+
+all: help
+
+help:
+       @echo "List of targets available:"
+       @echo ""
+       @echo "- all"
+       @echo "- clean"
+       @echo "- distclean"
+       @echo "- configure"
+       @echo "- build: compilation, link and prepare files for package into a widget"
+       @echo "- package: output a widget file '*.wgt'"
+       @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory"
+       @echo ""
+       @echo "Usage: ./autobuild/agl/autobuild package DEST=${HOME}/opt"
+       @echo "Don't use your build dir as DEST as wgt file is generated at this location"
+
+update: configure
+       @cmake --build ${BUILD_DIR} --target autobuild
+
+clean:
+       @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} ${CLEAN_ARGS} clean) || echo Nothing to clean
+
+distclean:
+       @rm -rf ${BUILD_DIR}
+
+configure:
+       @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
+       @[ -f ${BUILD_DIR}/Makefile -o -f ${BUILD_DIR}/build.ninja ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
+
+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}/$@/var
+       @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
+       @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
+               @mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}; \
+       fi
+
+package-test: 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}/$@/var
+       @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
+       @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target test_widget
+       @mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}
+
+install: build
+       @cmake --build ${BUILD_DIR} ${INSTALL_ARGS} --target install
diff --git a/autobuild/linux/autobuild b/autobuild/linux/autobuild
new file mode 100755 (executable)
index 0000000..83097ab
--- /dev/null
@@ -0,0 +1,67 @@
+#!/usr/bin/make -f
+# Copyright (C) 2015, 2016 "IoT.bzh"
+# Author "Romain Forlot" <romain.forlot@iot.bzh>
+#
+# 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 update
+
+all: help
+
+help:
+       @echo "List of targets available:"
+       @echo ""
+       @echo "- all"
+       @echo "- clean"
+       @echo "- distclean"
+       @echo "- configure"
+       @echo "- build: compilation, link and prepare files for package into a widget"
+       @echo "- package: output a widget file '*.wgt'"
+       @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory"
+       @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"
+
+update: configure
+       @cmake --build ${BUILD_DIR} --target autobuild
+
+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}/$@/var
+       @cmake --build ${BUILD_DIR} --target widget
+       @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST}
+
+install: build
+       @cmake --build ${BUILD_DIR} --target install
+
+${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 (file)
index 0000000..00ec204
--- /dev/null
@@ -0,0 +1,205 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+# Copyright 2019 Konsulko Group
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+# telematics-recorder: Scott Murray <scott.murray@konsulko.com>
+#
+# 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 telematics-recorder)
+set(PROJECT_PRETTY_NAME "Telematics Recorder Demo")
+set(PROJECT_DESCRIPTION "Telematics recorder demo application")
+set(PROJECT_URL "https://github.com/konsulko/agl-telematics-recorder")
+set(PROJECT_VERSION "1.0")
+set(PROJECT_ICON "icon.png")
+set(PROJECT_AUTHOR "Scott Murray")
+set(PROJECT_AUTHOR_MAIL "scott.murray@konsulko.com")
+set(PROJECT_LICENSE "APL2.0")
+set(PROJECT_LANGUAGES "CXX")
+
+# Where are stored the project configuration files
+set(PROJECT_APP_TEMPLATES_DIR "conf.d")
+
+# 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")
+
+# Which directories inspect to find CMakeLists.txt target files
+# set(PROJECT_SRC_DIR_PATTERN "*")
+
+# Compilation Mode (DEBUG, RELEASE)
+# ----------------------------------
+#set(BUILD_TYPE "DEBUG")
+set(BUILD_TYPE "RELEASE")
+
+#set(USE_EFENCE 1)
+
+# 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
+       afb-daemon
+       glib-2.0
+       gobject-2.0
+)
+
+# You can also consider to include libsystemd
+# -----------------------------------
+#list (APPEND PKG_REQUIRED_LIST libsystemd>=222)
+
+# Prefix path where will be installed the files
+# Default: /usr/local (need root permission to write in)
+# ------------------------------------------------------
+#set(INSTALL_PREFIX /opt/AGL CACHE PATH "INSTALL PREFIX PATH")
+
+# Customize link option
+# -----------------------------
+#list(APPEND link_libraries -an-option)
+
+# Compilation options definition
+# Use CMake generator expressions to specify only for a specific language
+# Values are prefilled with default options that is currently used.
+# Either separate options with ";", or each options must be quoted separately
+# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED !
+# ----------------------------------------------------------------------------
+#set(COMPILE_OPTIONS
+# -Wall
+# -Wextra
+# -Wconversion
+# -Wno-unused-parameter
+# -Wno-sign-compare
+# -Wno-sign-conversion
+# -Werror=maybe-uninitialized
+# -Werror=implicit-function-declaration
+# -ffunction-sections
+# -fdata-sections
+# -fPIC
+# CACHE STRING "Compilation flags")
+#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.")
+#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.")
+#set(PROFILING_COMPILE_OPTIONS
+# -g
+# -O0
+# -pg
+# -Wp,-U_FORTIFY_SOURCE
+# CACHE STRING "Compilation flags for PROFILING build type.")
+#set(DEBUG_COMPILE_OPTIONS
+# -g
+# -ggdb
+# -D_FORTIFY_SOURCE=2
+# CACHE STRING "Compilation flags for DEBUG build type.")
+#set(CCOV_COMPILE_OPTIONS
+# -g
+# -O2
+# --coverage
+# CACHE STRING "Compilation flags for CCOV build type.")
+#set(RELEASE_COMPILE_OPTIONS
+# -g
+# -O2
+# -D_FORTIFY_SOURCE=2
+# CACHE STRING "Compilation flags for RELEASE build type.")
+
+# Optional location for config.xml.in
+# -----------------------------------
+#set(WIDGET_ICON "\"conf.d/wgt/${PROJECT_ICON}\"" CACHE PATH "Path to the widget icon")
+set(WIDGET_CONFIG_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in" CACHE PATH "Path to widget config file template (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.native)
+
+# 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 bin/telematics-recorder)
+
+# 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 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 binder security token")
+set(AFB_REMPORT "1234" CACHE PATH "Default binder listening port")
+
+# Print a helper message when every thing is finished
+# ----------------------------------------------------
+#set(CLOSING_MESSAGE "Typical binding launch: cd ${CMAKE_BINARY_DIR}/package && afb-daemon --port=${AFB_REMPORT} --workdir=. --ldpaths=lib --roothttp=htdocs  --token=\"${AFB_TOKEN}\" --tracereq=common --verbose")
+set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt")
+
+# Optional schema validator about now only XML, LUA and JSON
+# are supported
+#------------------------------------------------------------
+#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler")
+#set(XML_CHECKER "xmllint" CACHE STRING "XML linter")
+#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter")
+
+#
+# This CMake module could be found at the following url:
+# https://gerrit.automotivelinux.org/gerrit/#/admin/projects/src/cmake-apps-module
+# -----------------------------------------------------------
+include(CMakeAfbTemplates)
diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in
new file mode 100644 (file)
index 0000000..ce45e87
--- /dev/null
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns="http://www.w3.org/ns/widgets" id="@PROJECT_NAME@" version="@PROJECT_VERSION@">
+  <name>@PROJECT_NAME@</name>
+  <icon src="@PROJECT_ICON@"/>
+  <content src="@WIDGET_ENTRY_POINT@" type="@WIDGET_TYPE@"/>
+  <description>@PROJECT_DESCRIPTION@</description>
+  <author>@PROJECT_AUTHOR@ &lt;@PROJECT_AUTHOR_MAIL@&gt;</author>
+  <license>@PROJECT_LICENSE@</license>
+  <feature name="urn:AGL:widget:required-api">
+    <param name="low-can" value="ws" />
+    <param name="gps" value="ws" />
+    <param name="network-manager" value="ws" />
+  </feature>
+  <feature name="urn:AGL:widget:required-permission">
+    <param name="urn:AGL:permission::system:run-by-default" value="required" />
+    <param name="urn:AGL:permission::public:no-htdocs" value="required" />
+  </feature>
+</widget>