#include <string.h> #include <stdlib.h> #include <stdint.h> #include <stdio.h> #include <uuid/uuid.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <netdb.h> #include <errno.h> #include <logger.h> #include <database.h> #include <handlers.h> #include <helpers.h> #include <mpack.h> #include <models/controller.h> #include <models/relay.h> #include <models/schedule.h> #include <models/junction_relay_schedule.h> typedef enum { COMMAND_CODE_CONTROLLER_ID_GET = 0, COMMAND_CODE_CONTROLLER_TIME_GET = 1, COMMAND_CODE_CONTROLLER_NAME_SET = 2, COMMAND_CODE_CONTROLLER_NAME_GET = 3, COMMAND_CODE_RELAY_SCHEDULES_SET = 100, COMMAND_CODE_RELAY_SCHEDULES_GET = 101, COMMAND_CODE_RELAY_NAME_SET = 102, COMMAND_CODE_RELAY_NAME_GET = 103, COMMAND_CODE_RELAY_PULSE = 200, COMMAND_CODE_SCHEDULE_UPDATE = 300 } command_code_t; typedef enum { COMMAND_MAPPING_CODE = 0, COMMAND_MAPPING_NAME = 1, COMMAND_MAPPING_RELAY_NUM = 2, COMMAND_MAPPING_SCHEDULES_ARRAY = 3, COMMAND_MAPPING_SCHEDULE_ID = 4, COMMAND_MAPPING_PERIODS_COUNT = 5, COMMAND_MAPPING_PERIODS_BLOB = 6, COMMAND_MAPPING_PULSE_DURATION = 7, } control_mapping_t; static void handler_command_relay_pulse(mpack_node_t map) { uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM)); if(relay_num > global_config->relay_count) { LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config->relay_count); return; } relay_t *target_relay = global_controller->relays[relay_num]; (void)target_relay; int duration = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_PULSE_DURATION)); if(duration == 0) { duration = global_config->relay_configs[relay_num].pulse_duration; } target_relay->pulse_timer = duration; LOGGER_DEBUG("pulsing relay %d for %ds\n", relay_num, duration); } static void handler_command_controller_name_set(mpack_node_t map) { char name_buffer[MAX_NAME_LENGTH + 1]; mpack_node_copy_cstr(mpack_node_map_uint(map, COMMAND_MAPPING_NAME), name_buffer, MAX_NAME_LENGTH + 1); controller_set_name(global_controller, name_buffer); LOGGER_DEBUG("setting new name %s for controller\n", name_buffer); controller_save(); } static void handler_command_relay_name_set(mpack_node_t map) { uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM)); const char *relay_name = mpack_node_str(mpack_node_map_uint(map, COMMAND_MAPPING_NAME)); if(relay_num > global_config->relay_count) { LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config->relay_count); return; } relay_set_name(global_controller->relays[relay_num], relay_name); LOGGER_DEBUG("setting new name %s for relay %d\n", relay_name, relay_num); relay_save(global_controller->relays[relay_num]); } static void handler_command_schedule_update(mpack_node_t map) { uuid_t schedule_uid; memcpy(schedule_uid, mpack_node_data(mpack_node_map_uint(map, COMMAND_MAPPING_SCHEDULE_ID)), sizeof(uuid_t)); uint16_t periods_count = mpack_node_u16(mpack_node_map_uint(map, COMMAND_MAPPING_PERIODS_COUNT)); uint16_t *periods = (uint16_t*)mpack_node_bin_data(mpack_node_map_uint(map, COMMAND_MAPPING_PERIODS_BLOB)); schedule_t *schedule = schedule_get_by_uid(schedule_uid); schedule_t *new_schedule = schedule_create(schedule_uid, periods_count, periods); if(schedule) { new_schedule->id = schedule->id; schedule_free(schedule); } schedule_save(new_schedule); int *relay_ids = junction_relay_schedule_get_relay_ids_with_schedule(new_schedule->id); for(int i = 0; i < global_config->relay_count; ++i) { for(int j = 0; relay_ids[j] != 0; ++j) { if(global_controller->relays[i]->id == relay_ids[j]) { relay_reload_schedules(global_controller->relays[i]); } } } free(relay_ids); schedule_free(new_schedule); } static void handler_command_relay_schedules_set(mpack_node_t map) { uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM)); if(relay_num > global_config->relay_count) { LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config->relay_count); return; } LOGGER_DEBUG("setting schedules for relay %d\n", relay_num); relay_t *target_relay = global_controller->relays[relay_num]; database_transaction_begin(); junction_relay_schedule_remove_for_relay(target_relay->id); mpack_node_t schedules_array = mpack_node_map_uint(map, COMMAND_MAPPING_SCHEDULES_ARRAY); for(int i = 0; i < 7; ++i) { mpack_node_t schedule_map = mpack_node_array_at(schedules_array, i); uuid_t schedule_uid; memcpy(schedule_uid, mpack_node_data(mpack_node_map_uint(schedule_map, COMMAND_MAPPING_SCHEDULE_ID)), sizeof(uuid_t)); uint16_t periods_count = mpack_node_u16(mpack_node_map_uint(schedule_map, COMMAND_MAPPING_PERIODS_COUNT)); uint16_t *periods = (uint16_t*)mpack_node_bin_data(mpack_node_map_uint(schedule_map, COMMAND_MAPPING_PERIODS_BLOB)); schedule_t *schedule = schedule_get_by_uid(schedule_uid); schedule_t *new_schedule = schedule_create(schedule_uid, periods_count, periods); if(schedule) { new_schedule->id = schedule->id; schedule_free(schedule); } schedule_save(new_schedule); junction_relay_schedule_insert(i, target_relay->id, new_schedule->id); } relay_reload_schedules(target_relay); database_transaction_commit(); } void handler_command(struct mg_connection *c, int ev, void *ev_data) { (void)ev_data; if(ev != MG_EV_RECV) { return; } uint32_t payload_length = *((uint32_t*)c->recv_mbuf.buf); if(c->recv_mbuf.len < payload_length + sizeof(payload_length)) { return; } char *payload = c->recv_mbuf.buf + sizeof(payload_length); mpack_tree_t tree; mpack_tree_init_data(&tree, payload, payload_length); mpack_tree_parse(&tree); mpack_node_t root = mpack_tree_root(&tree); uint16_t command_code = mpack_node_u16(mpack_node_map_uint(root, COMMAND_MAPPING_CODE)); LOGGER_DEBUG("received command %d\n", command_code); switch(command_code) { case COMMAND_CODE_CONTROLLER_ID_GET: break; case COMMAND_CODE_CONTROLLER_TIME_GET: break; case COMMAND_CODE_CONTROLLER_NAME_SET: handler_command_controller_name_set(root); break; case COMMAND_CODE_CONTROLLER_NAME_GET: break; case COMMAND_CODE_RELAY_SCHEDULES_SET: handler_command_relay_schedules_set(root); break; case COMMAND_CODE_RELAY_SCHEDULES_GET: break; case COMMAND_CODE_RELAY_NAME_SET: handler_command_relay_name_set(root); break; case COMMAND_CODE_RELAY_NAME_GET: break; case COMMAND_CODE_RELAY_PULSE: handler_command_relay_pulse(root); break; case COMMAND_CODE_SCHEDULE_UPDATE: handler_command_schedule_update(root); break; default: LOGGER_ERR("received invalid command\n"); } if(mpack_tree_destroy(&tree) != mpack_ok) { LOGGER_WARNING("error when destroying mpack tree\n"); } LOGGER_DEBUG("done with command %d - closing connection\n", command_code); c->flags |= MG_F_SEND_AND_CLOSE; }