aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorScott Murray <scott.murray@konsulko.com>2019-04-22 21:38:18 -0400
committerScott Murray <scott.murray@konsulko.com>2019-04-22 22:42:06 -0400
commitb523b0c8cc37a989bfb84a5ebefe1ec1f2b0cbfb (patch)
tree482f3fee892fc0a0a327d256bd25134fb6996e15
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>
-rw-r--r--.gitignore1
-rw-r--r--.gitreview5
-rw-r--r--CMakeLists.txt21
-rw-r--r--LICENSE201
-rw-r--r--README.md105
-rw-r--r--app/CMakeLists.txt57
-rw-r--r--app/afbclient.cpp236
-rw-r--r--app/afbclient.h63
-rw-r--r--app/configuration.cpp151
-rw-r--r--app/configuration.h71
-rw-r--r--app/event.cpp86
-rw-r--r--app/event.h32
-rw-r--r--app/gps.cpp89
-rw-r--r--app/gps.h33
-rw-r--r--app/main.cpp158
-rw-r--r--app/mqttclient.cpp68
-rw-r--r--app/mqttclient.h36
-rw-r--r--app/network.cpp119
-rw-r--r--app/network.h27
-rwxr-xr-xautobuild/agl/autobuild77
-rwxr-xr-xautobuild/linux/autobuild67
-rw-r--r--conf.d/cmake/config.cmake205
-rw-r--r--conf.d/wgt/config.xml.in18
23 files changed, 1926 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b25c15b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/.gitreview b/.gitreview
new file mode 100644
index 0000000..3a6dfb5
--- /dev/null
+++ b/.gitreview
@@ -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
index 0000000..bf284f7
--- /dev/null
+++ b/CMakeLists.txt
@@ -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
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
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
index 0000000..73470d5
--- /dev/null
+++ b/app/CMakeLists.txt
@@ -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
index 0000000..a40f6c9
--- /dev/null
+++ b/app/afbclient.cpp
@@ -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
index 0000000..9a36581
--- /dev/null
+++ b/app/afbclient.h
@@ -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
index 0000000..0762a4a
--- /dev/null
+++ b/app/configuration.cpp
@@ -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
index 0000000..0e7a780
--- /dev/null
+++ b/app/configuration.h
@@ -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
index 0000000..8b2d5b6
--- /dev/null
+++ b/app/event.cpp
@@ -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
index 0000000..e3d8fec
--- /dev/null
+++ b/app/event.h
@@ -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
index 0000000..79e97b4
--- /dev/null
+++ b/app/gps.cpp
@@ -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
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
index 0000000..2b07cc1
--- /dev/null
+++ b/app/main.cpp
@@ -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
index 0000000..f072013
--- /dev/null
+++ b/app/mqttclient.cpp
@@ -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
index 0000000..b7b893e
--- /dev/null
+++ b/app/mqttclient.h
@@ -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
index 0000000..d163ef5
--- /dev/null
+++ b/app/network.cpp
@@ -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
index 0000000..65d3330
--- /dev/null
+++ b/app/network.h
@@ -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
index 0000000..85ddaec
--- /dev/null
+++ b/autobuild/agl/autobuild
@@ -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
index 0000000..83097ab
--- /dev/null
+++ b/autobuild/linux/autobuild
@@ -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
index 0000000..00ec204
--- /dev/null
+++ b/conf.d/cmake/config.cmake
@@ -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
index 0000000..ce45e87
--- /dev/null
+++ b/conf.d/wgt/config.xml.in
@@ -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>