diff options
author | Andrey Shamanin <andrei.shamanin@orioninc.com> | 2020-10-16 11:17:53 +0300 |
---|---|---|
committer | Andrey Shamanin <andrei.shamanin@orioninc.com> | 2020-10-16 11:21:30 +0300 |
commit | 87c68a6bc0d655e796484d110fa5336322b171d0 (patch) | |
tree | 98891fb8c0090e59f7a9b6904a9d59f04e5b0e02 | |
parent | 329d8ba3fb46b8afc803ad4a9b286cd26141f2f8 (diff) |
Add AWS cloud support to agl-service-cloudproxy.needlefish_13.93.0needlefish/13.93.0marlin_12.93.0marlin_12.92.0marlin_12.91.0marlin_12.90.1marlin_12.90.0marlin/12.93.0marlin/12.92.0marlin/12.91.0marlin/12.90.1marlin/12.90.0lamprey_11.92.0lamprey_11.91.0lamprey/11.92.0lamprey/11.91.0koi_10.93.0koi_10.92.0koi_10.91.0koi/10.93.0koi/10.92.0koi/10.91.013.93.012.93.012.92.012.91.012.90.112.90.011.92.011.91.010.93.010.92.010.91.0
Bug-AGL: SPEC-3481
Signed-off-by: Andrey Shamanin <andrei.shamanin@orioninc.com>
Change-Id: I071d94a9dc2e1f455119124c1185bf135f4c5b78
52 files changed, 1315 insertions, 3612 deletions
@@ -173,5 +173,4 @@ incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - END OF TERMS AND CONDITIONS -
\ No newline at end of file + END OF TERMS AND CONDITIONS
\ No newline at end of file diff --git a/app_client_examples/README.md b/app_client_examples/README.md deleted file mode 100755 index 8edaddd..0000000 --- a/app_client_examples/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# Test cloud applications #
-
-## Description
-
-The test-cloud-app and telemetry-cloud-app are examples of an agl-cloudproxy-service client.
-They are used to demonstrate the possibilities of the cloudproxy service.
-
-## Applications
-
-* test-cloud-app: Simple application demonstrating the following possibilities:
- * Sending the messages to cloud over cloud proxy server
- * Receiving confirmations from cloud on the sent messages
- * Receiving messages from cloud
-
-
-* telemetry-cloud-app: Application which sends the GPS coordinates to the cloud
- * Sending of the messages with telemetry to the cloud
- * Receiving of the messages from the cloud
-
-Applications use CloudProxy client as high level interface for the messages sending and receiving
-
-## CloudProxy client
-
-CloudProxy client hides the communication with CloudProxy server. It incupsulates the following actions
-
-* Connection to **cloudporxy** over wsj1 websocket
-* Subscription to the event
-* Event loop handling
-
-**Note:** At the moment only two event types are supported
-1. Event_SendMessageConfirmation
-2. Event_ReceivedMessage
-
-### Usage of a Cloudproxy client:
-
-* Include the appropriate header to appliciation
-
- ```
- #include "cloudproxy-client/cloudproxyclient.h"
- ```
-
-* Initialization of the CloudProxy client inside the application:
-
-```
-CloudProxyClient* g_cloudproxyclient{nullptr};
-
-g_cloudproxyclient = new CloudProxyClient();
-g_cloudproxyclient->init(port, token.c_str());
-
-```
-
-### Message handling from/to the CloudProxy server:
-
-
-* Subsription to the Event_SendMessageConfirmation
-
-
-
-```
-g_cloudproxyclient->set_event_handler(CloudProxyClient::Event_SendMessageConfirmation, [](json_object* object){
- json_object *j_result;
- if(!json_object_object_get_ex(object, "result", &j_result))
- {
- qDebug("Can't read confirmation result");
- return;
- }
-
- qDebug("Confirmation result %d", (int)json_object_get_boolean(j_result));
- });
-
- ```
-
-
-
-* Subsription to the Event_SendMessageConfirmation
-
-```
-g_cloudproxyclient->set_event_handler(CloudProxyClient::Event_ReceivedMessage, [](json_object* object)
- json_object *event_data;
- const char* data_str{nullptr};
- if(!json_object_object_get_ex(object, "data", &event_data) ||
- data_str = json_object_get_string(event_data)) == nullptr)
- {
- qDebug("Can't read event data");
- return;
- }
-
- qDebug("Received data: %s", data_str);
- });
- ```
-
-
-* Message sending:
-
-
-```
-qDebug() << "sendMessage result: " << g_cloudproxyclient->sendMessage("{\"app_key\": \"app_value1111\"}");
-```
\ No newline at end of file diff --git a/app_client_examples/telemetry-cloud-app/app/TelemetryWorker.cpp b/app_client_examples/telemetry-cloud-app/app/TelemetryWorker.cpp deleted file mode 100755 index 2fb2a15..0000000 --- a/app_client_examples/telemetry-cloud-app/app/TelemetryWorker.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/*
- * Copyright (C) 2020 MERA
- *
- * 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 "TelemetryWorker.h"
-#include <QDebug>
-#include <nlohmann/json.hpp>
-#include <time.h>
-#include <stdlib.h>
-
-#include "cloudproxy-client/cloudproxyclient.h"
-
-/*
-left up: 56.327 43.981
-right up: 56.327 44.03
-right bottom: 56.3 44.03
-left bottom: 56.3 43.981
-
-start: 56.321674 44.006383
-top: 56.329702 44.021352
-
-diff -0.008028 -0.014969
-coeff lon/lat = 1,8646
-
-
-https://gps-coordinates.org/distance-between-coordinates.php
-*/
-
-
-static double drand(double min, double max)
-{
- double f = (double)rand() / RAND_MAX;
- return min + f * (max - min);
-}
-
-
-void TelemetryWorker::send()
-{
- const double lat_top{56.329702};
- const double lon_lat_coef{1.8646};
- const double lat_step{0.001}, lon_step{lat_step*lon_lat_coef};
-
- if (direction == 0 && lat >= lat_top)
- {
- direction = 1;
- qDebug() << "Change direction to " << direction;
- }
- else if (direction == 1 && lat <= init_lat)
- {
- direction = 2;
- qDebug() << "Change direction to " << direction;
- }
- else if (direction == 2 && lat <= (init_lat - (lat_top - init_lat)))
- {
- direction = 3;
- qDebug() << "Change direction to " << direction;
- }
- else if (direction == 3 && lat >= init_lat)
- {
- direction = 0;
- qDebug() << "Change direction to " << direction;
- }
-
-
- if (direction == 0)
- {
- lat += lat_step;
- lon += lon_step;
- }
- else if (direction == 1)
- {
- lat -= lat_step;
- lon += lon_step;
- }
- else if (direction == 2)
- {
- lat -= lat_step;
- lon -= lon_step;
- }
- else if (direction == 3)
- {
- lat += lat_step;
- lon -= lon_step;
- }
-
- nlohmann::json jmsg{
- {"tele_ver", "1.0"},
- {"lat", lat},
- {"lon", lon},
- {"alt", (size_t)(alt + drand(0, 10))},
- {"speed", (size_t)(speed + drand(20, 30))},
- {"ts", time(nullptr)}
- };
-
- {
- //WARN: remove
- const auto& msg{jmsg.dump()};
- qDebug() << "TelemetryWorker: " << msg.c_str();
- }
-
- if (cloudproxyclient)
- qDebug() << "TelemetryWorker: cloud sendMessage result: " << cloudproxyclient->sendMessage(jmsg.dump());
-
-}
diff --git a/app_client_examples/telemetry-cloud-app/app/TelemetryWorker.h b/app_client_examples/telemetry-cloud-app/app/TelemetryWorker.h deleted file mode 100755 index 11210cb..0000000 --- a/app_client_examples/telemetry-cloud-app/app/TelemetryWorker.h +++ /dev/null @@ -1,48 +0,0 @@ -/*
- * Copyright (C) 2020 MERA
- *
- * 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.
- */
-
-#pragma once
-
-#include <QTimer>
-
-class CloudProxyClient;
-
-class TelemetryWorker : public QTimer
-{
- Q_OBJECT
-public:
- TelemetryWorker(CloudProxyClient* cloudproxy, QObject* parent = nullptr)
- : QTimer(parent),
- cloudproxyclient{cloudproxy}
- {
- }
-
-public slots:
- void send();
-
-private:
- CloudProxyClient* cloudproxyclient{nullptr};
-
- int direction{0};
- const double init_lat{56.306};
- const double init_lon{44.070};
- double lat{init_lat};
- double lon{init_lon};
- double alt{300.0};
- double speed{500.0};
- uint32_t engine_rpm{3000};
- int32_t engine_temp{97};
-};
diff --git a/app_client_examples/telemetry-cloud-app/app/app.pri b/app_client_examples/telemetry-cloud-app/app/app.pri deleted file mode 100755 index 6d710e4..0000000 --- a/app_client_examples/telemetry-cloud-app/app/app.pri +++ /dev/null @@ -1,3 +0,0 @@ -TEMPLATE = app -QMAKE_LFLAGS += "-Wl,--hash-style=gnu -Wl,--as-needed" -DESTDIR = $${OUT_PWD}/../package/root/bin diff --git a/app_client_examples/telemetry-cloud-app/app/app.pro b/app_client_examples/telemetry-cloud-app/app/app.pro deleted file mode 100755 index 351d89d..0000000 --- a/app_client_examples/telemetry-cloud-app/app/app.pro +++ /dev/null @@ -1,35 +0,0 @@ -TARGET = telemetrycloudapp -QT += core gui gui-private - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -DEFINES += QT_DEPRECATED_WARNINGS - -SOURCES = main.cpp \ - mainwindow.cpp \ - TelemetryWorker.cpp \ - cloudproxy-client/cloudproxyclient.cpp - -HEADERS += mainwindow.h \ - ui_mainwindow.h \ - TelemetryWorker.h \ - cloudproxy-client/cloudproxyclient.h \ - cloudproxy-client/hmi-debug.h - -CONFIG += link_pkgconfig -PKGCONFIG += libhomescreen qlibwindowmanager qtappfw-core - -CONFIG(release, debug|release) { - QMAKE_POST_LINK = $(STRIP) --strip-unneeded $(TARGET) -} - -LIBS += -ljson-c -lafbwsc -lsystemd - -RESOURCES += \ - images/images.qrc - -include(app.pri) - -DISTFILES += \ - images/AGL_HMI_Blue_Background_NoCar-01.png - diff --git a/app_client_examples/telemetry-cloud-app/app/cloudproxy-client/cloudproxyclient.cpp b/app_client_examples/telemetry-cloud-app/app/cloudproxy-client/cloudproxyclient.cpp deleted file mode 100755 index 0a30446..0000000 --- a/app_client_examples/telemetry-cloud-app/app/cloudproxy-client/cloudproxyclient.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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 <thread> -#include <algorithm> - -#include "cloudproxyclient.h" -#include "hmi-debug.h" - - -static const char API_name[] = "cloudproxy"; - -const std::vector<std::string> CloudProxyClient::m_api_list { - std::string("ping"), - std::string("createConnection"), - std::string("sendMessage"), - std::string("destroyConnection") - }; - -const std::vector<std::string> CloudProxyClient::m_event_list { - std::string("sendMessageConfirmation"), - std::string("receivedMessage") - }; - -static void event_loop_run(sd_event* loop) -{ - sd_event_loop(loop); - sd_event_unref(loop); -} - -static void on_hangup(void *closure, afb_wsj1 *wsj) -{ - HMI_DEBUG("cloudproxyclient", "called"); - (void)closure; - (void)wsj; -} - -static void on_call(void *closure, const char *api, const char *verb, afb_wsj1_msg *msg) -{ - HMI_ERROR("cloudproxyclient", "this method should not be called"); - (void)closure; - (void)api; - (void)verb; - (void)msg; -} - -static void on_event(void* closure, const char* event, afb_wsj1_msg *msg) -{ - HMI_DEBUG("cloudproxyclient", "event [%s]", (event ? event: "")); - (void)closure; - - static_cast<CloudProxyClient*>(closure)->on_event(nullptr, event, msg); -} - -static void on_reply(void *closure, afb_wsj1_msg *msg) -{ - HMI_DEBUG("cloudproxyclient", "called"); - (void)closure; - (void)msg; -} - - -CloudProxyClient::CloudProxyClient() -{ -} - -CloudProxyClient::~CloudProxyClient() -{ - if(m_websock) - { - afb_wsj1_unref(m_websock); - } - if(m_loop) - { - sd_event_exit(m_loop, 0); - } -} - -int CloudProxyClient::init(const int port, const std::string& token) -{ - int ret = 0; - if(port <= 0 && token.size() == 0) - { - HMI_ERROR("cloudproxyclient","port and token should be > 0, Initial port and token uses."); - return -1; - } - - { - m_loop = nullptr; - int ret = sd_event_new(&m_loop); - if(ret < 0) - { - HMI_ERROR("cloudproxyclient","Failed to create event loop"); - return -1; - } - - { - // enforce context to avoid initialization/goto error - std::thread th(event_loop_run, m_loop); - th.detach(); - } - - /* Initialize interface from websocket */ - m_itf.on_hangup = ::on_hangup; - m_itf.on_call = ::on_call; - m_itf.on_event = ::on_event; - - m_uri += "ws://localhost:" + std::to_string(port) + "/api?token=" + token; - - m_websock = afb_ws_client_connect_wsj1(m_loop, m_uri.c_str(), &m_itf, this); - if(!m_websock) - { - HMI_ERROR("cloudproxyclient","Failed to create websocket connection"); - return -1; - } - } - - HMI_DEBUG("cloudproxyclient", "Initialized"); - - return ret; -} - - -int CloudProxyClient::call(const std::string& verb, json_object* arg) -{ - int ret; - if(!m_websock) - { - return -1; - } - - if (verb.empty() || m_api_list.end() == std::find(m_api_list.begin(), m_api_list.end(), verb)) - { - HMI_ERROR("cloudproxyclient","verb [%s] doesn't exit", verb.c_str()); - return -1; - } - - ret = afb_wsj1_call_j(m_websock, API_name, verb.c_str(), arg, ::on_reply, this); - if (ret < 0) - { - HMI_ERROR("cloudproxyclient", "Failed to call verb:%s", verb.c_str()); - } - - return ret; -} - - -int CloudProxyClient::sendMessage(const std::string& data) -{ - if(!m_websock) - return -1; - - json_object* j_obj = json_object_new_object(); - json_object_object_add(j_obj, "data", json_object_new_string(data.c_str())); - - return this->call("sendMessage", j_obj); -} - -void CloudProxyClient::set_event_handler(enum EventType et, handler_func f) -{ - if (et > Event_Min && et < Event_Max) - { - switch (et) - { - case Event_SendMessageConfirmation: - this->subscribe(CloudProxyClient::m_event_list[0]); - break; - case Event_ReceivedMessage: - this->subscribe(CloudProxyClient::m_event_list[1]); - break; - default: - break; - } - - this->handlers[et] = std::move(f); - } -} - -int CloudProxyClient::subscribe(const std::string& event_name) -{ - if(!m_websock) - return -1; - - json_object* j_obj = json_object_new_object(); - json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str())); - - int ret = afb_wsj1_call_j(m_websock, API_name, "subscribe", j_obj, ::on_reply, this); - if (ret < 0) - HMI_ERROR("cloudproxyclient", "subscribe filed for '%s'", event_name.c_str()); - - return ret; -} - -int CloudProxyClient::unsubscribe(const std::string& event_name) -{ - if(!m_websock) - return -1; - - json_object* j_obj = json_object_new_object(); - json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str())); - - int ret = afb_wsj1_call_j(m_websock, API_name, "unsubscribe", j_obj, ::on_reply, this); - if (ret < 0) - HMI_ERROR("cloudproxyclient", "unsubscribe filed for '%s'", event_name.c_str()); - - return ret; -} - -void CloudProxyClient::on_event(void *closure, const char *event, afb_wsj1_msg *msg) -{ - HMI_DEBUG("cloudproxyclient", "event: (%s) msg: (%s).", event, afb_wsj1_msg_object_s(msg)); - (void) closure; - - if (strstr(event, API_name) == nullptr) - return; - - - json_object* ev_contents = afb_wsj1_msg_object_j(msg); - json_object *json_data; - if(!json_object_object_get_ex(ev_contents, "data", &json_data)) - { - HMI_ERROR("cloudproxyclient", "got ev_contents error."); - return; - } - - const char* event_type = nullptr; - json_object *json_event_type; - if(!json_object_object_get_ex(json_data, "type", &json_event_type) || - (event_type = json_object_get_string(json_event_type)) == nullptr) - { - HMI_ERROR("cloudproxyclient", "event_type is null."); - return; - } - - const std::string et{event_type}; - if (CloudProxyClient::m_event_list[0] == et) - { - auto i = this->handlers.find(Event_SendMessageConfirmation); - if (i != this->handlers.end()) - i->second(json_data); - } - else if (CloudProxyClient::m_event_list[1] == et) - { - auto i = this->handlers.find(Event_ReceivedMessage); - if (i != this->handlers.end()) - i->second(json_data); - } -} diff --git a/app_client_examples/telemetry-cloud-app/app/cloudproxy-client/cloudproxyclient.h b/app_client_examples/telemetry-cloud-app/app/cloudproxy-client/cloudproxyclient.h deleted file mode 100755 index b333a14..0000000 --- a/app_client_examples/telemetry-cloud-app/app/cloudproxy-client/cloudproxyclient.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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. - */ - -#pragma once - -#include <vector> -#include <map> -#include <string> -#include <functional> -#include <json-c/json.h> -#include <systemd/sd-event.h> - -extern "C" -{ -#include <afb/afb-wsj1.h> -#include <afb/afb-ws-client.h> -} - -class CloudProxyClient -{ -public: - using handler_func = std::function<void(json_object*)>; - - enum EventType { - Event_Min, - Event_SendMessageConfirmation = 1, - Event_ReceivedMessage, - Event_Max - }; - - - CloudProxyClient(); - ~CloudProxyClient(); - - CloudProxyClient(const CloudProxyClient &) = delete; - CloudProxyClient &operator=(const CloudProxyClient &) = delete; - - int init(const int port, const std::string& token); - - int sendMessage(const std::string& data); - - void set_event_handler(enum EventType et, handler_func f); - - void on_event(void *closure, const char *event, struct afb_wsj1_msg *msg); - - int subscribe(const std::string& event_name); - int unsubscribe(const std::string& event_name); - -private: - int call(const std::string& verb, struct json_object* arg); - -private: - static const std::vector<std::string> m_api_list; - static const std::vector<std::string> m_event_list; - - std::map<EventType, handler_func> handlers; - struct afb_wsj1_itf m_itf; - struct afb_wsj1* m_websock{nullptr}; - sd_event* m_loop{nullptr}; - std::string m_uri; -}; diff --git a/app_client_examples/telemetry-cloud-app/app/cloudproxy-client/hmi-debug.h b/app_client_examples/telemetry-cloud-app/app/cloudproxy-client/hmi-debug.h deleted file mode 100755 index 674f5df..0000000 --- a/app_client_examples/telemetry-cloud-app/app/cloudproxy-client/hmi-debug.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2018 TOYOTA MOTOR CORPORATION - * - * 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 __HMI_DEBUG_H__ -#define __HMI_DEBUG_H__ - -#include <time.h> -#include <stdio.h> -#include <stdarg.h> -#include <string.h> -#include <stdlib.h> - -enum LOG_LEVEL{ - LOG_LEVEL_NONE = 0, - LOG_LEVEL_ERROR, - LOG_LEVEL_WARNING, - LOG_LEVEL_NOTICE, - LOG_LEVEL_INFO, - LOG_LEVEL_DEBUG, - LOG_LEVEL_MAX = LOG_LEVEL_DEBUG -}; - -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) - -#define HMI_ERROR(prefix, args,...) _HMI_LOG(LOG_LEVEL_ERROR, __FILENAME__, __FUNCTION__, __LINE__, prefix, args, ##__VA_ARGS__) -#define HMI_WARNING(prefix, args,...) _HMI_LOG(LOG_LEVEL_WARNING, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) -#define HMI_NOTICE(prefix, args,...) _HMI_LOG(LOG_LEVEL_NOTICE, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) -#define HMI_INFO(prefix, args,...) _HMI_LOG(LOG_LEVEL_INFO, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) -#define HMI_DEBUG(prefix, args,...) _HMI_LOG(LOG_LEVEL_DEBUG, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) - -static char ERROR_FLAG[6][20] = {"NONE", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"}; - -static void _HMI_LOG(enum LOG_LEVEL level, const char* file, const char* func, const int line, const char* prefix, const char* log, ...) -{ - const int log_level = (getenv("USE_HMI_DEBUG") == NULL)?LOG_LEVEL_ERROR:atoi(getenv("USE_HMI_DEBUG")); - if(log_level < level) - { - return; - } - - char *message; - struct timespec tp; - unsigned int time; - - clock_gettime(CLOCK_REALTIME, &tp); - time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - - va_list args; - va_start(args, log); - if (log == NULL || vasprintf(&message, log, args) < 0) - message = NULL; - fprintf(stderr, "[%10.3f] [%s %s] [%s, %s(), Line:%d] >>> %s \n", time / 1000.0, prefix, ERROR_FLAG[level], file, func, line, message); - va_end(args); - free(message); -} - -#endif //__HMI_DEBUG_H__ diff --git a/app_client_examples/telemetry-cloud-app/app/images/AGL_HMI_Blue_Background_NoCar-01.png b/app_client_examples/telemetry-cloud-app/app/images/AGL_HMI_Blue_Background_NoCar-01.png Binary files differdeleted file mode 100755 index a76b140..0000000 --- a/app_client_examples/telemetry-cloud-app/app/images/AGL_HMI_Blue_Background_NoCar-01.png +++ /dev/null diff --git a/app_client_examples/telemetry-cloud-app/app/images/images.qrc b/app_client_examples/telemetry-cloud-app/app/images/images.qrc deleted file mode 100755 index 78d9b77..0000000 --- a/app_client_examples/telemetry-cloud-app/app/images/images.qrc +++ /dev/null @@ -1,5 +0,0 @@ -<RCC> - <qresource prefix="/images"> - <file>AGL_HMI_Blue_Background_NoCar-01.png</file> - </qresource> -</RCC> diff --git a/app_client_examples/telemetry-cloud-app/app/main.cpp b/app_client_examples/telemetry-cloud-app/app/main.cpp deleted file mode 100755 index e0551fe..0000000 --- a/app_client_examples/telemetry-cloud-app/app/main.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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 <QtCore/QUrlQuery> -#include <QtGui/QGuiApplication> -#include <libhomescreen.hpp> -#include <qlibwindowmanager.h> -#include <QtCore/QCommandLineParser> -#include <QScopedPointer> - -#include "mainwindow.h" -#include <QApplication> -#include <QDebug> - -#include "cloudproxy-client/cloudproxyclient.h" - - -CloudProxyClient* g_cloudproxyclient{nullptr}; - - -int main(int argc, char *argv[]) -{ - QString graphic_role = QString("telemetrycloud"); - - QApplication app(argc, argv); - QScopedPointer<MainWindow> window; - - QCommandLineParser parser; - parser.addPositionalArgument("port", app.translate("main", "port for binding")); - parser.addPositionalArgument("secret", app.translate("main", "secret for binding")); - parser.addHelpOption(); - parser.addVersionOption(); - parser.process(app); - QStringList positionalArguments = parser.positionalArguments(); - - if (positionalArguments.length() == 2) - { - int port = positionalArguments.takeFirst().toInt(); - QString secret = positionalArguments.takeFirst(); - QUrl bindingAddress; - bindingAddress.setScheme(QStringLiteral("ws")); - bindingAddress.setHost(QStringLiteral("localhost")); - bindingAddress.setPort(port); - bindingAddress.setPath(QStringLiteral("/api")); - QUrlQuery query; - query.addQueryItem(QStringLiteral("token"), secret); - bindingAddress.setQuery(query); - - std::string token = secret.toStdString(); - LibHomeScreen* hs = new LibHomeScreen(); - QLibWindowmanager* qwm = new QLibWindowmanager(); - - // WindowManager - if(qwm->init(port,secret) != 0){ - exit(EXIT_FAILURE); - } - AGLScreenInfo screenInfo(qwm->get_scale_factor()); - window.reset(new MainWindow(screenInfo.scale_factor())); - - // Request a surface as described in layers.json windowmanager’s file - if (qwm->requestSurface(graphic_role) != 0) { - exit(EXIT_FAILURE); - } - // Create an event callback against an event type. Here a lambda is called when SyncDraw event occurs - qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, &graphic_role](json_object *object) { - (void)object; - qDebug() << "Surface got syncDraw!"; - qwm->endDraw(graphic_role); - }); - - // HomeScreen - hs->init(port, token.c_str()); - // Set the event handler for Event_ShowWindow which will activate the surface for windowmanager - hs->set_event_handler(LibHomeScreen::Event_ShowWindow, [qwm, &graphic_role](json_object *object){ - (void)object; - qDebug("Surface %s got showWindow\n", graphic_role.toStdString().c_str()); - qwm->activateWindow(graphic_role); - }); - - g_cloudproxyclient = new CloudProxyClient(); - g_cloudproxyclient->init(port, token.c_str()); - - g_cloudproxyclient->set_event_handler(CloudProxyClient::Event_ReceivedMessage, [&window](json_object* object){ - qDebug("CloudProxyClient::Event_ReceivedMessage: object ptr %p", object); - - const char* str = object ? json_object_to_json_string_ext(object, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) : "<obj_null>"; - if (!str) - str = "<empty>"; - qDebug("Event_SendMessageConfirmation: %s", str); - - json_object *event_data; - const char* data_str{nullptr}; - if(!json_object_object_get_ex(object, "data", &event_data) || - (data_str = json_object_get_string(event_data)) == nullptr) - { - qDebug("Can't read event data"); - return; - } - - window->updateState(QString(data_str)); - qDebug("Application received data: %s", data_str); - }); - - window->show(); - qwm->slotActivateWindow(); - } - - return app.exec(); -} diff --git a/app_client_examples/telemetry-cloud-app/app/mainwindow.cpp b/app_client_examples/telemetry-cloud-app/app/mainwindow.cpp deleted file mode 100755 index aecabc0..0000000 --- a/app_client_examples/telemetry-cloud-app/app/mainwindow.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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 "mainwindow.h" -#include "ui_mainwindow.h" -#include <QDebug> - -#include <unistd.h> -#include <sys/stat.h> -#include <nlohmann/json.hpp> - - -#include "cloudproxy-client/cloudproxyclient.h" -extern CloudProxyClient* g_cloudproxyclient; - - -MainWindow::MainWindow(double scale_factor, QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) -{ - ui->setupUi(scale_factor, this); - - //AGL style - setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint); - this->setStyleSheet( - "background-image:url(:/images/AGL_HMI_Blue_Background_NoCar-01.png) 0 0 0 0 stretch stretch; background: transparent;" ); -} - -void MainWindow::updateState(QVariant recvMsg) -{ - if (!recvMsg.isValid()) - return; - - // expected format: {"message": "bla-bla"} - std::string cloud_msg; - try - { - const std::string& msg = recvMsg.toString().toStdString(); - if (msg.empty()) - { - qDebug() << "Received message is empty"; - } - else - { - const nlohmann::json& jmsg = nlohmann::json::parse(msg); - cloud_msg = jmsg["message"].get<std::string>(); - } - } - catch (std::exception& ex) - { - qDebug() << "Can't parse incomming message: " << ex.what(); - } - - QString str = QString::asprintf("Cloud info: %s", cloud_msg.c_str()); - ui->stateLabel->setText(str); -} - -void MainWindow::startMsgButtonClick() -{ - qDebug() << "MainWindow::startMsgButtonClick()"; - - if (started) - ui->startMsgButton->setText("Start"); - else - ui->startMsgButton->setText("Stop"); - started = !started; - - if (worker) - { - worker->stop(); - worker.reset(); - qDebug() << "Worker destroyed"; - } - - if (started) - { - worker.reset(new TelemetryWorker(g_cloudproxyclient, this)); - connect(worker.get(), SIGNAL(timeout()), worker.get(), SLOT(send())); - worker->start(1001); - } -} - - -MainWindow::~MainWindow() -{ - delete ui; -} diff --git a/app_client_examples/telemetry-cloud-app/app/ui_mainwindow.h b/app_client_examples/telemetry-cloud-app/app/ui_mainwindow.h deleted file mode 100755 index 5dbcbd5..0000000 --- a/app_client_examples/telemetry-cloud-app/app/ui_mainwindow.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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 UI_MAINWINDOW_H -#define UI_MAINWINDOW_H - -#include <QtCore/QVariant> -#include <QtWidgets/QApplication> -#include <QtWidgets/QLabel> -#include <QtWidgets/QMainWindow> -#include <QtWidgets/QPushButton> -#include <QtWidgets/QLineEdit> -#include <QtWidgets/QWidget> -#include <QtWidgets/QFrame> -#include <QtWidgets/QGroupBox> -#include <QtWidgets/QRadioButton> -#include <QtWidgets/QComboBox> - -QT_BEGIN_NAMESPACE - -class Ui_MainWindow -{ -public: - QWidget *centralWidget{nullptr}; - QPushButton *startMsgButton{nullptr}; - QLabel *stateLabel{nullptr}; - - void setupUi(double scale_factor, QMainWindow *MainWindow) - { - if (MainWindow->objectName().isEmpty()) - MainWindow->setObjectName(QString::fromUtf8("MainWindow")); - MainWindow->resize(1080*scale_factor, 1400*scale_factor); - centralWidget = new QWidget(MainWindow); - centralWidget->setObjectName(QString::fromUtf8("centralWidget")); - - //QString style("color: white; font-size: 24px"); - QString style("color: white"); - - startMsgButton = new QPushButton(centralWidget); - startMsgButton->setObjectName(QString::fromUtf8("startMsgButton")); - startMsgButton->setGeometry(QRect((540-242/2)*scale_factor, 20*scale_factor, 242*scale_factor, 64*scale_factor)); - startMsgButton->setStyleSheet(style); - - stateLabel = new QLabel(centralWidget); - stateLabel->setObjectName(QString::fromUtf8("label")); - stateLabel->setGeometry(QRect(40*scale_factor, 180*scale_factor, 1000*scale_factor, 420*scale_factor)); - stateLabel->setWordWrap(true); - stateLabel->setAlignment(Qt::AlignTop); - stateLabel->setStyleSheet(style); - - MainWindow->setCentralWidget(centralWidget); - - QObject::connect(startMsgButton, SIGNAL(clicked()), MainWindow, SLOT(startMsgButtonClick())); - - retranslateUi(MainWindow); - - QMetaObject::connectSlotsByName(MainWindow); - } // setupUi - - void retranslateUi(QMainWindow *MainWindow) - { - MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", nullptr)); - startMsgButton->setText(QApplication::translate("MainWindow", "Start", nullptr)); - } // retranslateUi -}; - -namespace Ui { - class MainWindow: public Ui_MainWindow {}; -} // namespace Ui - -QT_END_NAMESPACE - -#endif // UI_MAINWINDOW_H diff --git a/app_client_examples/telemetry-cloud-app/autobuild/agl/autobuild b/app_client_examples/telemetry-cloud-app/autobuild/agl/autobuild deleted file mode 100755 index bbbc13e..0000000 --- a/app_client_examples/telemetry-cloud-app/autobuild/agl/autobuild +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/make -f -# Copyright (C) 2015 - 2018 "IoT.bzh" -# Copyright (C) 2020 Konsulko Group -# 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)) -ROOT_DIR := $(abspath $(dir $(THISFILE))/../..) - -# Build directories -# Note that the debug directory is defined in relation to the release -# directory (BUILD_DIR), this needs to be kept in mind if over-riding -# it and building that widget types, the specific widget type variable -# (e.g. BUILD_DIR_DEBUG) may also need to be specified to yield the -# desired output hierarchy. -BUILD_DIR = $(ROOT_DIR)/build -BUILD_DIR_DEBUG = $(abspath $(BUILD_DIR)/../build-debug) - -# Output directory variable for use in pattern rules. -# This is intended for internal use only, hence the explicit override -# definition. -override OUTPUT_DIR = $(BUILD_DIR) - -# Final install directory for widgets -DEST = $(OUTPUT_DIR) - -# Default build type for release builds -BUILD_TYPE = release - -.PHONY: all help update install distclean -.PHONY: clean clean-release clean-debug clean-all -.PHONY: configure configure-release configure-debug -.PHONY: build build-release build-debug build-all -.PHONY: package package-release package-debug package-all - -help: - @echo "List of targets available:" - @echo "" - @echo "- all" - @echo "- help" - @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 $(DEST) 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" - -all: package-all - -# Target specific variable over-rides so static pattern rules can be -# used for the various type-specific targets. - -configure-debug build-debug package-debug clean-debug: OUTPUT_DIR = $(BUILD_DIR_DEBUG) -configure-debug build-debug package-debug: BUILD_TYPE = debug - -clean-release clean-debug: - @if [ -d $(OUTPUT_DIR) ]; then \ - $(MAKE) -C $(OUTPUT_DIR) $(CLEAN_ARGS) clean; \ - else \ - echo Nothing to clean; \ - fi - -clean: clean-release - -clean-all: clean-release clean-debug - -distclean: clean-all - -configure-release configure-debug: - @mkdir -p $(OUTPUT_DIR) - @if [ ! -f $(OUTPUT_DIR)/Makefile ]; then \ - (cd $(OUTPUT_DIR) && qmake CONFIG+=$(BUILD_TYPE) $(CONFIGURE_ARGS) $(ROOT_DIR)); \ - fi - -configure: configure-release - -build-release build-debug: build-%: configure-% - @$(MAKE) -C $(OUTPUT_DIR) $(BUILD_ARGS) all - -build: build-release - -build-all: build-release build-debug - -package-release package-debug: package-%: build-% - @cp $(OUTPUT_DIR)/package/*.wgt $(OUTPUT_DIR)/ - @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \ - mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \ - fi - - -package: package-release - -package-all: package-release package-debug - -install: build - @$(MAKE) -C $(BUILD_DIR) $(INSTALL_ARGS) install diff --git a/app_client_examples/telemetry-cloud-app/autobuild/linux/autobuild b/app_client_examples/telemetry-cloud-app/autobuild/linux/autobuild deleted file mode 100755 index bbbc13e..0000000 --- a/app_client_examples/telemetry-cloud-app/autobuild/linux/autobuild +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/make -f -# Copyright (C) 2015 - 2018 "IoT.bzh" -# Copyright (C) 2020 Konsulko Group -# 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)) -ROOT_DIR := $(abspath $(dir $(THISFILE))/../..) - -# Build directories -# Note that the debug directory is defined in relation to the release -# directory (BUILD_DIR), this needs to be kept in mind if over-riding -# it and building that widget types, the specific widget type variable -# (e.g. BUILD_DIR_DEBUG) may also need to be specified to yield the -# desired output hierarchy. -BUILD_DIR = $(ROOT_DIR)/build -BUILD_DIR_DEBUG = $(abspath $(BUILD_DIR)/../build-debug) - -# Output directory variable for use in pattern rules. -# This is intended for internal use only, hence the explicit override -# definition. -override OUTPUT_DIR = $(BUILD_DIR) - -# Final install directory for widgets -DEST = $(OUTPUT_DIR) - -# Default build type for release builds -BUILD_TYPE = release - -.PHONY: all help update install distclean -.PHONY: clean clean-release clean-debug clean-all -.PHONY: configure configure-release configure-debug -.PHONY: build build-release build-debug build-all -.PHONY: package package-release package-debug package-all - -help: - @echo "List of targets available:" - @echo "" - @echo "- all" - @echo "- help" - @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 $(DEST) 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" - -all: package-all - -# Target specific variable over-rides so static pattern rules can be -# used for the various type-specific targets. - -configure-debug build-debug package-debug clean-debug: OUTPUT_DIR = $(BUILD_DIR_DEBUG) -configure-debug build-debug package-debug: BUILD_TYPE = debug - -clean-release clean-debug: - @if [ -d $(OUTPUT_DIR) ]; then \ - $(MAKE) -C $(OUTPUT_DIR) $(CLEAN_ARGS) clean; \ - else \ - echo Nothing to clean; \ - fi - -clean: clean-release - -clean-all: clean-release clean-debug - -distclean: clean-all - -configure-release configure-debug: - @mkdir -p $(OUTPUT_DIR) - @if [ ! -f $(OUTPUT_DIR)/Makefile ]; then \ - (cd $(OUTPUT_DIR) && qmake CONFIG+=$(BUILD_TYPE) $(CONFIGURE_ARGS) $(ROOT_DIR)); \ - fi - -configure: configure-release - -build-release build-debug: build-%: configure-% - @$(MAKE) -C $(OUTPUT_DIR) $(BUILD_ARGS) all - -build: build-release - -build-all: build-release build-debug - -package-release package-debug: package-%: build-% - @cp $(OUTPUT_DIR)/package/*.wgt $(OUTPUT_DIR)/ - @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \ - mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \ - fi - - -package: package-release - -package-all: package-release package-debug - -install: build - @$(MAKE) -C $(BUILD_DIR) $(INSTALL_ARGS) install diff --git a/app_client_examples/telemetry-cloud-app/package/config.xml b/app_client_examples/telemetry-cloud-app/package/config.xml deleted file mode 100755 index a49b1c3..0000000 --- a/app_client_examples/telemetry-cloud-app/package/config.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<widget xmlns="http://www.w3.org/ns/widgets" id="telemetrycloudapp" version="0.1"> - <name>CloudTelemetry</name> - <icon src="icon.svg"/> - <content src="bin/telemetrycloudapp" type="application/vnd.agl.native"/> - <description>Test Cloud App</description> - <author>Mera</author> - <license>APL 2.0</license> - <feature name="urn:AGL:widget:required-api"> - <param name="windowmanager" value="ws"/> - <param name="homescreen" value="ws"/> - <param name="cloudproxy" value="ws"/> - </feature> - <feature name="urn:AGL:widget:required-permission"> - <param name="urn:AGL:permission::public:no-htdocs" value="required"/> - <param name="urn:AGL:permission::public:display" value="required" /> - </feature> -</widget> diff --git a/app_client_examples/telemetry-cloud-app/package/icon.svg b/app_client_examples/telemetry-cloud-app/package/icon.svg deleted file mode 100755 index b9b8e67..0000000 --- a/app_client_examples/telemetry-cloud-app/package/icon.svg +++ /dev/null @@ -1,266 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> - -<svg - xmlns:i="&#38;ns_ai;" - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - x="0px" - y="0px" - viewBox="0 0 320 320" - style="enable-background:new 0 0 320 320;" - xml:space="preserve" - id="svg2" - inkscape:version="0.91 r13725" - sodipodi:docname="icon.svg"><metadata - id="metadata1292"><rdf:RDF><cc:Work - rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs - id="defs1290" /><sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="2560" - inkscape:window-height="1464" - id="namedview1288" - showgrid="false" - inkscape:zoom="4.17193" - inkscape:cx="183.76952" - inkscape:cy="191.17869" - inkscape:window-x="0" - inkscape:window-y="0" - inkscape:window-maximized="1" - inkscape:current-layer="svg2" /><style - type="text/css" - id="style4"> - .st0{display:none;} - .st1{display:inline;} - .st2{opacity:0.4;fill:url(#SVGID_1_);} - .st3{fill:url(#SVGID_2_);} - .st4{fill:#FFFFFF;} - .st5{font-family:'Roboto-Regular';} - .st6{font-size:25px;} - .st7{letter-spacing:6;} - .st8{fill:url(#SVGID_3_);} - .st9{fill:url(#SVGID_4_);} - .st10{fill:url(#SVGID_5_);} - .st11{fill:url(#SVGID_6_);} - .st12{fill:url(#SVGID_7_);} - .st13{fill:url(#SVGID_8_);} - .st14{fill:url(#SVGID_9_);} - .st15{fill:url(#SVGID_10_);} - .st16{fill:url(#SVGID_11_);} - .st17{fill:url(#SVGID_12_);} - .st18{fill:url(#SVGID_13_);} - .st19{fill:url(#SVGID_14_);} - .st20{fill:url(#SVGID_15_);} - .st21{fill:url(#SVGID_16_);} - .st22{fill:url(#SVGID_17_);} - .st23{fill:url(#SVGID_18_);} - .st24{opacity:0.29;} - .st25{fill:url(#SVGID_19_);} - .st26{fill:url(#SVGID_20_);} - .st27{fill:url(#SVGID_21_);} - .st28{fill:url(#SVGID_22_);} - .st29{fill:url(#SVGID_23_);} - .st30{fill:url(#SVGID_24_);} - .st31{fill:url(#SVGID_25_);} - .st32{fill:url(#SVGID_26_);} - .st33{fill:url(#SVGID_27_);} - .st34{fill:url(#SVGID_28_);} - .st35{fill:url(#SVGID_29_);} - .st36{fill:url(#SVGID_30_);} - .st37{fill:url(#SVGID_31_);} - .st38{fill:url(#SVGID_32_);} - .st39{fill:url(#SVGID_33_);} - .st40{fill:url(#SVGID_34_);} - .st41{fill:url(#SVGID_35_);} - .st42{fill:url(#SVGID_36_);} - .st43{opacity:0.4;fill:url(#SVGID_37_);} - .st44{fill:url(#SVGID_38_);} - .st45{fill:url(#SVGID_39_);} - .st46{fill:url(#SVGID_40_);} - .st47{fill:url(#SVGID_41_);} - .st48{fill:url(#SVGID_42_);} - .st49{fill:url(#SVGID_43_);} - .st50{fill:url(#SVGID_44_);} - .st51{display:inline;opacity:0.29;} - .st52{display:inline;fill:url(#SVGID_45_);} - .st53{display:inline;fill:url(#SVGID_46_);} - .st54{display:inline;fill:#FFFFFF;} - .st55{display:inline;fill:url(#SVGID_47_);} - .st56{display:inline;fill:url(#SVGID_48_);} - .st57{display:inline;fill:url(#SVGID_49_);} - .st58{display:inline;fill:url(#SVGID_50_);} - .st59{display:inline;fill:url(#SVGID_51_);} - .st60{display:inline;fill:url(#SVGID_52_);} - .st61{opacity:0.4;fill:url(#SVGID_53_);} - .st62{fill:url(#SVGID_54_);} - .st63{fill:url(#SVGID_55_);} - .st64{fill:url(#SVGID_56_);} - .st65{fill:url(#SVGID_57_);} - .st66{fill:url(#SVGID_58_);} - .st67{opacity:0.4;fill:url(#SVGID_59_);} - .st68{fill:url(#SVGID_60_);} - .st69{fill:url(#SVGID_61_);} - .st70{fill:url(#SVGID_62_);} - .st71{fill:url(#SVGID_63_);} - .st72{fill:url(#SVGID_64_);} - .st73{fill:url(#SVGID_65_);} - .st74{fill:url(#SVGID_66_);} - .st75{fill:url(#SVGID_67_);} - .st76{fill:url(#SVGID_68_);} - .st77{fill:url(#SVGID_69_);} - .st78{fill:url(#SVGID_70_);} - .st79{fill:url(#SVGID_71_);} - .st80{fill:url(#SVGID_72_);} - .st81{fill:url(#SVGID_73_);} - .st82{fill:url(#SVGID_74_);} - .st83{fill:url(#SVGID_75_);} - .st84{fill:url(#SVGID_76_);} - .st85{fill:url(#SVGID_77_);} - .st86{fill:url(#SVGID_78_);} - .st87{fill:url(#SVGID_79_);} - .st88{fill:url(#SVGID_80_);} - .st89{fill:url(#SVGID_81_);} - .st90{fill:url(#SVGID_82_);} - .st91{fill:url(#SVGID_83_);} - .st92{fill:url(#SVGID_84_);} - .st93{fill:url(#SVGID_85_);} - .st94{fill:url(#SVGID_86_);} - .st95{opacity:0.4;fill:url(#SVGID_87_);} - .st96{fill:url(#SVGID_88_);} - .st97{fill:url(#SVGID_89_);} - .st98{fill:url(#SVGID_90_);} - .st99{fill:url(#SVGID_91_);} - .st100{fill:url(#SVGID_92_);} - .st101{fill:url(#SVGID_93_);} - .st102{fill:url(#SVGID_94_);} - .st103{opacity:0.4;fill:url(#SVGID_95_);} - .st104{fill:url(#SVGID_96_);} - .st105{fill:url(#SVGID_97_);} - .st106{fill:url(#SVGID_98_);} - .st107{fill:url(#SVGID_99_);} - .st108{fill:url(#SVGID_100_);} - .st109{fill:url(#SVGID_101_);} - .st110{display:inline;fill:url(#SVGID_102_);} - .st111{display:inline;fill:url(#SVGID_103_);} - .st112{fill:url(#SVGID_104_);} - .st113{fill:url(#SVGID_105_);} - .st114{fill:url(#SVGID_106_);} - .st115{fill:url(#SVGID_107_);} - .st116{fill:url(#SVGID_108_);} - .st117{opacity:0.4;fill:url(#SVGID_109_);} - .st118{fill:url(#SVGID_110_);} - .st119{fill:url(#SVGID_111_);} - .st120{fill:url(#SVGID_112_);} - .st121{fill:url(#SVGID_113_);} - .st122{fill:url(#SVGID_114_);} - .st123{opacity:0.4;fill:url(#SVGID_115_);} - .st124{fill:url(#SVGID_116_);} - .st125{fill:url(#SVGID_117_);} - .st126{fill:url(#SVGID_118_);} - .st127{display:inline;fill:url(#SVGID_119_);} - .st128{display:inline;fill:url(#SVGID_120_);} - .st129{fill:url(#SVGID_121_);} - .st130{fill:url(#SVGID_122_);} -</style><switch - id="switch6"><g - i:extraneous="self" - id="g8"><g - id="Multimedia_Inactive_copy"><circle - class="st24" - cx="159.7" - cy="133.4" - r="101.9" - id="circle884" /><linearGradient - id="SVGID_91_" - gradientUnits="userSpaceOnUse" - x1="115.9317" - y1="254.1836" - x2="256.3852" - y2="-133.5267"><stop - offset="0" - style="stop-color:#8BC53F" - id="stop887" /><stop - offset="2.015080e-02" - style="stop-color:#7CCB56;stop-opacity:0.9678" - id="stop889" /><stop - offset="6.089833e-02" - style="stop-color:#62D67D;stop-opacity:0.9028" - id="stop891" /><stop - offset="0.1057" - style="stop-color:#4BDFA0;stop-opacity:0.8312" - id="stop893" /><stop - offset="0.1543" - style="stop-color:#38E7BE;stop-opacity:0.7537" - id="stop895" /><stop - offset="0.2077" - style="stop-color:#28EED6;stop-opacity:0.6684" - id="stop897" /><stop - offset="0.2681" - style="stop-color:#1CF3E8;stop-opacity:0.572" - id="stop899" /><stop - offset="0.3394" - style="stop-color:#13F6F5;stop-opacity:0.4581" - id="stop901" /><stop - offset="0.4323" - style="stop-color:#0EF8FD;stop-opacity:0.3098" - id="stop903" /><stop - offset="0.6264" - style="stop-color:#0DF9FF;stop-opacity:0" - id="stop905" /></linearGradient><circle - class="st99" - cx="159.7" - cy="133.4" - r="101.9" - id="circle907" /><linearGradient - id="SVGID_92_" - gradientUnits="userSpaceOnUse" - x1="4.0481" - y1="287.9492" - x2="320.4859" - y2="-15.4029" - gradientTransform="matrix(1 5.464556e-03 -5.464556e-03 1 -2.0192 -3.0212)"><stop - offset="0" - style="stop-color:#59FF7F" - id="stop910" /><stop - offset="1" - style="stop-color:#6BFBFF" - id="stop912" /></linearGradient><path - class="st100" - d="M160,238.8c-0.2,0-0.4,0-0.6,0c-58-0.3-104.9-47.7-104.6-105.7C55.2,75.3,102.3,28.5,160,28.5 c0.2,0,0.4,0,0.6,0c58,0.3,104.9,47.7,104.6,105.7l0,0C264.8,192,217.7,238.8,160,238.8z M160,32.2 c-55.7,0-101.2,45.2-101.5,100.9c-0.3,55.9,45,101.7,100.9,102c0.2,0,0.4,0,0.6,0c55.7,0,101.2-45.2,101.5-100.9 c0.3-55.9-45-101.7-100.9-102C160.4,32.2,160.2,32.2,160,32.2z" - id="path914" /><g - id="g916"><text - transform="matrix(1 0 0 1 53.5841 284.7119)" - class="st4 st5 st6 st7" - id="text918">VIDEOPLAYER -</text> -<linearGradient - id="SVGID_94_" - gradientUnits="userSpaceOnUse" - x1="145.3286" - y1="203.8951" - x2="191.6285" - y2="70.3637"><stop - offset="0" - style="stop-color:#59FF7F" - id="stop928" /><stop - offset="1" - style="stop-color:#6BFBFF" - id="stop930" /></linearGradient><polygon - class="st102" - points="125,75 125,200 225,137.5 " - id="polygon932" - style="fill:url(#SVGID_94_)" /></g></g></g></switch></svg>
\ No newline at end of file diff --git a/app_client_examples/telemetry-cloud-app/package/package.pro b/app_client_examples/telemetry-cloud-app/package/package.pro deleted file mode 100755 index 7a138a3..0000000 --- a/app_client_examples/telemetry-cloud-app/package/package.pro +++ /dev/null @@ -1,28 +0,0 @@ - -DISTFILES = icon.svg config.xml - -copy_icon.target = $$OUT_PWD/root/icon.svg -copy_icon.depends = $$_PRO_FILE_PWD_/icon.svg -copy_icon.commands = $(COPY_FILE) \"$$replace(copy_icon.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_icon.target, /, $$QMAKE_DIR_SEP)\" -QMAKE_EXTRA_TARGETS += copy_icon -PRE_TARGETDEPS += $$copy_icon.target - -copy_config.target = $$OUT_PWD/root/config.xml -copy_config.depends = $$_PRO_FILE_PWD_/config.xml -copy_config.commands = $(COPY_FILE) \"$$replace(copy_config.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_config.target, /, $$QMAKE_DIR_SEP)\" -QMAKE_EXTRA_TARGETS += copy_config -PRE_TARGETDEPS += $$copy_config.target - -WGT_TYPE = -CONFIG(debug, debug|release) { - WGT_TYPE = -debug -} - -wgt.target = package -wgt.commands = wgtpkg-pack -f -o telemetrycloudapp$${WGT_TYPE}.wgt root - -QMAKE_EXTRA_TARGETS += wgt - -deploy.files = telemetrycloudapp.wgt -deploy.path = /usr/AGL/apps/autoinstall -INSTALLS += deploy diff --git a/app_client_examples/telemetry-cloud-app/telemetrycloudapp.pro b/app_client_examples/telemetry-cloud-app/telemetrycloudapp.pro deleted file mode 100755 index 579a952..0000000 --- a/app_client_examples/telemetry-cloud-app/telemetrycloudapp.pro +++ /dev/null @@ -1,3 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS = app package -package.depends += app diff --git a/app_client_examples/test-cloud-app/app/app.pri b/app_client_examples/test-cloud-app/app/app.pri deleted file mode 100755 index 6d710e4..0000000 --- a/app_client_examples/test-cloud-app/app/app.pri +++ /dev/null @@ -1,3 +0,0 @@ -TEMPLATE = app -QMAKE_LFLAGS += "-Wl,--hash-style=gnu -Wl,--as-needed" -DESTDIR = $${OUT_PWD}/../package/root/bin diff --git a/app_client_examples/test-cloud-app/app/app.pro b/app_client_examples/test-cloud-app/app/app.pro deleted file mode 100755 index 4e8ae9a..0000000 --- a/app_client_examples/test-cloud-app/app/app.pro +++ /dev/null @@ -1,34 +0,0 @@ -TARGET = testcloudapp -QT += core gui gui-private - -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets - -DEFINES += QT_DEPRECATED_WARNINGS -DEFINES += APP_INSTANCE_NUM='\\"1\\"' - -SOURCES = main.cpp \ - mainwindow.cpp \ - cloudproxy-client/cloudproxyclient.cpp - -HEADERS += mainwindow.h \ - ui_mainwindow.h \ - cloudproxy-client/cloudproxyclient.h \ - cloudproxy-client/hmi-debug.h \ - -CONFIG += link_pkgconfig -PKGCONFIG += libhomescreen qlibwindowmanager qtappfw-core - -CONFIG(release, debug|release) { - QMAKE_POST_LINK = $(STRIP) --strip-unneeded $(TARGET) -} - -LIBS += -ljson-c -lafbwsc -lsystemd - -RESOURCES += \ - images/images.qrc - -include(app.pri) - -DISTFILES += \ - images/AGL_HMI_Blue_Background_NoCar-01.png - diff --git a/app_client_examples/test-cloud-app/app/cloudproxy-client/cloudproxyclient.cpp b/app_client_examples/test-cloud-app/app/cloudproxy-client/cloudproxyclient.cpp deleted file mode 100755 index cfbf943..0000000 --- a/app_client_examples/test-cloud-app/app/cloudproxy-client/cloudproxyclient.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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 <thread> -#include <algorithm> - -#include "cloudproxyclient.h" -#include "hmi-debug.h" - - -static const char API_name[] = "cloudproxy"; - -const std::vector<std::string> CloudProxyClient::m_api_list { - std::string("ping"), - std::string("createConnection"), - std::string("sendMessage"), - std::string("destroyConnection") - }; - -const std::vector<std::string> CloudProxyClient::m_event_list { - std::string("sendMessageConfirmation"), - std::string("receivedMessage") - }; - -static void event_loop_run(sd_event* loop) -{ - sd_event_loop(loop); - sd_event_unref(loop); -} - -static void on_hangup(void *closure, afb_wsj1 *wsj) -{ - HMI_DEBUG("cloudproxyclient", "called"); - (void)closure; - (void)wsj; -} - -static void on_call(void *closure, const char *api, const char *verb, afb_wsj1_msg *msg) -{ - HMI_ERROR("cloudproxyclient", "this method should not be called"); - (void)closure; - (void)api; - (void)verb; - (void)msg; -} - -static void on_event(void* closure, const char* event, afb_wsj1_msg *msg) -{ - HMI_DEBUG("cloudproxyclient", "event [%s]", (event ? event: "")); - (void)closure; - - static_cast<CloudProxyClient*>(closure)->on_event(nullptr, event, msg); -} - -static void on_reply(void *closure, afb_wsj1_msg *msg) -{ - HMI_DEBUG("cloudproxyclient", "called"); - (void)closure; - (void)msg; -} - - -CloudProxyClient::CloudProxyClient() -{ -} - -CloudProxyClient::~CloudProxyClient() -{ - if(m_websock) - { - afb_wsj1_unref(m_websock); - } - if(m_loop) - { - sd_event_exit(m_loop, 0); - } -} - -int CloudProxyClient::init(const int port, const std::string& token) -{ - int ret = 0; - if(port <= 0 && token.size() == 0) - { - HMI_ERROR("cloudproxyclient","port and token should be > 0, Initial port and token uses."); - return -1; - } - - { - m_loop = nullptr; - int ret = sd_event_new(&m_loop); - if(ret < 0) - { - HMI_ERROR("cloudproxyclient","Failed to create event loop"); - return -1; - } - - { - // enforce context to avoid initialization/goto error - std::thread th(event_loop_run, m_loop); - th.detach(); - } - - /* Initialize interface from websocket */ - m_itf.on_hangup = ::on_hangup; - m_itf.on_call = ::on_call; - m_itf.on_event = ::on_event; - - m_uri += "ws://localhost:" + std::to_string(port) + "/api?token=" + token; - - m_websock = afb_ws_client_connect_wsj1(m_loop, m_uri.c_str(), &m_itf, this); - if(!m_websock) - { - HMI_ERROR("cloudproxyclient","Failed to create websocket connection"); - return -1; - } - } - - HMI_DEBUG("cloudproxyclient", "Initialized"); - - return ret; -} - - -int CloudProxyClient::call(const std::string& verb, json_object* arg) -{ - int ret; - if(!m_websock) - { - return -1; - } - - if (verb.empty() || m_api_list.end() == std::find(m_api_list.begin(), m_api_list.end(), verb)) - { - HMI_ERROR("cloudproxyclient","verb [%s] doesn't exit", verb.c_str()); - return -1; - } - - ret = afb_wsj1_call_j(m_websock, API_name, verb.c_str(), arg, ::on_reply, this); - if (ret < 0) - { - HMI_ERROR("cloudproxyclient", "Failed to call verb:%s", verb.c_str()); - } - - return ret; -} - - -int CloudProxyClient::sendMessage(const std::string& data) -{ - if(!m_websock) - return -1; - - json_object* j_obj = json_object_new_object(); - json_object_object_add(j_obj, "data", json_object_new_string(data.c_str())); - - return this->call("sendMessage", j_obj); -} - -void CloudProxyClient::set_event_handler(enum EventType et, handler_func f) -{ - if (et > Event_Min && et < Event_Max) - { - switch (et) - { - case Event_SendMessageConfirmation: - this->subscribe(CloudProxyClient::m_event_list[0]); - break; - case Event_ReceivedMessage: - this->subscribe(CloudProxyClient::m_event_list[1]); - break; - default: - break; - } - - this->handlers[et] = std::move(f); - } -} - -int CloudProxyClient::subscribe(const std::string& event_name) -{ - if(!m_websock) - return -1; - - json_object* j_obj = json_object_new_object(); - json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str())); - - int ret = afb_wsj1_call_j(m_websock, API_name, "subscribe", j_obj, ::on_reply, this); - if (ret < 0) - HMI_ERROR("cloudproxyclient", "subscribe filed for '%s'", event_name.c_str()); - - return ret; -} - -int CloudProxyClient::unsubscribe(const std::string& event_name) -{ - if(!m_websock) - return -1; - - json_object* j_obj = json_object_new_object(); - json_object_object_add(j_obj, "event", json_object_new_string(event_name.c_str())); - - int ret = afb_wsj1_call_j(m_websock, API_name, "unsubscribe", j_obj, ::on_reply, this); - if (ret < 0) - HMI_ERROR("cloudproxyclient", "unsubscribe filed for '%s'", event_name.c_str()); - - return ret; -} - -void CloudProxyClient::on_event(void *closure, const char *event, afb_wsj1_msg *msg) -{ - HMI_DEBUG("cloudproxyclient", "event: (%s) msg: (%s).", event, afb_wsj1_msg_object_s(msg)); - (void) closure; - - if (strstr(event, API_name) == nullptr) - return; - - - json_object* ev_contents = afb_wsj1_msg_object_j(msg); - json_object *json_data; - if(!json_object_object_get_ex(ev_contents, "data", &json_data)) - { - HMI_ERROR("cloudproxyclient", "got ev_contents error."); - return; - } - - const char* event_type = nullptr; - json_object *json_event_type; - if(!json_object_object_get_ex(json_data, "type", &json_event_type) || - (event_type = json_object_get_string(json_event_type)) == nullptr) - { - HMI_ERROR("cloudproxyclient", "event_type is null."); - return; - } - - const std::string et{event_type}; - if (CloudProxyClient::m_event_list[0] == et) - { - auto i = this->handlers.find(Event_SendMessageConfirmation); - if (i != this->handlers.end()) - i->second(json_data); - } - else if (CloudProxyClient::m_event_list[1] == et) - { - auto i = this->handlers.find(Event_ReceivedMessage); - if (i != this->handlers.end()) - i->second(json_data); - } -} diff --git a/app_client_examples/test-cloud-app/app/cloudproxy-client/cloudproxyclient.h b/app_client_examples/test-cloud-app/app/cloudproxy-client/cloudproxyclient.h deleted file mode 100755 index b333a14..0000000 --- a/app_client_examples/test-cloud-app/app/cloudproxy-client/cloudproxyclient.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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. - */ - -#pragma once - -#include <vector> -#include <map> -#include <string> -#include <functional> -#include <json-c/json.h> -#include <systemd/sd-event.h> - -extern "C" -{ -#include <afb/afb-wsj1.h> -#include <afb/afb-ws-client.h> -} - -class CloudProxyClient -{ -public: - using handler_func = std::function<void(json_object*)>; - - enum EventType { - Event_Min, - Event_SendMessageConfirmation = 1, - Event_ReceivedMessage, - Event_Max - }; - - - CloudProxyClient(); - ~CloudProxyClient(); - - CloudProxyClient(const CloudProxyClient &) = delete; - CloudProxyClient &operator=(const CloudProxyClient &) = delete; - - int init(const int port, const std::string& token); - - int sendMessage(const std::string& data); - - void set_event_handler(enum EventType et, handler_func f); - - void on_event(void *closure, const char *event, struct afb_wsj1_msg *msg); - - int subscribe(const std::string& event_name); - int unsubscribe(const std::string& event_name); - -private: - int call(const std::string& verb, struct json_object* arg); - -private: - static const std::vector<std::string> m_api_list; - static const std::vector<std::string> m_event_list; - - std::map<EventType, handler_func> handlers; - struct afb_wsj1_itf m_itf; - struct afb_wsj1* m_websock{nullptr}; - sd_event* m_loop{nullptr}; - std::string m_uri; -}; diff --git a/app_client_examples/test-cloud-app/app/cloudproxy-client/hmi-debug.h b/app_client_examples/test-cloud-app/app/cloudproxy-client/hmi-debug.h deleted file mode 100755 index 674f5df..0000000 --- a/app_client_examples/test-cloud-app/app/cloudproxy-client/hmi-debug.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2018 TOYOTA MOTOR CORPORATION - * - * 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 __HMI_DEBUG_H__ -#define __HMI_DEBUG_H__ - -#include <time.h> -#include <stdio.h> -#include <stdarg.h> -#include <string.h> -#include <stdlib.h> - -enum LOG_LEVEL{ - LOG_LEVEL_NONE = 0, - LOG_LEVEL_ERROR, - LOG_LEVEL_WARNING, - LOG_LEVEL_NOTICE, - LOG_LEVEL_INFO, - LOG_LEVEL_DEBUG, - LOG_LEVEL_MAX = LOG_LEVEL_DEBUG -}; - -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) - -#define HMI_ERROR(prefix, args,...) _HMI_LOG(LOG_LEVEL_ERROR, __FILENAME__, __FUNCTION__, __LINE__, prefix, args, ##__VA_ARGS__) -#define HMI_WARNING(prefix, args,...) _HMI_LOG(LOG_LEVEL_WARNING, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) -#define HMI_NOTICE(prefix, args,...) _HMI_LOG(LOG_LEVEL_NOTICE, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) -#define HMI_INFO(prefix, args,...) _HMI_LOG(LOG_LEVEL_INFO, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) -#define HMI_DEBUG(prefix, args,...) _HMI_LOG(LOG_LEVEL_DEBUG, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) - -static char ERROR_FLAG[6][20] = {"NONE", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"}; - -static void _HMI_LOG(enum LOG_LEVEL level, const char* file, const char* func, const int line, const char* prefix, const char* log, ...) -{ - const int log_level = (getenv("USE_HMI_DEBUG") == NULL)?LOG_LEVEL_ERROR:atoi(getenv("USE_HMI_DEBUG")); - if(log_level < level) - { - return; - } - - char *message; - struct timespec tp; - unsigned int time; - - clock_gettime(CLOCK_REALTIME, &tp); - time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - - va_list args; - va_start(args, log); - if (log == NULL || vasprintf(&message, log, args) < 0) - message = NULL; - fprintf(stderr, "[%10.3f] [%s %s] [%s, %s(), Line:%d] >>> %s \n", time / 1000.0, prefix, ERROR_FLAG[level], file, func, line, message); - va_end(args); - free(message); -} - -#endif //__HMI_DEBUG_H__ diff --git a/app_client_examples/test-cloud-app/app/images/AGL_HMI_Blue_Background_NoCar-01.png b/app_client_examples/test-cloud-app/app/images/AGL_HMI_Blue_Background_NoCar-01.png Binary files differdeleted file mode 100755 index a76b140..0000000 --- a/app_client_examples/test-cloud-app/app/images/AGL_HMI_Blue_Background_NoCar-01.png +++ /dev/null diff --git a/app_client_examples/test-cloud-app/app/images/images.qrc b/app_client_examples/test-cloud-app/app/images/images.qrc deleted file mode 100755 index 78d9b77..0000000 --- a/app_client_examples/test-cloud-app/app/images/images.qrc +++ /dev/null @@ -1,5 +0,0 @@ -<RCC> - <qresource prefix="/images"> - <file>AGL_HMI_Blue_Background_NoCar-01.png</file> - </qresource> -</RCC> diff --git a/app_client_examples/test-cloud-app/app/main.cpp b/app_client_examples/test-cloud-app/app/main.cpp deleted file mode 100755 index 45a877b..0000000 --- a/app_client_examples/test-cloud-app/app/main.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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 <QtCore/QUrlQuery> -#include <QtGui/QGuiApplication> -#include <libhomescreen.hpp> -#include <qlibwindowmanager.h> -#include <QtCore/QCommandLineParser> -#include <QScopedPointer> - -#include "mainwindow.h" -#include <QApplication> -#include <QDebug> - -#include "cloudproxy-client/cloudproxyclient.h" - - -CloudProxyClient* g_cloudproxyclient{nullptr}; - - -int main(int argc, char *argv[]) -{ - QString graphic_role = QString("cloudapp") + QString(APP_INSTANCE_NUM); - - QApplication app(argc, argv); - QScopedPointer<MainWindow> window; - - QCommandLineParser parser; - parser.addPositionalArgument("port", app.translate("main", "port for binding")); - parser.addPositionalArgument("secret", app.translate("main", "secret for binding")); - parser.addHelpOption(); - parser.addVersionOption(); - parser.process(app); - QStringList positionalArguments = parser.positionalArguments(); - - if (positionalArguments.length() == 2) - { - int port = positionalArguments.takeFirst().toInt(); - QString secret = positionalArguments.takeFirst(); - QUrl bindingAddress; - bindingAddress.setScheme(QStringLiteral("ws")); - bindingAddress.setHost(QStringLiteral("localhost")); - bindingAddress.setPort(port); - bindingAddress.setPath(QStringLiteral("/api")); - QUrlQuery query; - query.addQueryItem(QStringLiteral("token"), secret); - bindingAddress.setQuery(query); - - std::string token = secret.toStdString(); - LibHomeScreen* hs = new LibHomeScreen(); - QLibWindowmanager* qwm = new QLibWindowmanager(); - - // WindowManager - if(qwm->init(port,secret) != 0){ - exit(EXIT_FAILURE); - } - AGLScreenInfo screenInfo(qwm->get_scale_factor()); - window.reset(new MainWindow(screenInfo.scale_factor())); - - // Request a surface as described in layers.json windowmanager’s file - if (qwm->requestSurface(graphic_role) != 0) { - exit(EXIT_FAILURE); - } - // Create an event callback against an event type. Here a lambda is called when SyncDraw event occurs - qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, &graphic_role](json_object *object) { - (void)object; - qDebug() << "Surface got syncDraw!"; - qwm->endDraw(graphic_role); - }); - - // HomeScreen - hs->init(port, token.c_str()); - // Set the event handler for Event_ShowWindow which will activate the surface for windowmanager - hs->set_event_handler(LibHomeScreen::Event_ShowWindow, [qwm, &graphic_role](json_object *object){ - (void)object; - qDebug("Surface %s got showWindow\n", graphic_role.toStdString().c_str()); - qwm->activateWindow(graphic_role); - }); - - g_cloudproxyclient = new CloudProxyClient(); - g_cloudproxyclient->init(port, token.c_str()); - - static int i_confirm_good{0}, i_confirm_bad{0}, i_recv{0}; - g_cloudproxyclient->set_event_handler(CloudProxyClient::Event_SendMessageConfirmation, [&window](json_object* object){ - qDebug("CloudProxyClient::Event_SendMessageConfirmation: object ptr %p", object); - - const char* str = object ? json_object_to_json_string_ext(object, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) : "<obj_null>"; - if (!str) - str = "<empty>"; - qDebug("Event_SendMessageConfirmation: %s", str); - - json_object *j_result; - if(!json_object_object_get_ex(object, "result", &j_result)) - { - qDebug("Can't read confirmation result"); - return; - } - - int result = (int)json_object_get_boolean(j_result); - - if (result) - ++i_confirm_good; - else - ++i_confirm_bad; - - window->updateStat(i_confirm_good, i_confirm_bad, i_recv, QVariant()); - qDebug("Application received confirmation result [good: %d, bad: %d]: %d", i_confirm_good, i_confirm_bad, result); - }); - g_cloudproxyclient->set_event_handler(CloudProxyClient::Event_ReceivedMessage, [&window](json_object* object){ - qDebug("CloudProxyClient::Event_ReceivedMessage: object ptr %p", object); - - const char* str = object ? json_object_to_json_string_ext(object, JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY) : "<obj_null>"; - if (!str) - str = "<empty>"; - qDebug("Event_SendMessageConfirmation: %s", str); - - json_object *event_data; - const char* data_str{nullptr}; - if(!json_object_object_get_ex(object, "data", &event_data) || - (data_str = json_object_get_string(event_data)) == nullptr) - { - qDebug("Can't read event data"); - return; - } - - ++i_recv; - window->updateStat(i_confirm_good, i_confirm_bad, i_recv, QString(data_str)); - qDebug("Application received data [count: %d, good: %d, bad %d]: %s", i_recv, i_confirm_good, i_confirm_bad, data_str); - }); - - - window->show(); - qwm->slotActivateWindow(); - } - - return app.exec(); -} diff --git a/app_client_examples/test-cloud-app/app/mainwindow.cpp b/app_client_examples/test-cloud-app/app/mainwindow.cpp deleted file mode 100755 index fda05e7..0000000 --- a/app_client_examples/test-cloud-app/app/mainwindow.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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 "mainwindow.h" -#include "ui_mainwindow.h" -#include <QDebug> - -#include <unistd.h> -#include <sys/stat.h> - - -#include "cloudproxy-client/cloudproxyclient.h" -extern CloudProxyClient* g_cloudproxyclient; - - -MainWindow::MainWindow(double scale_factor, QWidget *parent) : - QMainWindow(parent), - ui(new Ui::MainWindow) -{ - ui->setupUi(scale_factor, this); - - //AGL style - setWindowFlags(Qt::FramelessWindowHint | Qt::WindowSystemMenuHint); - this->setStyleSheet( - "background-image:url(:/images/AGL_HMI_Blue_Background_NoCar-01.png) 0 0 0 0 stretch stretch; background: transparent;" ); -} - -void MainWindow::updateStat(int confirm_good, int confirm_bad, int received, QVariant recvMsg) -{ - confirmGood = confirm_good; - confirmBad = confirm_bad; - receivedCount = received; - - if (recvMsg.isValid()) - lastReceivedMsg = recvMsg.toString(); - - updateStat(); -} - -void MainWindow::updateStat() -{ - QString str = QString::asprintf("Sent: OK=%d, NOK=%d\nConfirmed: OK=%d, NOK=%d\nReceived=%d\n\n", - sendCount, sendErrorCount, confirmGood, confirmBad, receivedCount); - str.append("Last sent message:\n"); - str.append(lastSentMsg); - str.append("\n\nLast received message:\n"); - str.append(lastReceivedMsg); - ui->statLabel->setText(str); - //QApplication::processEvents(); -} - -void MainWindow::sendMsgButtonClick() -{ - qDebug() << "MainWindow::sendMegButtonClick()"; - - std::string msg = std::string{"{\"app_key\": \"app_value_"} + std::to_string(sendCount + sendErrorCount) + "\"}"; - int res = g_cloudproxyclient->sendMessage(msg); - if (res == 0) - ++sendCount; - else - ++sendErrorCount; - - lastSentMsg = msg.c_str(); - qDebug() << "cloud sendMessage result: " << res; -} - - -MainWindow::~MainWindow() -{ - delete ui; -} diff --git a/app_client_examples/test-cloud-app/app/mainwindow.h b/app_client_examples/test-cloud-app/app/mainwindow.h deleted file mode 100755 index ae66729..0000000 --- a/app_client_examples/test-cloud-app/app/mainwindow.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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 MAINWINDOW_H -#define MAINWINDOW_H - -#include <QMainWindow> -#include <QVariant> -#include <memory> - - -namespace Ui { - class MainWindow; -} - -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(double scale_factor, QWidget *parent = 0); - ~MainWindow(); - -public slots: - void updateStat(int confirm_good, int confirm_bad, int received, QVariant recvMsg); - -private slots: - void sendMsgButtonClick(); - void updateStat(); - -private: - Ui::MainWindow *ui; - int sendCount{0}; - int sendErrorCount{0}; - int confirmGood{0}; - int confirmBad{0}; - int receivedCount{0}; - QString lastSentMsg; - QString lastReceivedMsg; -}; - -#endif // MAINWINDOW_H diff --git a/app_client_examples/test-cloud-app/app/ui_mainwindow.h b/app_client_examples/test-cloud-app/app/ui_mainwindow.h deleted file mode 100755 index 3dac77e..0000000 --- a/app_client_examples/test-cloud-app/app/ui_mainwindow.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2020 MERA - * - * 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 UI_MAINWINDOW_H -#define UI_MAINWINDOW_H - -#include <QtCore/QVariant> -#include <QtWidgets/QApplication> -#include <QtWidgets/QLabel> -#include <QtWidgets/QMainWindow> -#include <QtWidgets/QPushButton> -#include <QtWidgets/QLineEdit> -#include <QtWidgets/QWidget> -#include <QtWidgets/QFrame> -#include <QtWidgets/QGroupBox> -#include <QtWidgets/QRadioButton> -#include <QtWidgets/QComboBox> - -QT_BEGIN_NAMESPACE - -class Ui_MainWindow -{ -public: - QWidget *centralWidget{nullptr}; - QPushButton *sendMsgButton{nullptr}; - QLabel *appNameLabel{nullptr}; - QLabel *statLabel{nullptr}; - - void setupUi(double scale_factor, QMainWindow *MainWindow) - { - if (MainWindow->objectName().isEmpty()) - MainWindow->setObjectName(QString::fromUtf8("MainWindow")); - MainWindow->resize(1080*scale_factor, 1400*scale_factor); - centralWidget = new QWidget(MainWindow); - centralWidget->setObjectName(QString::fromUtf8("centralWidget")); - - //QString style("color: white; font-size: 24px"); - QString style("color: white"); - - sendMsgButton = new QPushButton(centralWidget); - sendMsgButton->setObjectName(QString::fromUtf8("sendMsgButton")); - sendMsgButton->setGeometry(QRect((540-242/2)*scale_factor, 100*scale_factor, 242*scale_factor, 64*scale_factor)); - sendMsgButton->setStyleSheet(style); - - appNameLabel = new QLabel(centralWidget); - appNameLabel->setObjectName(QString::fromUtf8("label")); - appNameLabel->setGeometry(QRect(40*scale_factor, 20*scale_factor, 1000*scale_factor, 32*scale_factor)); - appNameLabel->setStyleSheet(style); - - statLabel = new QLabel(centralWidget); - statLabel->setObjectName(QString::fromUtf8("label")); - statLabel->setGeometry(QRect(40*scale_factor, 180*scale_factor, 1000*scale_factor, 420*scale_factor)); - statLabel->setWordWrap(true); - statLabel->setAlignment(Qt::AlignTop); - statLabel->setStyleSheet(style); - - MainWindow->setCentralWidget(centralWidget); - - QObject::connect(sendMsgButton, SIGNAL(clicked()), MainWindow, SLOT(sendMsgButtonClick())); - - retranslateUi(MainWindow); - - QMetaObject::connectSlotsByName(MainWindow); - } // setupUi - - void retranslateUi(QMainWindow *MainWindow) - { - MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", nullptr)); - sendMsgButton->setText(QApplication::translate("MainWindow", "Send message", nullptr)); - QString name = QString("Test application ") + APP_INSTANCE_NUM; - appNameLabel->setText(QApplication::translate("MainWindow", name.toUtf8().constData(), nullptr)); - } // retranslateUi -}; - -namespace Ui { - class MainWindow: public Ui_MainWindow {}; -} // namespace Ui - -QT_END_NAMESPACE - -#endif // UI_MAINWINDOW_H diff --git a/app_client_examples/test-cloud-app/autobuild/agl/autobuild b/app_client_examples/test-cloud-app/autobuild/agl/autobuild deleted file mode 100755 index bbbc13e..0000000 --- a/app_client_examples/test-cloud-app/autobuild/agl/autobuild +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/make -f -# Copyright (C) 2015 - 2018 "IoT.bzh" -# Copyright (C) 2020 Konsulko Group -# 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)) -ROOT_DIR := $(abspath $(dir $(THISFILE))/../..) - -# Build directories -# Note that the debug directory is defined in relation to the release -# directory (BUILD_DIR), this needs to be kept in mind if over-riding -# it and building that widget types, the specific widget type variable -# (e.g. BUILD_DIR_DEBUG) may also need to be specified to yield the -# desired output hierarchy. -BUILD_DIR = $(ROOT_DIR)/build -BUILD_DIR_DEBUG = $(abspath $(BUILD_DIR)/../build-debug) - -# Output directory variable for use in pattern rules. -# This is intended for internal use only, hence the explicit override -# definition. -override OUTPUT_DIR = $(BUILD_DIR) - -# Final install directory for widgets -DEST = $(OUTPUT_DIR) - -# Default build type for release builds -BUILD_TYPE = release - -.PHONY: all help update install distclean -.PHONY: clean clean-release clean-debug clean-all -.PHONY: configure configure-release configure-debug -.PHONY: build build-release build-debug build-all -.PHONY: package package-release package-debug package-all - -help: - @echo "List of targets available:" - @echo "" - @echo "- all" - @echo "- help" - @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 $(DEST) 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" - -all: package-all - -# Target specific variable over-rides so static pattern rules can be -# used for the various type-specific targets. - -configure-debug build-debug package-debug clean-debug: OUTPUT_DIR = $(BUILD_DIR_DEBUG) -configure-debug build-debug package-debug: BUILD_TYPE = debug - -clean-release clean-debug: - @if [ -d $(OUTPUT_DIR) ]; then \ - $(MAKE) -C $(OUTPUT_DIR) $(CLEAN_ARGS) clean; \ - else \ - echo Nothing to clean; \ - fi - -clean: clean-release - -clean-all: clean-release clean-debug - -distclean: clean-all - -configure-release configure-debug: - @mkdir -p $(OUTPUT_DIR) - @if [ ! -f $(OUTPUT_DIR)/Makefile ]; then \ - (cd $(OUTPUT_DIR) && qmake CONFIG+=$(BUILD_TYPE) $(CONFIGURE_ARGS) $(ROOT_DIR)); \ - fi - -configure: configure-release - -build-release build-debug: build-%: configure-% - @$(MAKE) -C $(OUTPUT_DIR) $(BUILD_ARGS) all - -build: build-release - -build-all: build-release build-debug - -package-release package-debug: package-%: build-% - @cp $(OUTPUT_DIR)/package/*.wgt $(OUTPUT_DIR)/ - @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \ - mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \ - fi - - -package: package-release - -package-all: package-release package-debug - -install: build - @$(MAKE) -C $(BUILD_DIR) $(INSTALL_ARGS) install diff --git a/app_client_examples/test-cloud-app/autobuild/linux/autobuild b/app_client_examples/test-cloud-app/autobuild/linux/autobuild deleted file mode 100755 index bbbc13e..0000000 --- a/app_client_examples/test-cloud-app/autobuild/linux/autobuild +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/make -f -# Copyright (C) 2015 - 2018 "IoT.bzh" -# Copyright (C) 2020 Konsulko Group -# 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)) -ROOT_DIR := $(abspath $(dir $(THISFILE))/../..) - -# Build directories -# Note that the debug directory is defined in relation to the release -# directory (BUILD_DIR), this needs to be kept in mind if over-riding -# it and building that widget types, the specific widget type variable -# (e.g. BUILD_DIR_DEBUG) may also need to be specified to yield the -# desired output hierarchy. -BUILD_DIR = $(ROOT_DIR)/build -BUILD_DIR_DEBUG = $(abspath $(BUILD_DIR)/../build-debug) - -# Output directory variable for use in pattern rules. -# This is intended for internal use only, hence the explicit override -# definition. -override OUTPUT_DIR = $(BUILD_DIR) - -# Final install directory for widgets -DEST = $(OUTPUT_DIR) - -# Default build type for release builds -BUILD_TYPE = release - -.PHONY: all help update install distclean -.PHONY: clean clean-release clean-debug clean-all -.PHONY: configure configure-release configure-debug -.PHONY: build build-release build-debug build-all -.PHONY: package package-release package-debug package-all - -help: - @echo "List of targets available:" - @echo "" - @echo "- all" - @echo "- help" - @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 $(DEST) 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" - -all: package-all - -# Target specific variable over-rides so static pattern rules can be -# used for the various type-specific targets. - -configure-debug build-debug package-debug clean-debug: OUTPUT_DIR = $(BUILD_DIR_DEBUG) -configure-debug build-debug package-debug: BUILD_TYPE = debug - -clean-release clean-debug: - @if [ -d $(OUTPUT_DIR) ]; then \ - $(MAKE) -C $(OUTPUT_DIR) $(CLEAN_ARGS) clean; \ - else \ - echo Nothing to clean; \ - fi - -clean: clean-release - -clean-all: clean-release clean-debug - -distclean: clean-all - -configure-release configure-debug: - @mkdir -p $(OUTPUT_DIR) - @if [ ! -f $(OUTPUT_DIR)/Makefile ]; then \ - (cd $(OUTPUT_DIR) && qmake CONFIG+=$(BUILD_TYPE) $(CONFIGURE_ARGS) $(ROOT_DIR)); \ - fi - -configure: configure-release - -build-release build-debug: build-%: configure-% - @$(MAKE) -C $(OUTPUT_DIR) $(BUILD_ARGS) all - -build: build-release - -build-all: build-release build-debug - -package-release package-debug: package-%: build-% - @cp $(OUTPUT_DIR)/package/*.wgt $(OUTPUT_DIR)/ - @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \ - mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \ - fi - - -package: package-release - -package-all: package-release package-debug - -install: build - @$(MAKE) -C $(BUILD_DIR) $(INSTALL_ARGS) install diff --git a/app_client_examples/test-cloud-app/package/config.xml b/app_client_examples/test-cloud-app/package/config.xml deleted file mode 100755 index 43eceaf..0000000 --- a/app_client_examples/test-cloud-app/package/config.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<widget xmlns="http://www.w3.org/ns/widgets" id="testcloudapp1" version="0.1"> - <name>CloudApp1</name> - <icon src="icon.svg"/> - <content src="bin/testcloudapp" type="application/vnd.agl.native"/> - <description>Test Cloud App</description> - <author>Mera</author> - <license>APL 2.0</license> - <feature name="urn:AGL:widget:required-api"> - <param name="windowmanager" value="ws"/> - <param name="homescreen" value="ws"/> - <param name="cloudproxy" value="ws"/> - </feature> - <feature name="urn:AGL:widget:required-permission"> - <param name="urn:AGL:permission::public:no-htdocs" value="required"/> - <param name="urn:AGL:permission::public:display" value="required" /> - </feature> -</widget> diff --git a/app_client_examples/test-cloud-app/package/icon.svg b/app_client_examples/test-cloud-app/package/icon.svg deleted file mode 100755 index b9b8e67..0000000 --- a/app_client_examples/test-cloud-app/package/icon.svg +++ /dev/null @@ -1,266 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> - -<svg - xmlns:i="&#38;ns_ai;" - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.1" - x="0px" - y="0px" - viewBox="0 0 320 320" - style="enable-background:new 0 0 320 320;" - xml:space="preserve" - id="svg2" - inkscape:version="0.91 r13725" - sodipodi:docname="icon.svg"><metadata - id="metadata1292"><rdf:RDF><cc:Work - rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs - id="defs1290" /><sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="2560" - inkscape:window-height="1464" - id="namedview1288" - showgrid="false" - inkscape:zoom="4.17193" - inkscape:cx="183.76952" - inkscape:cy="191.17869" - inkscape:window-x="0" - inkscape:window-y="0" - inkscape:window-maximized="1" - inkscape:current-layer="svg2" /><style - type="text/css" - id="style4"> - .st0{display:none;} - .st1{display:inline;} - .st2{opacity:0.4;fill:url(#SVGID_1_);} - .st3{fill:url(#SVGID_2_);} - .st4{fill:#FFFFFF;} - .st5{font-family:'Roboto-Regular';} - .st6{font-size:25px;} - .st7{letter-spacing:6;} - .st8{fill:url(#SVGID_3_);} - .st9{fill:url(#SVGID_4_);} - .st10{fill:url(#SVGID_5_);} - .st11{fill:url(#SVGID_6_);} - .st12{fill:url(#SVGID_7_);} - .st13{fill:url(#SVGID_8_);} - .st14{fill:url(#SVGID_9_);} - .st15{fill:url(#SVGID_10_);} - .st16{fill:url(#SVGID_11_);} - .st17{fill:url(#SVGID_12_);} - .st18{fill:url(#SVGID_13_);} - .st19{fill:url(#SVGID_14_);} - .st20{fill:url(#SVGID_15_);} - .st21{fill:url(#SVGID_16_);} - .st22{fill:url(#SVGID_17_);} - .st23{fill:url(#SVGID_18_);} - .st24{opacity:0.29;} - .st25{fill:url(#SVGID_19_);} - .st26{fill:url(#SVGID_20_);} - .st27{fill:url(#SVGID_21_);} - .st28{fill:url(#SVGID_22_);} - .st29{fill:url(#SVGID_23_);} - .st30{fill:url(#SVGID_24_);} - .st31{fill:url(#SVGID_25_);} - .st32{fill:url(#SVGID_26_);} - .st33{fill:url(#SVGID_27_);} - .st34{fill:url(#SVGID_28_);} - .st35{fill:url(#SVGID_29_);} - .st36{fill:url(#SVGID_30_);} - .st37{fill:url(#SVGID_31_);} - .st38{fill:url(#SVGID_32_);} - .st39{fill:url(#SVGID_33_);} - .st40{fill:url(#SVGID_34_);} - .st41{fill:url(#SVGID_35_);} - .st42{fill:url(#SVGID_36_);} - .st43{opacity:0.4;fill:url(#SVGID_37_);} - .st44{fill:url(#SVGID_38_);} - .st45{fill:url(#SVGID_39_);} - .st46{fill:url(#SVGID_40_);} - .st47{fill:url(#SVGID_41_);} - .st48{fill:url(#SVGID_42_);} - .st49{fill:url(#SVGID_43_);} - .st50{fill:url(#SVGID_44_);} - .st51{display:inline;opacity:0.29;} - .st52{display:inline;fill:url(#SVGID_45_);} - .st53{display:inline;fill:url(#SVGID_46_);} - .st54{display:inline;fill:#FFFFFF;} - .st55{display:inline;fill:url(#SVGID_47_);} - .st56{display:inline;fill:url(#SVGID_48_);} - .st57{display:inline;fill:url(#SVGID_49_);} - .st58{display:inline;fill:url(#SVGID_50_);} - .st59{display:inline;fill:url(#SVGID_51_);} - .st60{display:inline;fill:url(#SVGID_52_);} - .st61{opacity:0.4;fill:url(#SVGID_53_);} - .st62{fill:url(#SVGID_54_);} - .st63{fill:url(#SVGID_55_);} - .st64{fill:url(#SVGID_56_);} - .st65{fill:url(#SVGID_57_);} - .st66{fill:url(#SVGID_58_);} - .st67{opacity:0.4;fill:url(#SVGID_59_);} - .st68{fill:url(#SVGID_60_);} - .st69{fill:url(#SVGID_61_);} - .st70{fill:url(#SVGID_62_);} - .st71{fill:url(#SVGID_63_);} - .st72{fill:url(#SVGID_64_);} - .st73{fill:url(#SVGID_65_);} - .st74{fill:url(#SVGID_66_);} - .st75{fill:url(#SVGID_67_);} - .st76{fill:url(#SVGID_68_);} - .st77{fill:url(#SVGID_69_);} - .st78{fill:url(#SVGID_70_);} - .st79{fill:url(#SVGID_71_);} - .st80{fill:url(#SVGID_72_);} - .st81{fill:url(#SVGID_73_);} - .st82{fill:url(#SVGID_74_);} - .st83{fill:url(#SVGID_75_);} - .st84{fill:url(#SVGID_76_);} - .st85{fill:url(#SVGID_77_);} - .st86{fill:url(#SVGID_78_);} - .st87{fill:url(#SVGID_79_);} - .st88{fill:url(#SVGID_80_);} - .st89{fill:url(#SVGID_81_);} - .st90{fill:url(#SVGID_82_);} - .st91{fill:url(#SVGID_83_);} - .st92{fill:url(#SVGID_84_);} - .st93{fill:url(#SVGID_85_);} - .st94{fill:url(#SVGID_86_);} - .st95{opacity:0.4;fill:url(#SVGID_87_);} - .st96{fill:url(#SVGID_88_);} - .st97{fill:url(#SVGID_89_);} - .st98{fill:url(#SVGID_90_);} - .st99{fill:url(#SVGID_91_);} - .st100{fill:url(#SVGID_92_);} - .st101{fill:url(#SVGID_93_);} - .st102{fill:url(#SVGID_94_);} - .st103{opacity:0.4;fill:url(#SVGID_95_);} - .st104{fill:url(#SVGID_96_);} - .st105{fill:url(#SVGID_97_);} - .st106{fill:url(#SVGID_98_);} - .st107{fill:url(#SVGID_99_);} - .st108{fill:url(#SVGID_100_);} - .st109{fill:url(#SVGID_101_);} - .st110{display:inline;fill:url(#SVGID_102_);} - .st111{display:inline;fill:url(#SVGID_103_);} - .st112{fill:url(#SVGID_104_);} - .st113{fill:url(#SVGID_105_);} - .st114{fill:url(#SVGID_106_);} - .st115{fill:url(#SVGID_107_);} - .st116{fill:url(#SVGID_108_);} - .st117{opacity:0.4;fill:url(#SVGID_109_);} - .st118{fill:url(#SVGID_110_);} - .st119{fill:url(#SVGID_111_);} - .st120{fill:url(#SVGID_112_);} - .st121{fill:url(#SVGID_113_);} - .st122{fill:url(#SVGID_114_);} - .st123{opacity:0.4;fill:url(#SVGID_115_);} - .st124{fill:url(#SVGID_116_);} - .st125{fill:url(#SVGID_117_);} - .st126{fill:url(#SVGID_118_);} - .st127{display:inline;fill:url(#SVGID_119_);} - .st128{display:inline;fill:url(#SVGID_120_);} - .st129{fill:url(#SVGID_121_);} - .st130{fill:url(#SVGID_122_);} -</style><switch - id="switch6"><g - i:extraneous="self" - id="g8"><g - id="Multimedia_Inactive_copy"><circle - class="st24" - cx="159.7" - cy="133.4" - r="101.9" - id="circle884" /><linearGradient - id="SVGID_91_" - gradientUnits="userSpaceOnUse" - x1="115.9317" - y1="254.1836" - x2="256.3852" - y2="-133.5267"><stop - offset="0" - style="stop-color:#8BC53F" - id="stop887" /><stop - offset="2.015080e-02" - style="stop-color:#7CCB56;stop-opacity:0.9678" - id="stop889" /><stop - offset="6.089833e-02" - style="stop-color:#62D67D;stop-opacity:0.9028" - id="stop891" /><stop - offset="0.1057" - style="stop-color:#4BDFA0;stop-opacity:0.8312" - id="stop893" /><stop - offset="0.1543" - style="stop-color:#38E7BE;stop-opacity:0.7537" - id="stop895" /><stop - offset="0.2077" - style="stop-color:#28EED6;stop-opacity:0.6684" - id="stop897" /><stop - offset="0.2681" - style="stop-color:#1CF3E8;stop-opacity:0.572" - id="stop899" /><stop - offset="0.3394" - style="stop-color:#13F6F5;stop-opacity:0.4581" - id="stop901" /><stop - offset="0.4323" - style="stop-color:#0EF8FD;stop-opacity:0.3098" - id="stop903" /><stop - offset="0.6264" - style="stop-color:#0DF9FF;stop-opacity:0" - id="stop905" /></linearGradient><circle - class="st99" - cx="159.7" - cy="133.4" - r="101.9" - id="circle907" /><linearGradient - id="SVGID_92_" - gradientUnits="userSpaceOnUse" - x1="4.0481" - y1="287.9492" - x2="320.4859" - y2="-15.4029" - gradientTransform="matrix(1 5.464556e-03 -5.464556e-03 1 -2.0192 -3.0212)"><stop - offset="0" - style="stop-color:#59FF7F" - id="stop910" /><stop - offset="1" - style="stop-color:#6BFBFF" - id="stop912" /></linearGradient><path - class="st100" - d="M160,238.8c-0.2,0-0.4,0-0.6,0c-58-0.3-104.9-47.7-104.6-105.7C55.2,75.3,102.3,28.5,160,28.5 c0.2,0,0.4,0,0.6,0c58,0.3,104.9,47.7,104.6,105.7l0,0C264.8,192,217.7,238.8,160,238.8z M160,32.2 c-55.7,0-101.2,45.2-101.5,100.9c-0.3,55.9,45,101.7,100.9,102c0.2,0,0.4,0,0.6,0c55.7,0,101.2-45.2,101.5-100.9 c0.3-55.9-45-101.7-100.9-102C160.4,32.2,160.2,32.2,160,32.2z" - id="path914" /><g - id="g916"><text - transform="matrix(1 0 0 1 53.5841 284.7119)" - class="st4 st5 st6 st7" - id="text918">VIDEOPLAYER -</text> -<linearGradient - id="SVGID_94_" - gradientUnits="userSpaceOnUse" - x1="145.3286" - y1="203.8951" - x2="191.6285" - y2="70.3637"><stop - offset="0" - style="stop-color:#59FF7F" - id="stop928" /><stop - offset="1" - style="stop-color:#6BFBFF" - id="stop930" /></linearGradient><polygon - class="st102" - points="125,75 125,200 225,137.5 " - id="polygon932" - style="fill:url(#SVGID_94_)" /></g></g></g></switch></svg>
\ No newline at end of file diff --git a/app_client_examples/test-cloud-app/package/package.pro b/app_client_examples/test-cloud-app/package/package.pro deleted file mode 100755 index c8af452..0000000 --- a/app_client_examples/test-cloud-app/package/package.pro +++ /dev/null @@ -1,28 +0,0 @@ - -DISTFILES = icon.svg config.xml - -copy_icon.target = $$OUT_PWD/root/icon.svg -copy_icon.depends = $$_PRO_FILE_PWD_/icon.svg -copy_icon.commands = $(COPY_FILE) \"$$replace(copy_icon.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_icon.target, /, $$QMAKE_DIR_SEP)\" -QMAKE_EXTRA_TARGETS += copy_icon -PRE_TARGETDEPS += $$copy_icon.target - -copy_config.target = $$OUT_PWD/root/config.xml -copy_config.depends = $$_PRO_FILE_PWD_/config.xml -copy_config.commands = $(COPY_FILE) \"$$replace(copy_config.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_config.target, /, $$QMAKE_DIR_SEP)\" -QMAKE_EXTRA_TARGETS += copy_config -PRE_TARGETDEPS += $$copy_config.target - -WGT_TYPE = -CONFIG(debug, debug|release) { - WGT_TYPE = -debug -} - -wgt.target = package -wgt.commands = wgtpkg-pack -f -o testcloudapp$${WGT_TYPE}.wgt root - -QMAKE_EXTRA_TARGETS += wgt - -deploy.files = testcloudapp.wgt -deploy.path = /usr/AGL/apps/autoinstall -INSTALLS += deploy diff --git a/app_client_examples/test-cloud-app/testcloudapp.pro b/app_client_examples/test-cloud-app/testcloudapp.pro deleted file mode 100755 index 579a952..0000000 --- a/app_client_examples/test-cloud-app/testcloudapp.pro +++ /dev/null @@ -1,3 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS = app package -package.depends += app diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake index d6e9516..26a3dda 100644 --- a/conf.d/cmake/config.cmake +++ b/conf.d/cmake/config.cmake @@ -29,7 +29,7 @@ set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/apps/agl-service-clou set(PROJECT_ICON "icon.svg") set(PROJECT_AUTHOR "MERA") set(PROJECT_AUTHOR_EMAIL "") -set(PROJECT_VERSION "0.1") +set(PROJECT_VERSION "0.2") set(PROJECT_LICENSE "APL2.0") set(PROJECT_LANGUAGES,"CXX") @@ -75,12 +75,14 @@ set (PKG_REQUIRED_LIST libsystemd>=222 afb-daemon glib-2.0 + awsiotsdk ) # Static constante definition # ----------------------------- add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-pthread>) +add_compile_options(-std=c++17) # Customize link option # ----------------------------- @@ -136,7 +138,7 @@ set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install # Optional extra libraries # ------------------------- -#set(EXTRA_LINK_LIBRARIES) +set(EXTRA_LINK_LIBRARIES stdc++fs) # Optional force binding installation # ------------------------------------ diff --git a/etc/config.ini b/etc/config.ini index fa97cd7..797078e 100644 --- a/etc/config.ini +++ b/etc/config.ini @@ -1,3 +1,15 @@ [AzureCloudConnection] +Enabled=true DeviceConnectionString=My=Azure=Device=Connection=String +[AwsCloudConnection] +Enabled=false +MqttHost=a31l7c660mcoum-ats.iot.eu-central-1.amazonaws.com +MqttPort=443 +MqttClientId=my-agl-cloud-client +MqttOutTopic=my/sqs/rule +MqttInTopic=my/in +ThingName=MyAwsIotThing +RootCaCert=/aws/AmazonRootCA1.pem.crt +DeviceCert=/aws/device-certificate.pem.crt +DevicePrivKey=/aws/device-private.pem.key diff --git a/src/AwsClient.cpp b/src/AwsClient.cpp new file mode 100644 index 0000000..856eef0 --- /dev/null +++ b/src/AwsClient.cpp @@ -0,0 +1,410 @@ +#include "AwsClient.h" + +#include <filesystem> + +#include "aws_iot_config.h" +#include "aws_iot_mqtt_client_interface.h" + +#include <nlohmann/json.hpp> + +#include "CloudType.h" +#include "ClientManager.h" + +#include <glib.h> +#include <afb/afb-binding.h> // for AFB_* logger + +namespace +{ + +void aws_subscribe_callback(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) +{ + (void)pClient; + AFB_INFO("AWS subscribe callback: %.*s\t%.*s", topicNameLen, topicName, (int) params->payloadLen, (char *) params->payload); + + if (!pData) + { + AFB_INFO("AwsClient is null"); + return; + } + + AwsClient* aws = (AwsClient*)pData; + + std::string topic; + if (topicName && topicNameLen > 0) + topic.assign(topicName, topicNameLen); + + if (topic != aws->conf().mqtt_in_topic) + { + AFB_WARNING("Received message topic is not equal to configured: '%s'", topic.c_str()); + return; + } + + if (!params || !params->payload || params->payloadLen <= 0) + { + AFB_WARNING("Received message doesn't contain payload"); + return; + } + + const auto& pmsg = AwsClient::parseMessage(std::string((char*)params->payload, params->payloadLen)); + const auto& appid = pmsg.first; + const auto& data = pmsg.second; + if (appid.empty()) + { + AFB_ERROR("Can't parse received message"); + return; + } + + ClientManager::instance().emitReceivedMessage(appid, CloudType::Aws, data); +} + +void aws_disconnect_callback(AWS_IoT_Client *pClient, void *data) +{ + (void)data; + AFB_WARNING("AWS MQTT disconnect"); + IoT_Error_t rc = IoT_Error_t::FAILURE; + + if(pClient) + return; + + if(aws_iot_is_autoreconnect_enabled(pClient)) + { + AFB_INFO("Auto Reconnect is enabled. Reconnecting attempt will start now"); + } + else + { + AFB_WARNING("Auto Reconnect not enabled. Starting manual reconnect..."); + rc = aws_iot_mqtt_attempt_reconnect(pClient); + + if(IoT_Error_t::NETWORK_RECONNECTED == rc) + AFB_WARNING("Manual Reconnect Successful"); + else + AFB_WARNING("Manual Reconnect Failed - %d", rc); + } +} + +} // end namespace + +AwsClient::AwsClient() = default; + +AwsClient::~AwsClient() +{ + stop(); +} + +// static +std::pair<std::string, std::string> AwsClient::parseMessage(const std::string& msg) +{ + std::string appid; + std::string data; + std::string cloud; + try + { + const nlohmann::json& jmsg = nlohmann::json::parse(msg); + appid = jmsg.at("application_id").get<std::string>(); + data = jmsg.at("data").get<std::string>(); + } + catch (const std::exception& ex) + { + AFB_ERROR("Can't parse message (%s): %s", msg.c_str(), ex.what()); + appid.clear(); // indicate error + } + + return {appid, data}; +} + +// static +std::string AwsClient::getApplicationId(const std::string& msg) +{ + std::string appid; + try + { + const nlohmann::json& jmsg = nlohmann::json::parse(msg); + appid = jmsg.at("application_id").get<std::string>(); + } + catch (const std::exception& ex) + { + AFB_ERROR("Can't parse message (%s): %s", msg.c_str(), ex.what()); + } + + return appid; +} + +void AwsClient::main_loop() +{ + IoT_Publish_Message_Params params; + params.qos = QoS::QOS1; + params.isRetained = 0; + + while(!m_thread_stop) + { + IoT_Error_t rc = aws_iot_mqtt_yield(m_aws_client.get(), 100); + if(IoT_Error_t::NETWORK_ATTEMPTING_RECONNECT == rc) + { + // If the client is attempting to reconnect we will skip the rest of the loop. + continue; + } + + std::string data; + { + std::lock_guard<std::mutex> lock(m_mutex); + if (m_queue.empty()) + continue; + + data = std::move(m_queue.front()); + m_queue.pop_front(); + } + params.payload = const_cast<char*>(data.c_str()); + params.payloadLen = data.length(); + + + std::string appid{getApplicationId(data)}; + if (appid.empty()) + { + AFB_ERROR("Can't obtain application_id from scheduled message (msg will be dropped)"); + // ClientManager::instance().emitSendMessageConfirmation(appid, false); + continue; + } + + rc = IoT_Error_t::FAILURE; + if (!appid.empty()) + { + rc = aws_iot_mqtt_publish(m_aws_client.get(), m_conf.mqtt_out_topic.c_str(), (uint16_t)m_conf.mqtt_out_topic.length(), ¶ms); + if (rc == IoT_Error_t::MQTT_REQUEST_TIMEOUT_ERROR) + { + AFB_WARNING("Publish ack not received"); + //???? rc = IoT_Error_t::SUCCESS; + } + } + + ClientManager::instance().emitSendMessageConfirmation(appid, CloudType::Aws, rc == IoT_Error_t::SUCCESS); + } +} + +void AwsClient::start() +{ + if (m_started) + return; + + if (!m_aws_client || !m_connected) + { + AFB_ERROR("Can't start AWS thread: connection is not created"); + return; + } + + m_thread_stop = false; + m_thread = std::thread(&AwsClient::main_loop, this); + m_started = true; +} + +void AwsClient::stop() +{ + if (m_thread.joinable()) + { + AFB_DEBUG("Wait AWS thread..."); + m_thread_stop = true; + m_thread.join(); + AFB_DEBUG("AWS thread joined"); + + if (m_connected) + { + AFB_DEBUG("AWS disconnecting..."); + aws_iot_mqtt_unsubscribe(m_aws_client.get(), m_conf.mqtt_in_topic.c_str(), (uint16_t)m_conf.mqtt_in_topic.length()); + aws_iot_mqtt_disconnect(m_aws_client.get()); + aws_iot_mqtt_yield(m_aws_client.get(), 100); + AFB_DEBUG("AWS disconnected"); + } + } + + m_started = false; +} + +bool AwsClient::createConnection() +{ + AFB_NOTICE("%s called", __FUNCTION__); + + if (m_aws_client) + { + AFB_ERROR("AWS connection already created"); + return false; + } + + m_aws_client.reset(new AWS_IoT_Client); + + IoT_Client_Init_Params mqttInitParams = iotClientInitParamsDefault; + IoT_Client_Connect_Params connectParams = iotClientConnectParamsDefault; + + AFB_DEBUG("rootCA cert %s", m_conf.root_ca_cert_path.c_str()); + AFB_DEBUG("client cert %s", m_conf.device_cert_path.c_str()); + AFB_DEBUG("client key %s", m_conf.device_priv_key_path.c_str()); + + mqttInitParams.enableAutoReconnect = false; // We enable this later below + mqttInitParams.pHostURL = const_cast<char*>(m_conf.mqtt_host.c_str()); + mqttInitParams.port = m_conf.mqtt_port; + mqttInitParams.pRootCALocation = const_cast<char*>(m_conf.root_ca_cert_path.c_str()); + mqttInitParams.pDeviceCertLocation = const_cast<char*>(m_conf.device_cert_path.c_str()); + mqttInitParams.pDevicePrivateKeyLocation = const_cast<char*>(m_conf.device_priv_key_path.c_str()); + mqttInitParams.mqttCommandTimeout_ms = 20000; + mqttInitParams.tlsHandshakeTimeout_ms = 5000; + mqttInitParams.isSSLHostnameVerify = true; + mqttInitParams.disconnectHandler = aws_disconnect_callback; + mqttInitParams.disconnectHandlerData = nullptr; + + IoT_Error_t rc = aws_iot_mqtt_init(m_aws_client.get(), &mqttInitParams); + if(IoT_Error_t::SUCCESS != rc) + { + AFB_ERROR("aws_iot_mqtt_init returned error: %d ", rc); + return false; + } + + connectParams.keepAliveIntervalInSec = 600; + connectParams.isCleanSession = true; + connectParams.MQTTVersion = MQTT_Ver_t::MQTT_3_1_1; + connectParams.pClientID = const_cast<char*>(m_conf.mqtt_client_id.c_str()); + connectParams.clientIDLen = (uint16_t)m_conf.mqtt_client_id.size(); + connectParams.isWillMsgPresent = false; + + AFB_NOTICE("Connecting..."); + rc = aws_iot_mqtt_connect(m_aws_client.get(), &connectParams); + if(IoT_Error_t::SUCCESS != rc) + { + AFB_NOTICE("Error(%d) connecting to %s:%d", rc, mqttInitParams.pHostURL, mqttInitParams.port); + return false; + } + + AFB_NOTICE("Subscribing..."); + rc = aws_iot_mqtt_subscribe(m_aws_client.get(), m_conf.mqtt_in_topic.c_str(), (uint16_t)m_conf.mqtt_in_topic.length(), QOS1, aws_subscribe_callback, this); + if(IoT_Error_t::SUCCESS != rc) + { + AFB_ERROR("Error subscribing: %d ", rc); + return false; + } + + /* + Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h + #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL + #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL + */ + rc = aws_iot_mqtt_autoreconnect_set_status(m_aws_client.get(), true); + if(IoT_Error_t::SUCCESS != rc) + { + AFB_ERROR("Unable to set AutoReconnect to true: %d", rc); + return false; + } + + m_connected = true; + + if (!m_started) + start(); + + return true; +} + +bool AwsClient::sendMessage(const std::string& appid, const std::string& data) +{ + if (!m_aws_client || !m_connected) + { + AFB_ERROR("AwsClient is not ready for message sending"); + return false; + } + + nlohmann::json jmsg{ + {"application_id", appid}, + {"data", data} + }; + + const std::string& msg = jmsg.dump(); + + { + std::lock_guard<std::mutex> lock(m_mutex); + + if (m_queue.size() < MAX_QUEUE_SIZE) + { + m_queue.push_back(msg); + AFB_DEBUG("AWS queue size: %lu", m_queue.size()); + return true; + } + else + { + AFB_ERROR("Can't send message: AWS queue size exceeded: %lu", m_queue.size()); + } + } + + return false; +} + +bool AwsClient::enabled() const +{ + return m_conf.enabled; +} + +bool AwsClient::connected() const +{ + return m_connected; +} + +const AwsClientConfig& AwsClient::conf() const +{ + return m_conf; +} + +bool AwsClient::loadConf(GKeyFile* conf_file) +{ + g_autoptr(GError) error = nullptr; + + auto read_string = [&conf_file](const char* section, const char* key, std::string& set_to) + { + g_autoptr(GError) error = nullptr; + g_autofree gchar *value = g_key_file_get_string(conf_file, section, key, &error); + if (value == nullptr) + { + AFB_ERROR("Can't read %s/%s from config", section, key); + return false; + } + if (!value[0]) + { + AFB_ERROR("Value %s/%s is empty", section, key); + return false; + } + set_to = value; + return true; + }; + + auto check_file = [](const std::string& path) + { + if (!std::filesystem::exists(path)) + { + AFB_ERROR("File %s doesn't exists", path.c_str()); + return false; + } + return true; + }; + + //-- AWS parameters: + m_conf.enabled = g_key_file_get_boolean(conf_file, "AwsCloudConnection", "Enabled", &error); + + if (!m_conf.enabled) + return true; // don't check other settings in this case + + if (!read_string("AwsCloudConnection", "MqttHost", m_conf.mqtt_host) || + !read_string("AwsCloudConnection", "MqttClientId", m_conf.mqtt_client_id) || + !read_string("AwsCloudConnection", "MqttOutTopic", m_conf.mqtt_out_topic) || + !read_string("AwsCloudConnection", "MqttInTopic", m_conf.mqtt_in_topic) || + !read_string("AwsCloudConnection", "ThingName", m_conf.thing_name) || + !read_string("AwsCloudConnection", "RootCaCert", m_conf.root_ca_cert_path) || + !read_string("AwsCloudConnection", "DeviceCert", m_conf.device_cert_path) || + !read_string("AwsCloudConnection", "DevicePrivKey", m_conf.device_priv_key_path)) + return false; + + m_conf.mqtt_port = (uint16_t)g_key_file_get_uint64(conf_file, "AwsCloudConnection", "MqttPort", &error); + if (!m_conf.mqtt_port) + { + AFB_ERROR("Value AwsCloudConnection/MqttPort is not configured"); + return false; + } + + if (!check_file(m_conf.root_ca_cert_path) || !check_file(m_conf.device_cert_path) || !check_file(m_conf.device_priv_key_path)) + return false; + + return true; +} diff --git a/src/AwsClient.h b/src/AwsClient.h new file mode 100644 index 0000000..6ab15c4 --- /dev/null +++ b/src/AwsClient.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 MERA + * + * 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. + */ + +#pragma once + +#include "CloudClient.h" +#include <string> +#include <thread> +#include <atomic> +#include <deque> +#include <mutex> + + +struct AwsClientConfig +{ + bool enabled{false}; + std::string mqtt_host; + uint16_t mqtt_port{433}; + std::string mqtt_client_id; + std::string mqtt_out_topic; + std::string mqtt_in_topic; + std::string thing_name; + std::string root_ca_cert_path; + std::string device_cert_path; + std::string device_priv_key_path; +}; + +typedef struct _Client AWS_IoT_Client; + +class AwsClient : public CloudClient +{ +public: + static constexpr size_t MAX_QUEUE_SIZE{10000}; + + AwsClient(); + ~AwsClient(); + + bool sendMessage(const std::string& appid, const std::string& data) override; + bool createConnection() override; + + bool enabled() const override; + bool connected() const override; + bool loadConf(GKeyFile* conf_file) override; + + const AwsClientConfig& conf() const; + + static std::pair<std::string, std::string> parseMessage(const std::string& msg); + static std::string getApplicationId(const std::string& msg); + +protected: + void main_loop(); + void start(); + void stop(); + +protected: + std::unique_ptr<AWS_IoT_Client> m_aws_client; + AwsClientConfig m_conf; + std::thread m_thread; + std::atomic_bool m_thread_stop{false}; + std::atomic_bool m_started{false}; + bool m_connected{false}; + std::deque<std::string> m_queue; + std::mutex m_mutex; +}; diff --git a/src/AzureClient.cpp b/src/AzureClient.cpp new file mode 100644 index 0000000..e381922 --- /dev/null +++ b/src/AzureClient.cpp @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2020 MERA + * + * 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 "AzureClient.h" + +#include <iothub.h> +#include <iothub_device_client.h> +#include <iothub_client_options.h> +#include <iothub_message.h> +#include <iothubtransportmqtt.h> +#include <azure_c_shared_utility/threadapi.h> // ThreadAPI_Sleep() +#include <azure_c_shared_utility/tickcounter.h> // tickcounter_ms_t + +#include "utils.h" +#include "CloudType.h" +#include "ClientManager.h" + +#include <glib.h> +#include <afb/afb-binding.h> // for AFB_* logger + +namespace +{ + +void connection_status_callback(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* user_context) +{ + AFB_NOTICE("%s called: result %d, reason %d", __FUNCTION__, result, reason); + + (void)reason; + (void)user_context; + // This sample DOES NOT take into consideration network outages. + if (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED && reason == IOTHUB_CLIENT_CONNECTION_OK) + { + AFB_NOTICE("The device client is connected to iothub"); + } + else + { + AFB_NOTICE("The device client has been disconnected"); + } +} + +IOTHUBMESSAGE_DISPOSITION_RESULT receive_msg_callback(IOTHUB_MESSAGE_HANDLE message, void* user_context) +{ + AFB_NOTICE("%s called", __FUNCTION__); + (void)user_context; + + IOTHUBMESSAGE_CONTENT_TYPE content_type = IoTHubMessage_GetContentType(message); + + if (content_type == IOTHUBMESSAGE_BYTEARRAY) + { + const unsigned char* buff_msg; + size_t buff_len; + + if (IoTHubMessage_GetByteArray(message, &buff_msg, &buff_len) != IOTHUB_MESSAGE_OK) + { + AFB_ERROR("Failure retrieving byte array message"); + } + else + { + AFB_NOTICE("Received Binary message, size %d, data '%.*s'", (int)buff_len, (int)buff_len, buff_msg); + } + + const char* app_id = IoTHubMessage_GetProperty(message, "application_id"); + AFB_NOTICE("Received property 'application_id': %s", (app_id ? app_id : "<unavailable>")); + + if (app_id && app_id[0]) + ClientManager::instance().emitReceivedMessage(app_id, CloudType::Azure, std::string((const char*)buff_msg, buff_len)); + else + AFB_ERROR("Can't emit SendMessageConfirmation: appid is not valid"); + } + else if (content_type == IOTHUBMESSAGE_STRING) + { + const char* string_msg = IoTHubMessage_GetString(message); + if (string_msg == nullptr) + { + AFB_NOTICE("Failure retrieving String message"); + } + else + { + AFB_NOTICE("Received String message, size %lu, data '%s'", strlen(string_msg), string_msg); + } + + const char* app_id = IoTHubMessage_GetProperty(message, "application_id"); + AFB_NOTICE("Received property 'application_id': %s", (app_id ? app_id : "<unavailable>")); + + if (app_id && app_id[0]) + ClientManager::instance().emitReceivedMessage(app_id, CloudType::Azure, string_msg); + else + AFB_ERROR("Can't emit SendMessageConfirmation: appid is not valid"); + } + else + { + AFB_ERROR("Unsupported message content type"); + } + + return IOTHUBMESSAGE_ACCEPTED; +} + + +int device_method_callback(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* resp_size, void* userContextCallback) +{ + AFB_NOTICE("%s called, method_name %s", __FUNCTION__, method_name); + + const char* device_id = (const char*)userContextCallback; + + int status = 501; + const char* RESPONSE_STRING = "{ \"Response\": \"Unknown method requested.\" }"; + + AFB_NOTICE("Device Method called for device %s", device_id); + AFB_NOTICE("Device Method name: %s", method_name); + AFB_NOTICE("Device Method payload: %.*s", (int)size, (const char*)payload); + + + AFB_NOTICE("Response status: %d", status); + AFB_NOTICE("Response payload: %s", RESPONSE_STRING); + + *resp_size = strlen(RESPONSE_STRING); + if ((*response = (unsigned char*)malloc(*resp_size)) == nullptr) + { + status = -1; + } + else + { + memcpy(*response, RESPONSE_STRING, *resp_size); + } + + return status; +} + + +void send_confirm_callback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) +{ + AFB_NOTICE("%s called, result %d", __FUNCTION__, result); + (void)userContextCallback; + // When a message is sent this callback will get invoked + + AFB_NOTICE("Confirmation callback result %s", MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); + + const char* appid = (const char*)userContextCallback; + if (!appid || !appid[0]) + { + AFB_ERROR("Confirmation callback: appid is not set"); + + if (userContextCallback) + free(userContextCallback); + + return; + } + + ClientManager::instance().emitSendMessageConfirmation(appid, CloudType::Azure, result == IOTHUB_CLIENT_CONFIRMATION_OK); + free(userContextCallback); +} + +} //end namespace + +AzureClient::AzureClient() = default; + +AzureClient::~AzureClient() +{ + if (m_iot_inited) + { + if (m_azure_client && *m_azure_client) + IoTHubDeviceClient_Destroy(*m_azure_client); + + IoTHub_Deinit(); + } +} + +bool AzureClient::createConnection() +{ + AFB_NOTICE("%s called", __FUNCTION__); + + if (m_iot_inited) + { + AFB_ERROR("Azure IoT already initalized"); + return false; + } + + // Init Azure API: + { + int res = IoTHub_Init(); + m_iot_inited = true; + + if (res) + { + AFB_ERROR("Azure IoTHub_Init() failed: %d", res); + return false; + } + } + + if (m_azure_client) + { + AFB_ERROR("connection already created"); + return false; + } + + IOTHUB_DEVICE_CLIENT_HANDLE device_handle = IoTHubDeviceClient_CreateFromConnectionString(m_conf.device_connection_string.c_str(), MQTT_Protocol); + if (!device_handle) + { + AFB_ERROR("Failure creating Azure IoTHubDeviceClient device"); + return false; + } + + bool traceOn = false; + IoTHubDeviceClient_SetOption(device_handle, OPTION_LOG_TRACE, &traceOn); + IoTHubDeviceClient_SetConnectionStatusCallback(device_handle, connection_status_callback, nullptr); + IoTHubDeviceClient_SetMessageCallback(device_handle, receive_msg_callback, nullptr); + IoTHubDeviceClient_SetDeviceMethodCallback(device_handle, device_method_callback, nullptr); + + tickcounter_ms_t ms_delay = 10; + IoTHubDeviceClient_SetOption(device_handle, OPTION_DO_WORK_FREQUENCY_IN_MS, &ms_delay); // DoWork multithread + + m_azure_client.reset(new IOTHUB_DEVICE_CLIENT_HANDLE{device_handle}); + + return true; +} + + +bool AzureClient::sendMessage(const std::string& appid, const std::string& data) +{ + if (!m_azure_client) + { + AFB_ERROR("AzureClient is not ready for message sending"); + return false; + } + + IOTHUB_MESSAGE_HANDLE message_handle = IoTHubMessage_CreateFromString(data.c_str()); + + utils::scope_exit message_handle_destroy([&message_handle](){ + // The message is copied to the sdk, so the we can destroy it + if (message_handle) + IoTHubMessage_Destroy(message_handle); + }); + + if (!message_handle) + { + AFB_ERROR("Can't create IoTHubMessage message"); + return false; + } + + IoTHubMessage_SetProperty(message_handle, "application_id", appid.c_str()); + + if (IoTHubDeviceClient_SendEventAsync(*m_azure_client, message_handle, send_confirm_callback, strdup(appid.c_str()))) + { + AFB_ERROR("Can't send IoTHubMessage message"); + return false; + } + + return true; +} + +bool AzureClient::enabled() const +{ + return m_conf.enabled; +} + +bool AzureClient::connected() const +{ + return (m_azure_client && *m_azure_client); +} + +bool AzureClient::loadConf(GKeyFile* conf_file) +{ + g_autoptr(GError) error = nullptr; + + // Azure parameters: + m_conf.enabled = g_key_file_get_boolean(conf_file, "AzureCloudConnection", "Enabled", &error); + + g_autofree gchar *value = g_key_file_get_string(conf_file, "AzureCloudConnection", "DeviceConnectionString", &error); + if (value == nullptr) + { + AFB_ERROR("can't read AzureCloudConnection/DeviceConnectionString from config"); + return false; + } + + m_conf.device_connection_string = value; + if (m_conf.device_connection_string.empty()) + { + AFB_ERROR("AzureCloudConnection/DeviceConnectionString is empty"); + return false; + } + + return true; +} diff --git a/src/AzureClient.h b/src/AzureClient.h new file mode 100644 index 0000000..ea69814 --- /dev/null +++ b/src/AzureClient.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 MERA + * + * 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. + */ + +#pragma once + +#include "CloudClient.h" + +#include <memory> + + +struct AzureClientConfig +{ + bool enabled{false}; + std::string device_connection_string; +}; + +typedef struct IOTHUB_CLIENT_CORE_INSTANCE_TAG* IOTHUB_CLIENT_CORE_HANDLE; +typedef IOTHUB_CLIENT_CORE_HANDLE IOTHUB_DEVICE_CLIENT_HANDLE; + +class AzureClient : public CloudClient +{ +public: + AzureClient(); + ~AzureClient(); + + bool sendMessage(const std::string& appid, const std::string& data) override; + bool createConnection() override; + + bool enabled() const override; + bool connected() const override; + bool loadConf(GKeyFile* conf_file) override; + + const AzureClientConfig& conf() const; + + static std::pair<std::string, std::string> parseMessage(const std::string& msg); + static std::string getApplicationId(const std::string& msg); + +protected: + std::unique_ptr<IOTHUB_DEVICE_CLIENT_HANDLE> m_azure_client; + AzureClientConfig m_conf; + bool m_iot_inited{false}; +}; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fde17a5..eec5b09 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,14 +16,18 @@ PROJECT_TARGET_ADD(cloudproxy-binding) -add_definitions(-DAFB_BINDING_VERSION=3) +add_definitions(-DAFB_BINDING_VERSION=3 -D_GNU_SOURCE) set(BINDING_SOURCES cloudproxy-bindings.cpp utils.h - hmi-debug.h ClientManager.h ClientManager.cpp + CloudType.h + AwsClient.h + AwsClient.cpp + AzureClient.h + AzureClient.cpp ) set(AZURE_DEP_LIBS iothub_client) diff --git a/src/ClientManager.cpp b/src/ClientManager.cpp index 74d672d..cbda0e2 100755 --- a/src/ClientManager.cpp +++ b/src/ClientManager.cpp @@ -1,215 +1,218 @@ -/*
- * Copyright (C) 2020 MERA
- *
- * 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 "ClientManager.h"
-#include "hmi-debug.h"
-
-#include <set>
-#include <json-c/json.h>
-
-// static
-void ClientManager::cbRemoveClientCtx(void *data)
-{
- ClientManager::instance().removeClient((ClientManager::ClientCtx*)data);
-}
-
-
-// static
-ClientManager& ClientManager::instance()
-{
- static ClientManager instance;
- return instance;
-}
-
-
-ClientManager::~ClientManager()
-{
- std::lock_guard<std::mutex> lock(this->m_mutex);
- for (auto cl : m_clients)
- delete cl.second;
-}
-
-
-ClientManager::ClientCtx* ClientManager::addClient(afb_req_t req, const std::string& appid)
-{
- ClientCtx* ctx = (ClientCtx*)afb_req_context_get(req);
- if (!ctx)
- {
- HMI_NOTICE("cloudproxy-service", "create new session for %s", appid.c_str());
- ctx = new ClientCtx{appid, afb_api_make_event(req->api, appid.c_str())};
- afb_req_session_set_LOA(req, 1);
- afb_req_context_set(req, ctx, cbRemoveClientCtx);
- }
-
- m_clients[appid] = ctx;
- return ctx;
-}
-
-
-void ClientManager::removeClient(ClientCtx* ctx)
-{
- if(!ctx)
- {
- HMI_ERROR("cloudproxy-service", "data is nullptr");
- return;
- }
-
- HMI_NOTICE("cloudproxy-service", "remove app %s", ctx->appid.c_str());
- std::lock_guard<std::mutex> lock(this->m_mutex);
-
- auto it = m_clients.find(ctx->appid);
- if (it != m_clients.end())
- {
- delete it->second;
- m_clients.erase(it);
- }
-}
-
-
-bool ClientManager::handleRequest(afb_req_t request, const std::string& verb, const std::string& appid)
-{
- HMI_NOTICE("cloudproxy-service", "handleRequest: verb='%s', appid='%s'", verb.c_str(), appid.c_str());
- std::lock_guard<std::mutex> lock(this->m_mutex);
-
- if (appid.empty())
- {
- HMI_ERROR("cloudproxy-service", "appid is empty");
- return false;
- }
-
- auto client_it = m_clients.find(appid);
- if (verb != "subscribe" && client_it == m_clients.end())
- {
- HMI_NOTICE("cloudproxy-service", "client with appid '%s' is not registered", appid.c_str());
- return false;
- }
-
- if (verb == "subscribe")
- {
- const char *value = afb_req_value(request, "event");
- if(!value)
- {
- HMI_ERROR("cloudproxy-service", "Can't subscribe: event name is not defined");
- return false;
- }
- std::string req_event{value};
- HMI_NOTICE("cloudproxy-service", "subscribe req: appid '%s', event '%s'", appid.c_str(), req_event.c_str());
-
- if (!isSupportedEvent(req_event))
- {
- HMI_ERROR("cloudproxy-service", "event '%s' is not supported", req_event.c_str());
- return false;
- }
-
- ClientCtx* ctx = addClient(request, appid);
- ctx->subs_events.insert(req_event);
- if(!ctx->subscription)
- {
- if(afb_req_subscribe(request, ctx->event) == 0)
- {
- ctx->subscription = true;
- }
- else
- {
- HMI_ERROR("cloudproxy-service", "API error in afb_req_subscribe");
- return false;
- }
- }
-
- return true;
- }
- else if (verb == "unsubscribe")
- {
- const char *value = afb_req_value(request, "event");
- if(!value)
- {
- HMI_ERROR("cloudproxy-service", "Can't unsubscribe: event name is not defined");
- return false;
- }
- std::string req_event{value};
- HMI_NOTICE("cloudproxy-service", "unsubscribe req: appid '%s', event '%s'", appid.c_str(), req_event.c_str());
-
- ClientCtx* ctx{client_it->second};
- ctx->subs_events.erase(req_event);
-
- if(ctx->subs_events.empty())
- {
- if (afb_req_unsubscribe(request, ctx->event) != 0)
- HMI_ERROR("cloudproxy-service", "API error in afb_req_unsubscribe");
-
- ctx->subscription = false;
- }
-
- return true;
- }
-
- HMI_NOTICE("cloudproxy-service", "Unsupported verb '%s'", verb.c_str());
- return false;
-}
-
-
-bool ClientManager::isSupportedEvent(const std::string& event)
-{
- const std::set<std::string> event_list{
- "sendMessageConfirmation",
- "receivedMessage"
- };
-
- return (event_list.end() != event_list.find(event));
-}
-
-
-bool ClientManager::emitReceivedMessage(const std::string& appid, const std::string& data)
-{
- std::lock_guard<std::mutex> lock(this->m_mutex);
-
- auto it = m_clients.find(appid);
- if (it == m_clients.end())
- {
- HMI_WARNING("cloudproxy-service", "Client with appid '%s' is not present in list", appid.c_str());
- // print app list
- for (const auto& i : m_clients)
- HMI_DEBUG("cloudproxy-service", "Client list: appid '%s' - '%s'", i.first.c_str(), i.second->appid.c_str());
-
- return false;
- }
-
- json_object* push_obj = json_object_new_object();
- json_object_object_add(push_obj, "type", json_object_new_string("receivedMessage"));
- json_object_object_add(push_obj, "data", json_object_new_string(data.c_str()));
- return (0 == afb_event_push(it->second->event, push_obj));
-}
-
-bool ClientManager::emitSendMessageConfirmation(const std::string& appid, bool result)
-{
- std::lock_guard<std::mutex> lock(this->m_mutex);
-
- auto it = m_clients.find(appid);
- if (it == m_clients.end())
- {
- HMI_WARNING("cloudproxy-service", "Client with appid '%s' is not present in list", appid.c_str());
- // print app list
- for (const auto& i : m_clients)
- HMI_DEBUG("cloudproxy-service", "Client list: appid '%s' - '%s'", i.first.c_str(), i.second->appid.c_str());
-
- return false;
- }
-
- json_object* push_obj = json_object_new_object();
- json_object_object_add(push_obj, "type", json_object_new_string("sendMessageConfirmation"));
- json_object_object_add(push_obj, "result", json_object_new_boolean(result));
- return (0 == afb_event_push(it->second->event, push_obj));
-}
+/* + * Copyright (C) 2020 MERA + * + * 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 "ClientManager.h" +#include "CloudType.h" + +#include <set> +#include <json-c/json.h> +#include <afb/afb-binding.h> // for AFB_* logger + +// static +void ClientManager::cbRemoveClientCtx(void *data) +{ + ClientManager::instance().removeClient((ClientManager::ClientCtx*)data); +} + + +// static +ClientManager& ClientManager::instance() +{ + static ClientManager instance; + return instance; +} + + +ClientManager::~ClientManager() +{ + std::lock_guard<std::mutex> lock(this->m_mutex); + for (auto cl : m_clients) + delete cl.second; +} + + +ClientManager::ClientCtx* ClientManager::addClient(afb_req_t req, const std::string& appid) +{ + ClientCtx* ctx = (ClientCtx*)afb_req_context_get(req); + if (!ctx) + { + AFB_NOTICE("create new session for %s", appid.c_str()); + ctx = new ClientCtx{appid, afb_api_make_event(req->api, appid.c_str())}; + afb_req_session_set_LOA(req, 1); + afb_req_context_set(req, ctx, cbRemoveClientCtx); + } + + m_clients[appid] = ctx; + return ctx; +} + + +void ClientManager::removeClient(ClientCtx* ctx) +{ + if(!ctx) + { + AFB_ERROR("data is nullptr"); + return; + } + + AFB_NOTICE("remove app %s", ctx->appid.c_str()); + std::lock_guard<std::mutex> lock(this->m_mutex); + + auto it = m_clients.find(ctx->appid); + if (it != m_clients.end()) + { + delete it->second; + m_clients.erase(it); + } +} + + +bool ClientManager::handleRequest(afb_req_t request, const std::string& verb, const std::string& appid) +{ + AFB_NOTICE("handleRequest: verb='%s', appid='%s'", verb.c_str(), appid.c_str()); + std::lock_guard<std::mutex> lock(this->m_mutex); + + if (appid.empty()) + { + AFB_ERROR("appid is empty"); + return false; + } + + auto client_it = m_clients.find(appid); + if (verb != "subscribe" && client_it == m_clients.end()) + { + AFB_NOTICE("client with appid '%s' is not registered", appid.c_str()); + return false; + } + + if (verb == "subscribe") + { + const char *value = afb_req_value(request, "event"); + if(!value) + { + AFB_ERROR("Can't subscribe: event name is not defined"); + return false; + } + std::string req_event{value}; + AFB_NOTICE("subscribe req: appid '%s', event '%s'", appid.c_str(), req_event.c_str()); + + if (!isSupportedEvent(req_event)) + { + AFB_ERROR("event '%s' is not supported", req_event.c_str()); + return false; + } + + ClientCtx* ctx = addClient(request, appid); + ctx->subs_events.insert(req_event); + if(!ctx->subscription) + { + if(afb_req_subscribe(request, ctx->event) == 0) + { + ctx->subscription = true; + } + else + { + AFB_ERROR("API error in afb_req_subscribe"); + return false; + } + } + + return true; + } + else if (verb == "unsubscribe") + { + const char *value = afb_req_value(request, "event"); + if(!value) + { + AFB_ERROR("Can't unsubscribe: event name is not defined"); + return false; + } + std::string req_event{value}; + AFB_NOTICE("unsubscribe req: appid '%s', event '%s'", appid.c_str(), req_event.c_str()); + + ClientCtx* ctx{client_it->second}; + ctx->subs_events.erase(req_event); + + if(ctx->subs_events.empty()) + { + if (afb_req_unsubscribe(request, ctx->event) != 0) + AFB_ERROR("API error in afb_req_unsubscribe"); + + ctx->subscription = false; + } + + return true; + } + + AFB_NOTICE("Unsupported verb '%s'", verb.c_str()); + return false; +} + + +bool ClientManager::isSupportedEvent(const std::string& event) +{ + const std::set<std::string> event_list{ + "sendMessageConfirmation", + "receivedMessage" + }; + + return (event_list.end() != event_list.find(event)); +} + + +bool ClientManager::emitReceivedMessage(const std::string& appid, const std::string& cloud_type, const std::string& data) +{ + std::lock_guard<std::mutex> lock(this->m_mutex); + + auto it = m_clients.find(appid); + if (it == m_clients.end()) + { + AFB_WARNING("Client with appid '%s' is not present in list", appid.c_str()); + // print app list + for (const auto& i : m_clients) + AFB_DEBUG("Client list: appid '%s' - '%s'", i.first.c_str(), i.second->appid.c_str()); + + return false; + } + + json_object* push_obj = json_object_new_object(); + json_object_object_add(push_obj, "type", json_object_new_string("receivedMessage")); + json_object_object_add(push_obj, "cloud_type", json_object_new_string(cloud_type.c_str())); + json_object_object_add(push_obj, "data", json_object_new_string(data.c_str())); + return (0 == afb_event_push(it->second->event, push_obj)); +} + +bool ClientManager::emitSendMessageConfirmation(const std::string& appid, const std::string& cloud_type, bool result) +{ + std::lock_guard<std::mutex> lock(this->m_mutex); + + auto it = m_clients.find(appid); + if (it == m_clients.end()) + { + AFB_WARNING("Client with appid '%s' is not present in list", appid.c_str()); + // print app list + for (const auto& i : m_clients) + AFB_DEBUG("Client list: appid '%s' - '%s'", i.first.c_str(), i.second->appid.c_str()); + + return false; + } + + json_object* push_obj = json_object_new_object(); + json_object_object_add(push_obj, "type", json_object_new_string("sendMessageConfirmation")); + json_object_object_add(push_obj, "cloud_type", json_object_new_string(cloud_type.c_str())); + json_object_object_add(push_obj, "result", json_object_new_boolean(result)); + return (0 == afb_event_push(it->second->event, push_obj)); +} diff --git a/src/ClientManager.h b/src/ClientManager.h index a1038e1..d6348d1 100755 --- a/src/ClientManager.h +++ b/src/ClientManager.h @@ -1,59 +1,59 @@ -/*
- * Copyright (C) 2020 MERA
- *
- * 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.
- */
-
-#pragma once
-
-#include <afb/afb-binding.h>
-
-#include <map>
-#include <unordered_set>
-#include <mutex>
-#include <string>
-
-class ClientManager
-{
-public:
- struct ClientCtx
- {
- std::string appid;
- afb_event_t event;
- std::unordered_set<std::string> subs_events;
- bool subscription{false};
- };
-
- ClientManager() = default;
- ~ClientManager();
- ClientManager(const ClientManager&) = delete;
- void operator=(const ClientManager&) = delete;
-
- bool handleRequest(afb_req_t request, const std::string& verb, const std::string& appid);
- bool emitReceivedMessage(const std::string& appid, const std::string& data);
- bool emitSendMessageConfirmation(const std::string& appid, bool result);
-
- static ClientManager& instance();
-
- // don't use it directly
- static void cbRemoveClientCtx(void *data);
-
-protected:
- ClientCtx* addClient(afb_req_t req, const std::string& appid);
- void removeClient(ClientCtx* ctx);
- bool isSupportedEvent(const std::string& event);
-
-protected:
- std::mutex m_mutex;
- std::map<std::string, ClientCtx*> m_clients;
-};
+/* + * Copyright (C) 2020 MERA + * + * 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. + */ + +#pragma once + +#include <afb/afb-binding.h> + +#include <map> +#include <unordered_set> +#include <mutex> +#include <string> + +class ClientManager +{ +public: + struct ClientCtx + { + std::string appid; + afb_event_t event; + std::unordered_set<std::string> subs_events{}; + bool subscription{false}; + }; + + ClientManager() = default; + ~ClientManager(); + ClientManager(const ClientManager&) = delete; + void operator=(const ClientManager&) = delete; + + bool handleRequest(afb_req_t request, const std::string& verb, const std::string& appid); + bool emitReceivedMessage(const std::string& appid, const std::string& cloud_type, const std::string& data); + bool emitSendMessageConfirmation(const std::string& appid, const std::string& cloud_type, bool result); + + static ClientManager& instance(); + + // don't use it directly + static void cbRemoveClientCtx(void *data); + +protected: + ClientCtx* addClient(afb_req_t req, const std::string& appid); + void removeClient(ClientCtx* ctx); + bool isSupportedEvent(const std::string& event); + +protected: + std::mutex m_mutex; + std::map<std::string, ClientCtx*> m_clients; +}; diff --git a/app_client_examples/telemetry-cloud-app/app/mainwindow.h b/src/CloudClient.h index ce21845..f59906f 100755..100644 --- a/app_client_examples/telemetry-cloud-app/app/mainwindow.h +++ b/src/CloudClient.h @@ -14,39 +14,24 @@ * limitations under the License. */ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H +#pragma once -#include <QMainWindow> -#include <QVariant> -#include <QScopedPointer> -#include <memory> +#include <string> -#include "TelemetryWorker.h" +typedef struct _GKeyFile GKeyFile; - -namespace Ui { - class MainWindow; -} - -class MainWindow : public QMainWindow +class CloudClient { - Q_OBJECT - public: - explicit MainWindow(double scale_factor, QWidget *parent = 0); - ~MainWindow(); + CloudClient() = default; + virtual ~CloudClient() = default; + CloudClient(const CloudClient&) = delete; + CloudClient& operator= (const CloudClient&) = delete; -public slots: - void updateState(QVariant recvMsg); + virtual bool sendMessage(const std::string& appid, const std::string& data) = 0; + virtual bool createConnection() = 0; -private slots: - void startMsgButtonClick(); - -private: - Ui::MainWindow *ui; - bool started{false}; - QScopedPointer<TelemetryWorker> worker; + virtual bool enabled() const = 0; + virtual bool connected() const = 0; + virtual bool loadConf(GKeyFile* conf_file) = 0; }; - -#endif // MAINWINDOW_H diff --git a/src/CloudType.h b/src/CloudType.h new file mode 100644 index 0000000..fd4f801 --- /dev/null +++ b/src/CloudType.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020 MERA + * + * 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. + */ + +#pragma once + +#include <string> + +struct CloudType +{ + static constexpr const char* Azure{"azure"}; + static constexpr const char* Aws {"aws"}; + + inline static bool isSupported(const std::string& type) + { + return (type == Azure || type == Aws); + } +}; diff --git a/src/cloudproxy-bindings.cpp b/src/cloudproxy-bindings.cpp index 1a40730..c69e9c7 100755 --- a/src/cloudproxy-bindings.cpp +++ b/src/cloudproxy-bindings.cpp @@ -20,37 +20,20 @@ #include <memory> #include <algorithm> -#include <iothub.h> -#include <iothub_device_client.h> -#include <iothub_client_options.h> -#include <iothub_message.h> -#include <iothubtransportmqtt.h> -#include <azure_c_shared_utility/threadapi.h> // ThreadAPI_Sleep() -#include <azure_c_shared_utility/tickcounter.h> // tickcounter_ms_t - -#include "hmi-debug.h" #include "utils.h" #include "ClientManager.h" +#include "CloudType.h" +#include "AwsClient.h" +#include "AzureClient.h" #include <glib.h> -static const char* API_name{"cloudproxy"}; - -static std::string g_connectionString; -static IOTHUB_DEVICE_CLIENT_HANDLE g_device_handle{nullptr}; -static bool g_iot_inited{false}; - - +namespace +{ -static utils::scope_exit g_destroy([]{ - if (g_iot_inited) - { - if (g_device_handle) - IoTHubDeviceClient_Destroy(g_device_handle); +std::map<std::string, std::unique_ptr<CloudClient>> g_clouds; - IoTHub_Deinit(); - } -}); +} static bool loadConf() @@ -60,7 +43,7 @@ static bool loadConf() const char *p = getenv("AFM_APP_INSTALL_DIR"); if(!p) { - HMI_ERROR("cloudproxy-service", "AFM_APP_INSTALL_DIR is not set, try to find conf in %s", DEFAULT_ETC_PATH); + AFB_ERROR("AFM_APP_INSTALL_DIR is not set, try to find conf in %s", DEFAULT_ETC_PATH); p = DEFAULT_ETC_PATH; } @@ -70,273 +53,127 @@ static bool loadConf() g_autoptr(GKeyFile) conf_file = g_key_file_new(); g_autoptr(GError) error = nullptr; + AFB_DEBUG("Load config file: %s", conf_path.c_str()); if (!conf_file || !g_key_file_load_from_file(conf_file, conf_path.c_str(), G_KEY_FILE_NONE, &error)) { - HMI_ERROR("cloudproxy-service", "can't load file %s", conf_path.c_str()); - return false; - } - - g_autofree gchar *value = g_key_file_get_string(conf_file, "AzureCloudConnection", "DeviceConnectionString", &error); - if (value == nullptr) - { - HMI_ERROR("cloudproxy-service", "can't read DeviceConnectionString from config %d", conf_path.c_str()); - return false; - } - - g_connectionString = value; - if (g_connectionString.empty()) - { - HMI_ERROR("cloudproxy-service", "DeviceConnectionString is empty"); + AFB_ERROR("Can't load file %s", conf_path.c_str()); return false; } - return true; -} - - -//-------------- Iot callbacks -static void connection_status_callback(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* user_context) -{ - HMI_NOTICE("cloudproxy-service", "%s called: result %d, reason %d", __FUNCTION__, result, reason); - - (void)reason; - (void)user_context; - // This sample DOES NOT take into consideration network outages. - if (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED && reason == IOTHUB_CLIENT_CONNECTION_OK) - { - HMI_NOTICE("cloudproxy-service", "The device client is connected to iothub"); - } - else - { - HMI_NOTICE("cloudproxy-service", "The device client has been disconnected"); - } -} - -static IOTHUBMESSAGE_DISPOSITION_RESULT receive_msg_callback(IOTHUB_MESSAGE_HANDLE message, void* user_context) -{ - HMI_NOTICE("cloudproxy-service", "%s called", __FUNCTION__); - (void)user_context; - const char* messageId; - const char* correlationId; - - IOTHUBMESSAGE_CONTENT_TYPE content_type = IoTHubMessage_GetContentType(message); - - if (content_type == IOTHUBMESSAGE_BYTEARRAY) + //-- Azure parameters: { - const unsigned char* buff_msg; - size_t buff_len; + std::unique_ptr<CloudClient> azure{new AzureClient}; + if (!azure->loadConf(conf_file)) + return false; - if (IoTHubMessage_GetByteArray(message, &buff_msg, &buff_len) != IOTHUB_MESSAGE_OK) - { - HMI_ERROR("cloudproxy-service", "Failure retrieving byte array message"); - } - else - { - HMI_NOTICE("cloudproxy-service", "Received Binary message, size %d, data '%.*s'", (int)buff_len, (int)buff_len, buff_msg); - } - - const char* app_id = IoTHubMessage_GetProperty(message, "application_id"); - HMI_NOTICE("cloudproxy-service", "Received property 'application_id': %s", (app_id ? app_id : "<unavailable>")); - - if (app_id && app_id[0]) - ClientManager::instance().emitReceivedMessage(app_id, std::string((const char*)buff_msg, buff_len)); - else - HMI_ERROR("cloudproxy-service", "Can't emit SendMessageConfirmation: appid is not valid"); - } - else if (content_type == IOTHUBMESSAGE_STRING) - { - const char* string_msg = IoTHubMessage_GetString(message); - if (string_msg == nullptr) - { - HMI_NOTICE("cloudproxy-service", "Failure retrieving String message"); - } - else - { - HMI_NOTICE("cloudproxy-service", "Received String message, size %d, data '%s'", strlen(string_msg), string_msg); - } - - const char* app_id = IoTHubMessage_GetProperty(message, "application_id"); - HMI_NOTICE("cloudproxy-service", "Received property 'application_id': %s", (app_id ? app_id : "<unavailable>")); - - if (app_id && app_id[0]) - ClientManager::instance().emitReceivedMessage(app_id, string_msg); + if (azure->enabled()) + g_clouds[CloudType::Azure] = std::move(azure); else - HMI_ERROR("cloudproxy-service", "Can't emit SendMessageConfirmation: appid is not valid"); - } - else - { - HMI_ERROR("cloudproxy-service", "Unsupported message content type"); - } - - return IOTHUBMESSAGE_ACCEPTED; -} - - -static int device_method_callback(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* resp_size, void* userContextCallback) -{ - HMI_NOTICE("cloudproxy-service", "%s called, method_name %s", __FUNCTION__, method_name); - - const char* device_id = (const char*)userContextCallback; - char* end = nullptr; - int newInterval; - - int status = 501; - const char* RESPONSE_STRING = "{ \"Response\": \"Unknown method requested.\" }"; - - HMI_NOTICE("cloudproxy-service", "Device Method called for device %s", device_id); - HMI_NOTICE("cloudproxy-service", "Device Method name: %s", method_name); - HMI_NOTICE("cloudproxy-service", "Device Method payload: %.*s", (int)size, (const char*)payload); - - HMI_NOTICE("cloudproxy-service", "Response status: %d", status); - HMI_NOTICE("cloudproxy-service", "Response payload: %s", RESPONSE_STRING); - - *resp_size = strlen(RESPONSE_STRING); - if ((*response = (unsigned char*)malloc(*resp_size)) == NULL) - { - status = -1; - } - else - { - memcpy(*response, RESPONSE_STRING, *resp_size); + g_clouds[CloudType::Azure].reset(); } - return status; -} - - -static void send_confirm_callback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) -{ - HMI_NOTICE("cloudproxy-service", "%s called, result %d", __FUNCTION__, result); - (void)userContextCallback; - // When a message is sent this callback will get invoked - - HMI_NOTICE("cloudproxy-service", "Confirmation callback result %s", MU_ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); - - const char* appid = (const char*)userContextCallback; - if (!appid || !appid[0]) + //-- AWS parameters: { - HMI_ERROR("cloudproxy-service", "Confirmation callback: appid is not set"); - - if (userContextCallback) - free(userContextCallback); + std::unique_ptr<CloudClient> aws{new AwsClient}; + if (!aws->loadConf(conf_file)) + return false; - return; - } - - ClientManager::instance().emitSendMessageConfirmation(appid, result == IOTHUB_CLIENT_CONFIRMATION_OK); - free(userContextCallback); -} -//-------------- - -//-------------- help functions -static bool createConnection() -{ - HMI_NOTICE("cloudproxy-service", "%s called", __FUNCTION__); - - if (g_device_handle) - { - HMI_WARNING("cloudproxy-service", "connection already created"); - return true; + if (aws->enabled()) + g_clouds[CloudType::Aws] = std::move(aws); + else + g_clouds[CloudType::Aws].reset(); } - g_device_handle = IoTHubDeviceClient_CreateFromConnectionString(g_connectionString.c_str(), MQTT_Protocol); - if (!g_device_handle) + if (!std::any_of(g_clouds.begin(), g_clouds.end(), [](const auto& c){ return !!c.second; })) { - HMI_ERROR("cloudproxy-service", "Failure creating IoTHubDeviceClient device"); + AFB_ERROR("All cloud connection types are disabled by configuration"); return false; } - bool traceOn = false; - IoTHubDeviceClient_SetOption(g_device_handle, OPTION_LOG_TRACE, &traceOn); - IoTHubDeviceClient_SetConnectionStatusCallback(g_device_handle, connection_status_callback, nullptr); - IoTHubDeviceClient_SetMessageCallback(g_device_handle, receive_msg_callback, nullptr); - IoTHubDeviceClient_SetDeviceMethodCallback(g_device_handle, device_method_callback, nullptr); - - tickcounter_ms_t ms_delay = 10; - IoTHubDeviceClient_SetOption(g_device_handle, OPTION_DO_WORK_FREQUENCY_IN_MS, &ms_delay); // DoWork multithread - return true; } -//-------------- - static void pingSample(afb_req_t request) { static int pingcount = 0; afb_req_success_f(request, json_object_new_int(pingcount), "Ping count = %d", pingcount); - HMI_NOTICE("cloudproxy-service", "Verbosity macro at level notice invoked at ping invocation count = %d", pingcount); + AFB_NOTICE("Verbosity macro at level notice invoked at ping invocation count = %d", pingcount); pingcount++; } -static bool initAzureSdk() -{ - //Allow program to try to establish connection several times - if (!g_iot_inited) - { - if(IoTHub_Init()) - { - HMI_ERROR("cloudproxy-service","Azure IoTHub_Init() failed"); - } - else - { - g_iot_inited = true; - } - } - - return g_iot_inited; -} - static void sendMessage(afb_req_t request) { - HMI_NOTICE("cloudproxy-service", "%s called", __FUNCTION__); + AFB_NOTICE("%s called", __FUNCTION__); json_object* object = afb_req_json(request); if (!object) { - HMI_ERROR("cloudproxy-service", "Can't parse request"); + AFB_ERROR("Can't parse request"); afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); return; } const std::string appid{utils::get_application_id(request)}; - std::string data; - json_object *obj_data; - if(!json_object_object_get_ex(object, "data", &obj_data)) + if (appid.empty()) { - HMI_ERROR("cloudproxy-service", "can't obtain application_id or data from request"); + AFB_ERROR("can't obtain application_id from request"); + afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); return; } - data = json_object_get_string(obj_data); - if (!g_device_handle && !createConnection()) + auto read_string = [object](const char* tag, std::string& read_to) + { + json_object* jobj{nullptr}; + if (!json_object_object_get_ex(object, tag, &jobj)) + { + AFB_ERROR("can't obtain %s from request", tag); + return false; + } + read_to = json_object_get_string(jobj); + return true; + }; + + std::string cloud_type; + if (!read_string("cloud_type", cloud_type)) { - HMI_ERROR("cloudproxy-service", "Can't create connection to cloud"); afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); return; } - IOTHUB_MESSAGE_HANDLE message_handle = IoTHubMessage_CreateFromString(data.c_str()); + std::string data; + if (!read_string("data", data)) + { + afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); + return; + } - utils::scope_exit message_handle_destroy([&message_handle](){ - // The message is copied to the sdk, so the we can destroy it - if (message_handle) - IoTHubMessage_Destroy(message_handle); - }); + if (!CloudType::isSupported(cloud_type)) + { + AFB_ERROR("Unsupported cloud type is requested: '%s'", cloud_type.c_str()); + afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); + return; + } - if (!message_handle) + if (!g_clouds[cloud_type]) { - HMI_ERROR("cloudproxy-service", "Can't create IoTHubMessage message"); + AFB_ERROR("%s cloud connection is disabled by config", cloud_type.c_str()); afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); return; } - IoTHubMessage_SetProperty(message_handle, "application_id", appid.c_str()); + CloudClient* client{g_clouds[cloud_type.c_str()].get()}; + if (!client->connected() && !client->createConnection()) + { + AFB_ERROR("Can't create connection to %s cloud", cloud_type.c_str()); + afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); + return; + } - if (IoTHubDeviceClient_SendEventAsync(g_device_handle, message_handle, send_confirm_callback, strdup(appid.c_str()))) + if (!client->sendMessage(appid, data)) { - HMI_ERROR("cloudproxy-service", "Can't send IoTHubMessage message"); + AFB_ERROR("Can't send message to %s cloud", cloud_type.c_str()); afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); return; } @@ -347,19 +184,19 @@ static void sendMessage(afb_req_t request) static void subscribe(afb_req_t request) { - HMI_NOTICE("cloudproxy-service", "%s called", __FUNCTION__); + AFB_NOTICE("%s called", __FUNCTION__); std::string req_appid{utils::get_application_id(request)}; if(req_appid.empty()) { - HMI_ERROR("cloudproxy-service", "Can't subscribe: empty appid"); + AFB_ERROR("Can't subscribe: empty appid"); afb_req_fail_f(request, "%s failed: application_id is not defined in request", __FUNCTION__); return; } if (!ClientManager::instance().handleRequest(request, __FUNCTION__, req_appid)) { - HMI_ERROR("cloudproxy-service", "%s failed in handleRequest", __FUNCTION__); + AFB_ERROR("%s failed in handleRequest", __FUNCTION__); afb_req_fail_f(request, "%s failed", __FUNCTION__); } else @@ -370,19 +207,19 @@ static void subscribe(afb_req_t request) static void unsubscribe(afb_req_t request) { - HMI_NOTICE("cloudproxy-service", "%s called", __FUNCTION__); + AFB_NOTICE("%s called", __FUNCTION__); std::string req_appid{utils::get_application_id(request)}; if(req_appid.empty()) { - HMI_ERROR("cloudproxy-service", "Can't unsubscribe: empty appid"); + AFB_ERROR("Can't unsubscribe: empty appid"); afb_req_fail_f(request, "%s failed: application_id is not defined in request", __FUNCTION__); return; } if (!ClientManager::instance().handleRequest(request, __FUNCTION__, req_appid)) { - HMI_ERROR("cloudproxy-service", "%s failedin handleRequest", __FUNCTION__); + AFB_ERROR("%s failedin handleRequest", __FUNCTION__); afb_req_fail_f(request, "%s failed", __FUNCTION__); } else @@ -406,17 +243,11 @@ static const afb_verb_t verbs[]= { static int preinit(afb_api_t api) { - HMI_NOTICE("cloudproxy-service", "binding preinit (was register)"); + AFB_NOTICE("binding preinit (was register)"); if (!loadConf()) { - HMI_ERROR("cloudproxy-service", "Can't load configuration file or configuration is wrong"); - return -1; - } - - if (!initAzureSdk()) - { - HMI_ERROR("cloudproxy-service", "Can't initialize Azure SDK"); + AFB_ERROR("Can't load configuration file or configuration is wrong"); return -1; } @@ -426,8 +257,8 @@ static int preinit(afb_api_t api) static int init(afb_api_t api) { - HMI_NOTICE("cloudproxy-service","binding init"); - return (g_iot_inited ? 0 : -1); + AFB_NOTICE("binding init"); + return 0; } const afb_binding_t afbBindingExport = { diff --git a/src/export.map b/src/export.map deleted file mode 100755 index f3961c0..0000000 --- a/src/export.map +++ /dev/null @@ -1 +0,0 @@ -{ global: afbBindingV*; local: *; };
\ No newline at end of file diff --git a/src/hmi-debug.h b/src/hmi-debug.h deleted file mode 100755 index c8e8638..0000000 --- a/src/hmi-debug.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2017 TOYOTA MOTOR CORPORATION - * - * 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 __HMI_DEBUG_H__ -#define __HMI_DEBUG_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#include <time.h> -#include <stdio.h> -#include <stdarg.h> -#include <string.h> -#include <stdlib.h> - -#include <sys/types.h> // gettid() -#include <unistd.h> -#include <sys/syscall.h> - - enum LOG_LEVEL{ - LOG_LEVEL_NONE = 0, - LOG_LEVEL_ERROR, - LOG_LEVEL_WARNING, - LOG_LEVEL_NOTICE, - LOG_LEVEL_INFO, - LOG_LEVEL_DEBUG, - LOG_LEVEL_MAX = LOG_LEVEL_DEBUG - }; - -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) - -#define HMI_ERROR(prefix, args,...) _HMI_LOG(LOG_LEVEL_ERROR, __FILENAME__, __FUNCTION__, __LINE__, prefix, args, ##__VA_ARGS__) -#define HMI_WARNING(prefix, args,...) _HMI_LOG(LOG_LEVEL_WARNING, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) -#define HMI_NOTICE(prefix, args,...) _HMI_LOG(LOG_LEVEL_NOTICE, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) -#define HMI_INFO(prefix, args,...) _HMI_LOG(LOG_LEVEL_INFO, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) -#define HMI_DEBUG(prefix, args,...) _HMI_LOG(LOG_LEVEL_DEBUG, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__) - - static char ERROR_FLAG[6][20] = {"NONE", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"}; - - static void _HMI_LOG(enum LOG_LEVEL level, const char* file, const char* func, const int line, const char* prefix, const char* log, ...) - { - //SA: WARN: const int log_level = (getenv("USE_HMI_DEBUG") == NULL)?LOG_LEVEL_ERROR:atoi(getenv("USE_HMI_DEBUG")); - const int log_level = LOG_LEVEL_MAX; - if(log_level < level) - { - return; - } - - char *message; - struct timespec tp; - unsigned int time; - - clock_gettime(CLOCK_REALTIME, &tp); - time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000); - - va_list args; - va_start(args, log); - if (log == NULL || vasprintf(&message, log, args) < 0) - message = NULL; - fprintf(stderr, "[%10.3f] [%s %s] [%s, %s(), Line:%d] [thread %llx]>>> %s \n", time / 1000.0, prefix, ERROR_FLAG[level], file, func, line, (unsigned long long)syscall(__NR_gettid), message); - va_end(args); - free(message); - } - -#ifdef __cplusplus -} -#endif -#endif //__HMI_DEBUG_H__ diff --git a/src/utils.h b/src/utils.h index 9069c4a..fa28b09 100755 --- a/src/utils.h +++ b/src/utils.h @@ -1,54 +1,54 @@ -/*
- * Copyright (C) 2020 MERA
- *
- * 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.
- */
-
-#pragma once
-
-#include <functional>
-#include <string>
-
-#include <afb/afb-binding.h>
-
-namespace utils
-{
- class scope_exit
- {
- public:
- explicit scope_exit(std::function<void()> func)
- : m_f(func)
- {}
- ~scope_exit()
- {
- if (!!m_f)
- m_f();
- }
-
- private:
- std::function<void()> m_f;
- };
-
- std::string get_application_id(const afb_req_t request)
- {
- char *app_id = afb_req_get_application_id(request);
- std::string appid;
- if(app_id)
- {
- appid = std::string(app_id);
- free(app_id);
- }
-
- return appid;
- }
-}
+/* + * Copyright (C) 2020 MERA + * + * 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. + */ + +#pragma once + +#include <functional> +#include <string> + +#include <afb/afb-binding.h> + +namespace utils +{ + class scope_exit + { + public: + explicit scope_exit(std::function<void()> func) + : m_f(func) + {} + ~scope_exit() + { + if (!!m_f) + m_f(); + } + + private: + std::function<void()> m_f; + }; + + inline std::string get_application_id(const afb_req_t request) + { + char *app_id = afb_req_get_application_id(request); + std::string appid; + if(app_id) + { + appid = std::string(app_id); + free(app_id); + } + + return appid; + } +} |