Implemented car connection manager 59/189259/19
authorKrzysztof Wieclaw <k.wieclaw@samsung.com>
Tue, 25 Sep 2018 12:37:54 +0000 (14:37 +0200)
committerKrzysztof Wieclaw <k.wieclaw@samsung.com>
Tue, 2 Oct 2018 18:17:39 +0000 (20:17 +0200)
Change-Id: I1c101665304cbeae8939dde8f4bdeba58d5c6b71
Signed-off-by: Krzysztof Wieclaw <k.wieclaw@samsung.com>
inc/car_connection_manager.h
src/car_connection_manager.c [new file with mode: 0644]
src/math_helper.c

index 7cacc8f..03ee807 100644 (file)
@@ -18,6 +18,7 @@
 #define INC_CAR_CONNECTION_MANAGER_H_
 
 #include "command.h"
+#include "messages/message.h"
 
 /**
  * @brief Describes state of connection.
@@ -37,11 +38,10 @@ typedef void (*connection_state_cb)(car_connection_state_e previous, car_connect
 
 /**
  * @brief Inits communication manager on the given port.
- * @param[in] port Port on which application will be listening.
  * @return 0 on success, -1 otherwise.
  * @remarks This function allocates resources that have to be freed with controller_car_manager_fini.
  */
-int car_connection_manager_init(int port);
+int car_connection_manager_init();
 
 /**
  * @brief Gets currect connection state.
@@ -71,6 +71,12 @@ int car_connection_manager_connect(const char *address, int port);
 void car_connection_manager_disconnect();
 
 /**
+ * @brief Handles arriving message.
+ * @param[in] message Message to be handled.
+ */
+void car_connection_manager_handle_message(message_t *message);
+
+/**
  * @brief Sends data about driving.
  * @param[in] command command to be
  */
diff --git a/src/car_connection_manager.c b/src/car_connection_manager.c
new file mode 100644 (file)
index 0000000..9dd5132
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * 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 "car_connection_manager.h"
+#include <stdlib.h>
+#include <glib.h>
+#include <string.h>
+#include "log.h"
+#include "messages/message.h"
+#include "messages/message_command.h"
+#include "messages/message_manager.h"
+#include "messages/message_factory.h"
+#include "messages/message_ack.h"
+
+#define KEEP_ALIVE_ATTEMPTS 5
+#define HELLO_START_ATTEMPTS 5
+#define KEEP_ALIVE_INTERVAL 1000 //In ms
+#define HELLO_INTERVAL 1000 //In ms
+
+#define SAFE_SOURCE_REMOVE(source) do{if(source) { _W("Removing source no %d", source) ;g_source_remove(source); } source = 0;}while(0)
+
+typedef struct _car_connection_manager_info {
+       car_connection_state_e state;
+       char *car_address;
+       int car_port;
+       connection_state_cb state_cb;
+       unsigned int keep_alive_attempts_left;
+       unsigned int hello_attempts_left;
+       guint keep_alive_timer;
+       guint hello_timer;
+       message_factory_t *message_factory;
+       unsigned long long int last_acked;
+} _car_connection_manager_s;
+
+static _car_connection_manager_s s_info = {
+       .state = CAR_CONNECTION_STATE_DISCONNECTED,
+       .car_address = NULL,
+       .state_cb = NULL,
+       .keep_alive_attempts_left = 0,
+       .hello_attempts_left = 0,
+       .hello_timer = 0,
+       .keep_alive_timer = 0
+};
+
+static void _set_state(car_connection_state_e state);
+static int _start_hello_timer();
+static int _start_keep_alive_timer();
+static void _receive_cb(message_t *message, void *data);
+static gboolean _send_hello();
+static void _reset_counters();
+static gboolean _send_keep_alive();
+static gboolean _try_connect_cb(gpointer data);
+static gboolean _keep_alive_cb(gpointer data);
+static int _addr_cmp(const char *addr1, int port1, const char *addr2, int port2);
+
+int car_connection_manager_init()
+{
+       s_info.message_factory = message_factory_create();
+       if(!s_info.message_factory) {
+               _E("Failed to create message factory");
+               return -1;
+       }
+       message_manager_set_receive_message_cb(_receive_cb, NULL);
+       return 0;
+}
+
+car_connection_state_e car_connection_manager_get_state()
+{
+       return s_info.state;
+}
+
+void car_connection_manager_set_state_change_cb(connection_state_cb callback)
+{
+       s_info.state_cb = callback;
+}
+
+int car_connection_manager_connect(const char *address, int port)
+{
+       if(s_info.state != CAR_CONNECTION_STATE_DISCONNECTED) {
+               _E("Failed to connect with %s:%d - connected already initiated with %s:%d", address, port, s_info.car_address, s_info.car_port);
+               return -1;
+       }
+
+       if(!address) {
+               _E("Address cannot be NULL");
+               return -1;
+       }
+
+       s_info.car_address = strdup(address);
+       if(!s_info.car_address) {
+               _E("Failed to save new address");
+               return -1;
+       }
+       s_info.car_port = port;
+       _I("Connecting with %s:%d...", address, port);
+       _set_state(CAR_CONNECTION_STATE_CONNECTING);
+       _reset_counters();
+       message_manager_set_receive_message_cb(_receive_cb, NULL);
+       _start_hello_timer();
+       return 0;
+}
+
+void car_connection_manager_disconnect()
+{
+       if(s_info.state == CAR_CONNECTION_STATE_DISCONNECTED) {
+               _E("Failed to disconnect - connection hasn't been initiated");
+               return;
+       }
+
+       message_t *message = message_factory_create_message(s_info.message_factory, MESSAGE_BYE);
+       message_set_receiver(message, s_info.car_address, s_info.car_port);
+
+       if(!message) {
+               _W("Failed to create BYE message");
+       }
+
+       if(message && message_manager_send_message(message)) {
+               _W("Failed to send BYE message");
+       }
+
+       if(message) {
+               message_destroy(message);
+       }
+       _I("Disconnected from %s:%d", s_info.car_address, s_info.car_port);
+       free(s_info.car_address);
+       s_info.car_address = NULL;
+       s_info.car_port = -1;
+       SAFE_SOURCE_REMOVE(s_info.hello_timer);
+       SAFE_SOURCE_REMOVE(s_info.keep_alive_timer);
+       _set_state(CAR_CONNECTION_STATE_DISCONNECTED);
+}
+
+void car_connection_manager_handle_message(message_t *message)
+{
+       const char *msg_address;
+       int msg_port;
+       message_get_sender(message, &msg_address, &msg_port);
+       if(_addr_cmp(s_info.car_address, s_info.car_port, msg_address, msg_port)) {
+               _W("Received message from unconnected sender");
+               return;
+       }
+
+       switch(message_get_type(message)) {
+       case MESSAGE_CONNECT_ACCEPTED:
+               if(s_info.state == CAR_CONNECTION_STATE_CONNECTING) {
+                       SAFE_SOURCE_REMOVE(s_info.hello_timer);
+                       _D("Received CONNECT_ACCEPTED");
+                       _I("Connected with car %s:%d", s_info.car_address, s_info.car_port);
+                       s_info.keep_alive_attempts_left = KEEP_ALIVE_ATTEMPTS;
+                       _set_state(CAR_CONNECTION_STATE_CONNECTED);
+                       _start_keep_alive_timer();
+                       s_info.last_acked = 0;
+               } else {
+                       _W("Unexpectedly received CONNECT_ACCEPTED");
+               }
+               break;
+       case MESSAGE_ACK:
+               if(s_info.state == CAR_CONNECTION_STATE_CONNECTED) {
+                       unsigned long long int serial = message_ack_get_ack_serial((message_ack_t*)message);
+                       if(serial > s_info.last_acked) {
+                               _D("Received KEEP_ALIVE_ACK");
+                               s_info.keep_alive_attempts_left = KEEP_ALIVE_ATTEMPTS;
+                               s_info.last_acked = serial;
+                       }
+                       else {
+                               _W("Received late ACK (%d, when last is %d)", serial, s_info.last_acked);
+                       }
+               } else {
+                       _W("Unexpectedly received ACK");
+               }
+               break;
+       case MESSAGE_CONNECT_REFUSED:
+               if(s_info.state == CAR_CONNECTION_STATE_CONNECTING) {
+                       _D("Received CONNECT_REFUSE");
+                       _I("Car refused connecting");
+                       _set_state(CAR_CONNECTION_STATE_DISCONNECTED);
+               } else {
+                       _W("Unexpectedly received CONNECT_REFUSE");
+               }
+               break;
+       default:
+               _W("Unknown message received");
+               break;
+       }
+}
+
+int car_connection_manager_send_command(command_s command)
+{
+       if(s_info.state != CAR_CONNECTION_STATE_CONNECTED) {
+               _E("Connection not established, command not sent");
+               return -1;
+
+       }
+
+       message_t *message = message_factory_create_message(s_info.message_factory, MESSAGE_COMMAND);
+       if(!message) {
+               _E("Failed to create COMMAND message");
+               return -1;
+       }
+       message_set_receiver(message, s_info.car_address, s_info.car_port);
+       message_command_set_command((message_command_t*)message, &command);
+       int res = message_manager_send_message(message);
+       message_destroy(message);
+       if(res) {
+               _E("Failed to send command");
+               return -1;
+       }
+       return 0;
+}
+
+void car_connection_manager_fini()
+{
+       if(s_info.state == CAR_CONNECTION_STATE_CONNECTED || s_info.state == CAR_CONNECTION_STATE_CONNECTING)
+               car_connection_manager_disconnect();
+       message_manager_shutdown();
+}
+
+static void _receive_cb(message_t *message, void *data)
+{
+       car_connection_manager_handle_message(message);
+}
+
+static void _set_state(car_connection_state_e state)
+{
+       if(s_info.state == state) {
+               return;
+       }
+
+       car_connection_state_e previous = s_info.state;
+       s_info.state = state;
+       _I("Connection state changed from %d to %d", previous, state);
+
+       if(s_info.state_cb) {
+               s_info.state_cb(previous, state);
+       }
+}
+
+static gboolean _send_hello()
+{
+       if(s_info.state != CAR_CONNECTION_STATE_CONNECTING) {
+               s_info.hello_attempts_left = 0;
+               s_info.hello_timer = 0;
+               _E("Cannot send when not connecting");
+               return FALSE;
+       }
+
+       if(!s_info.hello_attempts_left--) {
+               _W("Made all attempts for hello - disconnecting started");
+               car_connection_manager_disconnect();
+               return FALSE;
+       }
+
+       message_t *message = message_factory_create_message(s_info.message_factory, MESSAGE_CONNECT);
+       if(message) {
+               message_set_receiver(message, s_info.car_address, s_info.car_port);
+               int res = message_manager_send_message(message);
+               if(res) {
+                       _E("Failed to send HELLO message");
+               }
+               message_destroy(message);
+       } else {
+               _E("Failed to create HELLO message");
+       }
+
+       return TRUE;
+}
+static gboolean _send_keep_alive()
+{
+       if(s_info.state == CAR_CONNECTION_STATE_DISCONNECTED) {
+               s_info.keep_alive_attempts_left = 0;
+               s_info.keep_alive_timer = 0;
+               return FALSE;
+       }
+
+       if(s_info.state == CAR_CONNECTION_STATE_CONNECTING) {
+               return TRUE;
+       }
+
+       if(!s_info.keep_alive_attempts_left--) {
+               car_connection_manager_disconnect();
+               return FALSE;
+       }
+
+       message_t *message = message_factory_create_message(s_info.message_factory, MESSAGE_KEEP_ALIVE);
+       if(!message) {
+               _E("Failed to create KEEP_ALIVE message");
+               return TRUE;
+       }
+
+       message_set_receiver(message, s_info.car_address, s_info.car_port);
+       int res = message_manager_send_message(message);
+       message_destroy(message);
+       if(res) {
+               _E("Failed to send KEEP_ALIVE message");
+       }
+
+       return TRUE;
+}
+
+static gboolean _keep_alive_cb(gpointer data)
+{
+       return _send_keep_alive();
+}
+
+static gboolean _try_connect_cb(gpointer data)
+{
+       return _send_hello();
+}
+
+static int _start_hello_timer()
+{
+       if(_send_hello()) {
+               s_info.hello_timer = g_timeout_add(HELLO_INTERVAL, _try_connect_cb, NULL);
+               return 0;
+       }
+       return -1;
+}
+
+static int _start_keep_alive_timer()
+{
+       if(_send_keep_alive()) {
+               s_info.keep_alive_timer = g_timeout_add(KEEP_ALIVE_INTERVAL, _keep_alive_cb, NULL);
+               return 0;
+       }
+       return -1;
+}
+
+static void _reset_counters()
+{
+       s_info.keep_alive_attempts_left = HELLO_START_ATTEMPTS;
+       s_info.hello_attempts_left = KEEP_ALIVE_ATTEMPTS;
+}
+
+static int _addr_cmp(const char *addr1, int port1, const char *addr2, int port2)
+{
+       if(addr1 == NULL || addr2 == NULL) {
+               return -1;
+       }
+
+       unsigned int address_length = strlen(addr2);
+       return port1 != port2 || strlen(addr1) != address_length || strncmp(addr1, addr2, address_length);
+}
+
index 31342cb..7d25a5e 100644 (file)
@@ -15,7 +15,7 @@
 */
 
 #include "math_helper.h"
-
+#include "car_connection_manager.h"
 #include <math.h>
 
 float math_helper_range_map(float value, float input_min, float input_max, float output_min, float output_max)