diff options
author | maratsabitov <marat.sabitov@mera.com> | 2020-05-08 18:03:57 +0300 |
---|---|---|
committer | Marat Sabitov <marat.sabitov@mera.com> | 2020-07-09 09:38:01 +0300 |
commit | 0cecffd4565b52bd8d200f6bc9f4144f244a6515 (patch) | |
tree | c0969bb192bb711c0bd2e2444a66c0060b2e13af /src | |
parent | f496bfe75ef0a1ec2658d3fec51866d4c7551282 (diff) |
Introduce cloudproxy servicejellyfish_9.99.4jellyfish_9.99.3jellyfish_9.99.2jellyfish/9.99.4jellyfish/9.99.3jellyfish/9.99.29.99.49.99.39.99.2
Cloudproxy service allows applications to communicate with clouds.
The commit includes the test applications as sample for real client imlementation.
Bug-AGL: SPEC-3370
Change-Id: I82d8122e93d01451f4471cc20c706e75c16f1c29
Signed-off-by: maratsabitov <marat.sabitov@mera.com>
Diffstat (limited to 'src')
-rwxr-xr-x | src/CMakeLists.txt | 77 | ||||
-rwxr-xr-x | src/ClientManager.cpp | 215 | ||||
-rwxr-xr-x | src/ClientManager.h | 59 | ||||
-rwxr-xr-x | src/cloudproxy-bindings.cpp | 441 | ||||
-rw-r--r-- | src/config_sample.ini | 3 | ||||
-rwxr-xr-x | src/export.map | 1 | ||||
-rwxr-xr-x | src/hmi-debug.h | 82 | ||||
-rwxr-xr-x | src/utils.h | 54 |
8 files changed, 932 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100755 index 0000000..fc397f1 --- /dev/null +++ b/src/CMakeLists.txt @@ -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. +########################################################################### + +cmake_minimum_required(VERSION 2.8) + +set(TARGETS_ARBINDER cloudproxy-service) + +INCLUDE(FindThreads) +FIND_PACKAGE(Threads) + +pkg_check_modules(cp_binding_depends afb-daemon glib-2.0 gio-2.0 gio-unix-2.0 json-c) +set(binding_cp_sources + cloudproxy-bindings.cpp + utils.h + hmi-debug.h + ClientManager.h + ClientManager.cpp +) + +link_libraries(-Wl,--as-needed -Wl,--gc-sections -Wl,--no-undefined) + +set(AZURE_DEP_LIBS iothub_client) + +add_library(${TARGETS_ARBINDER} MODULE ${binding_cp_sources}) + +include_directories(${CMAKE_SYSROOT}/usr/include/azureiot) + +target_compile_options(${TARGETS_ARBINDER} PRIVATE ${cp_binding_depends_CFLAGS}) +if(DEFINED DEBUGMODE) + target_compile_options(${TARGETS_ARBINDER} PRIVATE -g -O0) +else(DEFINED DEBUGMODE) + target_compile_options(${TARGETS_ARBINDER} PRIVATE -g -O2) +endif(DEFINED DEBUGMODE) + +target_link_libraries(${TARGETS_ARBINDER} ${CMAKE_THREAD_LIBS_INIT} ${link_libraries} ${cp_binding_depends_LIBRARIES} ${AZURE_DEP_LIBS}) + + +set_target_properties(${TARGETS_ARBINDER} PROPERTIES + PREFIX "" + LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/export.map" + ) + +target_compile_definitions(${TARGETS_ARBINDER} + PRIVATE + AFB_BINDING_VERSION=3 + _GNU_SOURCE) + + +if(NOT EXISTS ${PROJECT_BINARY_DIR}/package) + add_custom_command(TARGET ${TARGETS_ARBINDER} POST_BUILD + COMMAND cp -rf ${PROJECT_SOURCE_DIR}/package ${PROJECT_BINARY_DIR} + ) +endif() + +add_custom_command(TARGET ${TARGETS_ARBINDER} POST_BUILD + COMMAND mkdir -p ${PROJECT_BINARY_DIR}/package/root/lib + COMMAND cp -rf ${PROJECT_BINARY_DIR}/src/${TARGETS_ARBINDER}.so ${PROJECT_BINARY_DIR}/package/root/lib + COMMAND mkdir -p ${PROJECT_BINARY_DIR}/package/root/etc + COMMAND cp -f ${PROJECT_SOURCE_DIR}/src/config_sample.ini ${PROJECT_BINARY_DIR}/package/root/etc/config.ini +) + +add_custom_target(widget DEPENDS ${PROJECT_BINARY_DIR}/package/root + COMMAND wgtpkg-pack -f -o ${PROJECT_BINARY_DIR}/${TARGETS_ARBINDER}.wgt ${PROJECT_BINARY_DIR}/package/root +) diff --git a/src/ClientManager.cpp b/src/ClientManager.cpp new file mode 100755 index 0000000..74d672d --- /dev/null +++ b/src/ClientManager.cpp @@ -0,0 +1,215 @@ +/*
+ * 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));
+}
diff --git a/src/ClientManager.h b/src/ClientManager.h new file mode 100755 index 0000000..a1038e1 --- /dev/null +++ b/src/ClientManager.h @@ -0,0 +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;
+};
diff --git a/src/cloudproxy-bindings.cpp b/src/cloudproxy-bindings.cpp new file mode 100755 index 0000000..1a40730 --- /dev/null +++ b/src/cloudproxy-bindings.cpp @@ -0,0 +1,441 @@ +/* + * 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 <afb/afb-binding.h> +#include <json-c/json.h> + +#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 <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}; + + + +static utils::scope_exit g_destroy([]{ + if (g_iot_inited) + { + if (g_device_handle) + IoTHubDeviceClient_Destroy(g_device_handle); + + IoTHub_Deinit(); + } +}); + + +static bool loadConf() +{ + const char* CONF_FILE_PATH{"/etc/config.ini"}; + const char* DEFAULT_ETC_PATH{"/etc/cloudproxy-service"}; + 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); + p = DEFAULT_ETC_PATH; + } + + std::string conf_path = p; + conf_path += CONF_FILE_PATH; + + g_autoptr(GKeyFile) conf_file = g_key_file_new(); + g_autoptr(GError) error = nullptr; + + 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"); + 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) + { + const unsigned char* buff_msg; + size_t buff_len; + + 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); + 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); + } + + 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]) + { + HMI_ERROR("cloudproxy-service", "Confirmation callback: appid is not set"); + + if (userContextCallback) + free(userContextCallback); + + 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; + } + + g_device_handle = IoTHubDeviceClient_CreateFromConnectionString(g_connectionString.c_str(), MQTT_Protocol); + if (!g_device_handle) + { + HMI_ERROR("cloudproxy-service", "Failure creating IoTHubDeviceClient device"); + 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); + 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__); + + json_object* object = afb_req_json(request); + if (!object) + { + HMI_ERROR("cloudproxy-service", "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)) + { + HMI_ERROR("cloudproxy-service", "can't obtain application_id or data from request"); + return; + } + data = json_object_get_string(obj_data); + + if (!g_device_handle && !createConnection()) + { + 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()); + + 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) + { + HMI_ERROR("cloudproxy-service", "Can't create IoTHubMessage message"); + afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); + return; + } + + IoTHubMessage_SetProperty(message_handle, "application_id", appid.c_str()); + + if (IoTHubDeviceClient_SendEventAsync(g_device_handle, message_handle, send_confirm_callback, strdup(appid.c_str()))) + { + HMI_ERROR("cloudproxy-service", "Can't send IoTHubMessage message"); + afb_req_fail_f(request, "failed", "called %s", __FUNCTION__); + return; + } + + afb_req_success(request, json_object_new_object(), __FUNCTION__); +} + + +static void subscribe(afb_req_t request) +{ + HMI_NOTICE("cloudproxy-service", "%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_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_req_fail_f(request, "%s failed", __FUNCTION__); + } + else + { + afb_req_success(request, json_object_new_object(), __FUNCTION__); + } +} + +static void unsubscribe(afb_req_t request) +{ + HMI_NOTICE("cloudproxy-service", "%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_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_req_fail_f(request, "%s failed", __FUNCTION__); + } + else + { + afb_req_success(request, json_object_new_object(), __FUNCTION__); + } +} + +/* + * array of the verbs exported to afb-daemon + */ +static const afb_verb_t verbs[]= { + /* VERB'S NAME FUNCTION TO CALL */ + { .verb="ping", .callback=pingSample }, + { .verb="sendMessage", .callback=sendMessage }, + { .verb="subscribe", .callback=subscribe }, + { .verb="unsubscribe", .callback=unsubscribe }, + {nullptr } /* marker for end of the array */ +}; + + +static int preinit(afb_api_t api) +{ + HMI_NOTICE("cloudproxy-service", "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"); + return -1; + } + + return 0; +} + + +static int init(afb_api_t api) +{ + HMI_NOTICE("cloudproxy-service","binding init"); + return (g_iot_inited ? 0 : -1); +} + +const afb_binding_t afbBindingExport = { + .api = "cloudproxy", + .specification = nullptr, + .info = nullptr, + .verbs = verbs, + .preinit = preinit, + .init = init, + .onevent = nullptr +}; diff --git a/src/config_sample.ini b/src/config_sample.ini new file mode 100644 index 0000000..fa97cd7 --- /dev/null +++ b/src/config_sample.ini @@ -0,0 +1,3 @@ +[AzureCloudConnection] +DeviceConnectionString=My=Azure=Device=Connection=String + diff --git a/src/export.map b/src/export.map new file mode 100755 index 0000000..f3961c0 --- /dev/null +++ b/src/export.map @@ -0,0 +1 @@ +{ global: afbBindingV*; local: *; };
\ No newline at end of file diff --git a/src/hmi-debug.h b/src/hmi-debug.h new file mode 100755 index 0000000..c8e8638 --- /dev/null +++ b/src/hmi-debug.h @@ -0,0 +1,82 @@ +/* + * 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 new file mode 100755 index 0000000..9069c4a --- /dev/null +++ b/src/utils.h @@ -0,0 +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;
+ }
+}
|