summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Kolodziejski <m.kolodziejs@samsung.com>2018-09-18 16:19:40 +0200
committerMichal Kolodziejski <m.kolodziejs@samsung.com>2018-09-20 10:13:37 +0200
commit0943999701f10c2952f11f90542f5f7ae1e5daca (patch)
tree91f475c600808502ef88184e9262e9f6f6709ab7
parent7b42a10921915e2fc058bbb3588f67124eb6edfe (diff)
downloadgear-racing-controller-0943999701f10c2952f11f90542f5f7ae1e5daca.tar.gz
gear-racing-controller-0943999701f10c2952f11f90542f5f7ae1e5daca.tar.bz2
gear-racing-controller-0943999701f10c2952f11f90542f5f7ae1e5daca.zip
Cloud async API implementation
Change-Id: Ibcc6c65a0a273e92c20eb0d9bf713d009fdb54c3 Signed-off-by: Michal Kolodziejski <m.kolodziejs@samsung.com>
-rw-r--r--inc/cloud/car_info.h6
-rw-r--r--inc/cloud/cloud_request.h47
-rw-r--r--src/cloud/car_info.c20
-rw-r--r--src/cloud/car_info_serializer.c19
-rw-r--r--src/cloud/cloud_request.c245
5 files changed, 319 insertions, 18 deletions
diff --git a/inc/cloud/car_info.h b/inc/cloud/car_info.h
index 2daeb0c..da95734 100644
--- a/inc/cloud/car_info.h
+++ b/inc/cloud/car_info.h
@@ -31,6 +31,12 @@ typedef struct car_info car_info_t;
car_info_t *car_info_create();
/**
+ * @brief Crates deep copy of given car_info_t object.
+ * @return Copied car_info_t object, or NULL on error.
+ */
+car_info_t *car_info_copy(const car_info_t *car_info);
+
+/**
* @brief Releases car_info_t.
* @param[in] car_info Car info struct.
*/
diff --git a/inc/cloud/cloud_request.h b/inc/cloud/cloud_request.h
index f266d04..8137fed 100644
--- a/inc/cloud/cloud_request.h
+++ b/inc/cloud/cloud_request.h
@@ -17,33 +17,56 @@
#ifndef __CLOUD_REQUEST_H_
#define __CLOUD_REQUEST_H_
+#include <gio/gio.h>
#include "car_info.h"
/**
- * @brief Called when data from HTTP /api/racing request was obtained.
+ * @brief Enum that indicates if HTTP request finished succesfully or not.
+*/
+typedef enum
+{
+ SUCCESS = 0,
+ FAILURE
+} request_result_e;
+
+/**
+ * @brief Called when data from HTTP /api/racing GET request was obtained.
+ *
+ * @param[in] result Result of request.
* @param[in] car_info The array of car_info struct.
* @param[in] size Length of the car_info array
+ * @param[in] user_data User data passed in @cloud_request_api_racing_get function.
+ */
+typedef void(*cloud_request_car_list_data_cb)(request_result_e result, car_info_t **car_info, int size, void *user_data);
+
+/**
+ * @brief Called when data from HTTP /api/racing POST request was obtained.
+ *
+ * @param[in] result Result of request.
+ * @param[in] user_data User data passed in @cloud_request_api_racing_post function.
*/
-typedef void(*cloud_request_car_info_data_cb)(car_info_t *car_info, int size);
+typedef void(*cloud_request_car_post_finish_cb)(request_result_e result, void *user_data);
/**
* @brief Sends cloud request that obtains list of registered cars.
+ *
* @param[in] ap_mac Mac address of access point that device is connected to.
- * @return Returns 0 on success, -1 otherwise.
+ * @param[in] callback Function that will be invoked, when request will be finished.
+ *
+ * @return Returns @GCancellable object that allows to cancel this request.
+ * @remark To cancel task function g_cancellable_cancel should be called.
*/
-int cloud_request_api_racing_get(const char *ap_mac);
+GCancellable *cloud_request_api_racing_get(const char *ap_mac, cloud_request_car_list_data_cb callback, void *user_data);
/**
* @brief Sends cloud request registering the car.
+ *
* @param[in] car_info The car_info object with car data.
- * @return Returns 0 on success, -1 otherwise.
- */
-int cloud_request_api_racing_post(car_info_t *car_info);
-
-/**
- * @brief Sets callback that will be called when data from request will be obtained.
- * @param[in] callback Callback function.
+ * @param[in] callback Function that will be invoked, when request will be finished.
+ *
+ * @return Returns @GCancellable object that allows to cancel this request.
+ * @remark To cancel task function g_cancellable_cancel should be called.
*/
-void cloud_request_data_cb_set(cloud_request_car_info_data_cb callback);
+GCancellable *cloud_request_api_racing_post(const car_info_t *car_info, cloud_request_car_post_finish_cb callback, void *user_data);
#endif \ No newline at end of file
diff --git a/src/cloud/car_info.c b/src/cloud/car_info.c
index f54d31a..7c977d7 100644
--- a/src/cloud/car_info.c
+++ b/src/cloud/car_info.c
@@ -29,6 +29,12 @@
#define MAX_LENGTH_MAC 18
#define MAX_LENGTH_SSID 33
+#define SAFE_STR_CPY(src, obj, size)\
+do {\
+ if(obj)\
+ src = strndup(obj, size);\
+} while(0)
+
static int validate_ip_address(const char *ip_address);
static int validate_mac_address(const char *mac_address);
@@ -49,6 +55,20 @@ car_info_t *car_info_create()
return car_info;
}
+car_info_t *car_info_copy(const car_info_t *car_info)
+{
+ retv_if(!car_info, NULL);
+
+ struct car_info *car_info_cpy = g_new0(struct car_info, 1);
+ SAFE_STR_CPY(car_info_cpy->id, car_info->id, MAX_LENGTH);
+ SAFE_STR_CPY(car_info_cpy->name, car_info->name, MAX_LENGTH);
+ SAFE_STR_CPY(car_info_cpy->ip, car_info->ip, MAX_LENGTH_IP);
+ SAFE_STR_CPY(car_info_cpy->ap_mac, car_info->ap_mac, MAX_LENGTH_MAC);
+ SAFE_STR_CPY(car_info_cpy->ap_ssid, car_info->ap_ssid, MAX_LENGTH_SSID);
+
+ return car_info_cpy;
+}
+
void car_info_destroy(car_info_t *car_info)
{
ret_if(!car_info);
diff --git a/src/cloud/car_info_serializer.c b/src/cloud/car_info_serializer.c
index a9bf9f3..3ee74f6 100644
--- a/src/cloud/car_info_serializer.c
+++ b/src/cloud/car_info_serializer.c
@@ -21,8 +21,10 @@
#include <json-glib/json-glib.h>
#include "log.h"
#include "cloud/car_info.h"
+#include "string.h"
#define JSON_SCHEMA_CAR_ID "carId"
+#define JSON_SCHEMA_RESPONSE_CAR_ID "id"
#define JSON_SCHEMA_CAR_NAME "carName"
#define JSON_SCHEMA_CAR_IP "carIp"
#define JSON_SCHEMA_AP_MAC "apMac"
@@ -72,12 +74,17 @@ car_info_t **car_info_serializer_deserialize_array(const char *json_data, int *s
JsonArray *array = json_node_get_array(root);
if (!array)
{
- _E("Json config is invalid!");
+ _E("Json is invalid!");
g_object_unref(parser);
return NULL;
}
*size = json_array_get_length(array);
+
+ if (*size == 0) {
+ return NULL;
+ }
+
car_info_t **car_info_array = g_malloc(*size * sizeof(car_info_t *));
json_array_foreach_element(array, car_info_array_iterate_cb, car_info_array);
@@ -86,13 +93,13 @@ car_info_t **car_info_serializer_deserialize_array(const char *json_data, int *s
return car_info_array;
}
-static JsonNode *parse_string(JsonParser *parser, const char *config_json)
+static JsonNode *parse_string(JsonParser *parser, const char *json)
{
GError *err = NULL;
- if (!json_parser_load_from_data(parser, config_json, -1, &err))
+ if (!json_parser_load_from_data(parser, json, -1, &err))
{
- _E("Function \"json_parser_load_from_data()\" failed with message: ", err->message);
+ _E("Function \"json_parser_load_from_data()\" failed with message: %s", err->message);
g_error_free(err);
return NULL;
}
@@ -108,8 +115,8 @@ static void car_info_array_iterate_cb(JsonArray *array, guint index, JsonNode *e
car_info_array[index] = car_info_create();
- if (json_object_has_member(entry, JSON_SCHEMA_CAR_ID) &&
- car_info_set_car_id(car_info_array[index], json_object_get_string_member(entry, JSON_SCHEMA_CAR_ID)) != 0)
+ if (json_object_has_member(entry, JSON_SCHEMA_RESPONSE_CAR_ID) &&
+ car_info_set_car_id(car_info_array[index], json_object_get_string_member(entry, JSON_SCHEMA_RESPONSE_CAR_ID)) != 0)
{
_E("Couldn't set car id!");
}
diff --git a/src/cloud/cloud_request.c b/src/cloud/cloud_request.c
new file mode 100644
index 0000000..1a46fa1
--- /dev/null
+++ b/src/cloud/cloud_request.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Flora License, Version 1.1 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://floralicense.org/license/
+ *
+ * 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 "cloud/cloud_request.h"
+#include "cloud/car_info_serializer.h"
+#include "cloud/http_request.h"
+#include <glib.h>
+#include <gio/gio.h>
+#include <string.h>
+#include "log.h"
+
+#define BASE_URL "https://son.tizen.online"
+#define PATH_API_RACING "/api/racing"
+
+typedef struct {
+ char *ap_mac;
+ cloud_request_car_list_data_cb cb;
+ void *user_data;
+} car_api_get_request_context_t;
+
+typedef struct {
+ car_info_t *car;
+ cloud_request_car_post_finish_cb cb;
+ void *user_data;
+} car_api_post_request_context_t;
+
+typedef struct {
+ long response_code;
+ char *response_msg;
+} car_api_post_request_response_t;
+
+typedef struct {
+ long response_code;
+ car_info_t **cars;
+ int size;
+} car_api_get_request_response_t;
+
+GQuark g_spawn_error_quark();
+static void car_api_post_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable);
+static void car_api_post_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data);
+static void car_api_post_task_context_free(car_api_post_request_context_t *context);
+static void car_api_post_request_response_free(car_api_post_request_response_t *response);
+
+static void car_api_get_task_context_free(car_api_get_request_context_t *context);
+static void car_api_get_request_response_free(car_api_get_request_response_t *response);
+static void car_api_get_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable);
+static void car_api_get_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data);
+
+#define G_ERROR_DOMAIN g_spawn_error_quark()
+
+GCancellable *cloud_request_api_racing_get(const char *ap_mac, cloud_request_car_list_data_cb cb, void *user_data)
+{
+ GCancellable *cancellable = g_cancellable_new();
+
+ GTask *task = g_task_new(NULL, cancellable, car_api_get_task_ready_cb, NULL);
+ g_task_set_source_tag(task, cloud_request_api_racing_get);
+ g_task_set_return_on_cancel(task, FALSE);
+
+ car_api_get_request_context_t *context = g_new0(car_api_get_request_context_t, 1);
+ context->ap_mac = strndup(ap_mac, strlen(ap_mac));
+ context->cb = cb;
+ context->user_data = user_data;
+
+ g_task_set_task_data(task, context, (GDestroyNotify)car_api_get_task_context_free);
+ g_task_run_in_thread(task, car_api_get_task_thread_cb);
+
+ g_object_unref(task);
+
+ return cancellable;
+}
+
+GCancellable *cloud_request_api_racing_post(const car_info_t *car_info, cloud_request_car_post_finish_cb cb, void *user_data)
+{
+ GCancellable *cancellable = g_cancellable_new();
+
+ GTask *task = g_task_new(NULL, cancellable, car_api_post_task_ready_cb, NULL);
+ g_task_set_source_tag(task, cloud_request_api_racing_post);
+ g_task_set_return_on_cancel(task, FALSE);
+
+ car_api_post_request_context_t *context = g_new0(car_api_post_request_context_t, 1);
+ context->car = car_info_copy(car_info);
+ context->cb = cb;
+ context->user_data = user_data;
+
+ g_task_set_task_data(task, context, (GDestroyNotify)car_api_post_task_context_free);
+ g_task_run_in_thread(task, car_api_post_task_thread_cb);
+
+ g_object_unref(task);
+
+ return cancellable;
+}
+
+GQuark g_spawn_error_quark()
+{
+ return g_quark_from_static_string("cloud_request");
+}
+
+static void car_api_post_task_context_free(car_api_post_request_context_t *context)
+{
+ ret_if(!context);
+
+ car_info_destroy(context->car);
+ g_free(context);
+}
+
+static void car_api_post_request_response_free(car_api_post_request_response_t *response)
+{
+ ret_if(!response);
+
+ g_free(response->response_msg);
+ g_free(response);
+}
+
+static void car_api_post_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
+{
+ car_api_post_request_context_t *context = (car_api_post_request_context_t *)task_data;
+
+ if (g_task_return_error_if_cancelled(task)) {
+ return;
+ }
+
+ car_api_post_request_response_t *response = g_new0(car_api_post_request_response_t, 1);
+
+ char *json = car_info_serializer_serialize(context->car);
+ int retval = http_request_post(BASE_URL""PATH_API_RACING, json, &(response->response_msg), &(response->response_code));
+ g_free(json);
+
+ if (retval != 0) {
+ GError *err = g_error_new(G_ERROR_DOMAIN, retval, "http_request_post failed!");
+ g_task_return_error(task, err);
+ }
+
+ g_task_return_pointer(task, response, (GDestroyNotify)car_api_post_request_response_free);
+}
+
+static void car_api_post_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GTask *task = G_TASK(res);
+ GError *error = NULL;
+
+ //If no error occurred g_task_propagate_pointer transfers ownership, so later response have to be freed.
+ car_api_post_request_response_t *response = g_task_propagate_pointer(task, &error);
+ if (error != NULL) {
+ _E("POST async task failed with msg: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+ car_api_post_request_context_t *context = g_task_get_task_data(task);
+
+ request_result_e result = (response->response_code == 200 && (strcmp(response->response_msg, "Success") == 0)) ?
+ SUCCESS :
+ FAILURE;
+
+ if (context->cb) {
+ context->cb(result, context->user_data);
+ }
+
+ car_api_post_request_response_free(response);
+}
+
+static void car_api_get_task_context_free(car_api_get_request_context_t *context)
+{
+ ret_if(!context);
+
+ g_free(context->ap_mac);
+ g_free(context);
+}
+
+static void car_api_get_request_response_free(car_api_get_request_response_t *response)
+{
+ ret_if(!response);
+ ret_if(response->size <= 0);
+
+ for (int i = 0; i < response->size; i++)
+ {
+ car_info_destroy(response->cars[i]);
+ }
+ g_free(response->cars);
+ g_free(response);
+}
+
+static void car_api_get_task_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable)
+{
+ car_api_get_request_context_t *context = (car_api_get_request_context_t *)task_data;
+
+ if (g_task_return_error_if_cancelled(task)) {
+ return;
+ }
+
+ car_api_get_request_response_t *response = g_new0(car_api_get_request_response_t, 1);
+ char *response_json = NULL;
+
+ GString *url = g_string_new(BASE_URL""PATH_API_RACING"?apMac=");
+ g_string_append(url, context->ap_mac);
+
+ int retval = http_request_get(url->str, &response_json, &(response->response_code));
+ g_string_free(url, TRUE);
+
+ if (retval != 0) {
+ GError *err = g_error_new(G_ERROR_DOMAIN, retval, "http_request_get failed!");
+ g_task_return_error(task, err);
+ }
+ else {
+ response->cars = car_info_serializer_deserialize_array(response_json, &(response->size));
+ }
+
+ g_free(response_json);
+ g_task_return_pointer(task, response, (GDestroyNotify)car_api_get_request_response_free);
+}
+
+static void car_api_get_task_ready_cb(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+ GTask *task = G_TASK(res);
+ GError *error = NULL;
+
+ //If no error occurred g_task_propagate_pointer transfers ownership, so later response have to be freed.
+ car_api_get_request_response_t *response = g_task_propagate_pointer(task, &error);
+ if (error != NULL) {
+ _E("GET async task failed with msg: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+ car_api_get_request_context_t *context = g_task_get_task_data(task);
+
+ request_result_e result = (response->response_code == 200) ? SUCCESS : FAILURE;
+
+ if (context->cb) {
+ context->cb(result, response->cars, response->size, context->user_data);
+ }
+
+ car_api_get_request_response_free(response);
+} \ No newline at end of file