From db64e4f820625b830c88fabd32dafa4013118465 Mon Sep 17 00:00:00 2001 From: Tobias Reisinger Date: Tue, 14 Apr 2020 00:50:55 +0200 Subject: [PATCH] add: relay/piface support --- CMakeLists.txt | 22 +++-- discovery.c | 125 ---------------------------- drivers/gpio.c | 11 +++ drivers/piface.c | 11 +++ handlers/command.c | 113 +++++++++++++++++++++++++ handlers/discovery.c | 72 ++++++++++++++++ helpers/bind_server.c | 51 ++++++++++++ helpers/connect_server.c | 44 +++++----- helpers/get_port.c | 24 ++++++ helpers/open_discovery_socket.c | 57 +++++++++++++ include/config.h | 6 +- include/constants.h | 3 + include/discovery.h | 27 ------ include/drivers.h | 14 ++++ include/enums.h | 36 +++++++- include/handlers.h | 24 ++++++ include/helper.h | 7 -- include/helpers.h | 25 ++++++ include/logger.h | 21 +++-- include/models/controller.h | 46 +++++++---- include/models/period.h | 19 +++++ include/models/relay.h | 61 ++++++++++++-- include/models/schedule.h | 28 +++++++ include/wiring_debug.h | 7 +- logger.c | 10 +++ main.c | 141 ++++++++++++++++++++++++++------ models/controller.c | 60 ++++++++++---- models/controller_load.c | 46 ++++++----- models/controller_save.c | 59 +++++++------ models/period.c | 44 ++++++++++ models/relay.c | 81 +++++++++++++++++- models/relay_load.c | 100 ++++++++++++++++++++++ models/relay_save.c | 88 ++++++++++++++++++++ models/schedule.c | 89 ++++++++++++++++++++ 34 files changed, 1259 insertions(+), 313 deletions(-) delete mode 100644 discovery.c create mode 100644 drivers/gpio.c create mode 100644 drivers/piface.c create mode 100644 handlers/command.c create mode 100644 handlers/discovery.c create mode 100644 helpers/bind_server.c create mode 100644 helpers/get_port.c create mode 100644 helpers/open_discovery_socket.c create mode 100644 include/constants.h delete mode 100644 include/discovery.h create mode 100644 include/drivers.h create mode 100644 include/handlers.h delete mode 100644 include/helper.h create mode 100644 include/helpers.h create mode 100644 include/models/period.h create mode 100644 include/models/schedule.h create mode 100644 logger.c create mode 100644 models/period.c create mode 100644 models/relay_load.c create mode 100644 models/relay_save.c create mode 100644 models/schedule.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b06ff60..6d03031 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ add_executable(controller main.c) option(WIRING_PI_DEBUG "Use WiringPi Debugging Tool" OFF) -SET(CMAKE_C_FLAGS "-Wall -Wextra -lwiringPi -luuid -llmdb -g") +SET(CMAKE_C_FLAGS "-Wall -Wextra -lwiringPi -lwiringPiDev -luuid -llmdb -g") string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE) add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}") @@ -16,14 +16,26 @@ if(WIRING_PI_DEBUG) endif(WIRING_PI_DEBUG) aux_source_directory(. SRC_DIR) -aux_source_directory(models MODEL_SRC) -aux_source_directory(helpers HELPER_SRC) +aux_source_directory(models MODELS_SRC) +aux_source_directory(helpers HELPERS_SRC) +aux_source_directory(handlers HANDLERS_SRC) +aux_source_directory(drivers DRIVERS_SRC) -target_sources(controller PRIVATE ${SRC_DIR} ${MODEL_SRC} ${HELPER_SRC}) +target_sources(controller PRIVATE ${SRC_DIR} ${MODELS_SRC} ${HELPERS_SRC} ${HANDLERS_SRC} ${DRIVERS_SRC}) target_include_directories(controller PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) add_custom_target(run - COMMAND controller + COMMAND ./controller + DEPENDS controller + WORKING_DIRECTORY ${CMAKE_PROJECT_DIR} +) +add_custom_target(debug + COMMAND valgrind ./controller + DEPENDS controller + WORKING_DIRECTORY ${CMAKE_PROJECT_DIR} +) +add_custom_target(debug-full + COMMAND valgrind --leak-check=full --show-leak-kinds=all ./controller DEPENDS controller WORKING_DIRECTORY ${CMAKE_PROJECT_DIR} ) diff --git a/discovery.c b/discovery.c deleted file mode 100644 index b1ae4d6..0000000 --- a/discovery.c +++ /dev/null @@ -1,125 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -enum DISCOVERY_MAPPING -{ - DISCOVERY_MAPPING_ID = 0, - DISCOVERY_MAPPING_NAME = 1, - DISCOVERY_MAPPING_COMMAND_PORT = 2, - DISCOVERY_MAPPING_RELAY_COUNT = 3, -}; - -int -discovery_socket_open(uint16_t discovery_port) -{ - struct addrinfo hints, *res; - int fd, status; - - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_INET; // use ipv4 - hints.ai_socktype = SOCK_DGRAM; //set socket flag - hints.ai_flags = AI_PASSIVE; // get my IP - - char* discovery_port_str = malloc(6 * sizeof(char)); - sprintf(discovery_port_str, "%u", discovery_port); - - //get connection info for our computer - if ((status = getaddrinfo(NULL, discovery_port_str, &hints, &res)) != 0) - { - LOG_FATAL("getaddrinfo: %s", gai_strerror(status)); - freeaddrinfo(res); - exit(EXIT_FAILURE); - } - - //creating socket - fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - int yes = 1; - - // lose the pesky "Address already in use" error message - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) - { - LOG_FATAL("setsockopt: %s", strerror(errno)); - freeaddrinfo(res); - exit(EXIT_FAILURE); - } - - if (bind(fd, res->ai_addr, res->ai_addrlen) == -1) - { - LOG_FATAL("bind: %s", strerror(errno)); - freeaddrinfo(res); - exit(EXIT_FAILURE); - } - - freeaddrinfo(res); - - LOG_DEBUG("opened discovery socket on port %u", discovery_port); - - return fd; -} - -void -discovery_handle_discover(int fd, controller *cntrlr) -{ - ssize_t bytes_transferred; - uint16_t discovery_answer_port; - struct sockaddr_in si_other; - socklen_t slen = sizeof(si_other); - - if((bytes_transferred = recvfrom(fd, &discovery_answer_port, sizeof(discovery_answer_port), 0, (struct sockaddr *) &si_other, &slen)) <= 0) - { - LOG_ERROR("received invalid discovery from %s", inet_ntoa(si_other.sin_addr)); - return; - } - LOG_DEBUG("received discovery from %s for port %d", inet_ntoa(si_other.sin_addr), discovery_answer_port); - - if(discovery_answer_port == 0) - { - LOG_ERROR("invalid port received"); - return; - } - - binn *map = binn_map(); - - binn_map_set_blob(map, DISCOVERY_MAPPING_ID, &cntrlr->id, sizeof(uuid_t)); - binn_map_set_str(map, DISCOVERY_MAPPING_NAME, cntrlr->name); - binn_map_set_uint32(map, DISCOVERY_MAPPING_COMMAND_PORT, cntrlr->command_port); - binn_map_set_uint8(map, DISCOVERY_MAPPING_RELAY_COUNT, cntrlr->relay_count); - - void *payload = binn_ptr(map); - size_t payload_size = binn_size(map); - - char discover_answer_port_str[6]; - sprintf(discover_answer_port_str, "%d", discovery_answer_port); - int fd_answer = helper_connect_server(inet_ntoa(si_other.sin_addr), discover_answer_port_str); - - LOG_DEBUG("size: %ld (%ld)", payload_size, sizeof(payload_size)); - - if((bytes_transferred = send(fd_answer, &payload_size, sizeof(payload_size), 0)) <= 0) - { - LOG_ERROR("error during sending"); - binn_free(map); - return; - } - if((bytes_transferred = send(fd_answer, payload, payload_size, 0)) <= 0) - { - LOG_ERROR("error during sending"); - binn_free(map); - return; - } - - close(fd_answer); -} diff --git a/drivers/gpio.c b/drivers/gpio.c new file mode 100644 index 0000000..fdff05e --- /dev/null +++ b/drivers/gpio.c @@ -0,0 +1,11 @@ +#include +#include +#include + +#include + +void +driver_gpio_set(relay_t *relay, int value) +{ + digitalWrite(relay->number, value); +} diff --git a/drivers/piface.c b/drivers/piface.c new file mode 100644 index 0000000..2cea3bb --- /dev/null +++ b/drivers/piface.c @@ -0,0 +1,11 @@ +#include +#include +#include + +#include + +void +driver_piface_set(relay_t *relay, int value) +{ + digitalWrite(DRIVER_PIFACE_GPIO_BASE + relay->number, value); +} diff --git a/handlers/command.c b/handlers/command.c new file mode 100644 index 0000000..3335445 --- /dev/null +++ b/handlers/command.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static void +handler_command_set_schedule(binn *map, controller_t *controller) +{ + uint8_t relay_num = binn_map_uint8(map, COMMAND_MAPPING_RELAY_NUM); + int uuid_size = sizeof(uuid_t); + uuid_t schedule_id; + memmove(schedule_id, binn_map_blob(map, COMMAND_MAPPING_SCHEDULE_ID, &uuid_size), uuid_size); + uint16_t periods_count = binn_map_uint16(map, COMMAND_MAPPING_PERIODS_COUNT); + int periods_size = sizeof(uint16_t) * (periods_count * 2); + uint16_t *periods = binn_map_blob(map, COMMAND_MAPPING_PERIODS_BLOB, &periods_size); + + relay_t *target_relay = controller->relays[relay_num]; + + if(target_relay->schedule) + { + schedule_free(target_relay->schedule); + } + target_relay->schedule = schedule_create(schedule_id, periods_count, periods); +} + +static void +handler_command_set_relay_name(binn *map, controller_t *controller) +{ + uint8_t relay_num = binn_map_uint8(map, COMMAND_MAPPING_RELAY_NUM); + char *relay_name = binn_map_str(map, COMMAND_MAPPING_NAME); + + if(relay_num < controller->relay_count) + { + relay_set_name(controller->relays[relay_num], relay_name); + } +} + +void +handler_command(int fd, controller_t *controller) +{ + struct sockaddr_storage their_addr; + socklen_t addr_size; + int client_fd; + + addr_size = sizeof(their_addr); + + if((client_fd = accept(fd, (struct sockaddr *) &their_addr, &addr_size)) < 0) + { + LOG_ERROR("could not accept client: %s", strerror(errno)); + return; + } + + size_t payload_length; + + if(recv(client_fd, &payload_length, sizeof(payload_length), 0) <= 0) + { + LOG_ERROR("unable to receive header: %s", strerror(errno)); + return; + } + + void *payload = malloc((payload_length + 1)); + ssize_t bytes_transferred; + + if((bytes_transferred = recv(client_fd, payload, payload_length, 0)) <= 0) + { + LOG_ERROR("unable to receive payload: %s", strerror(errno)); + return; + } + + uint8_t command_code = binn_map_uint8(payload, COMMAND_MAPPING_CODE); + + LOG_INFO("received command %d", command_code); + + switch(command_code) + { + case COMMAND_CODE_GET_TIME: + break; + case COMMAND_CODE_GET_ID: + break; + case COMMAND_CODE_SET_NAME: + controller_set_name(controller, binn_map_str(payload, COMMAND_MAPPING_NAME)); + break; + case COMMAND_CODE_GET_NAME: + break; + case COMMAND_CODE_SET_SCHEDULE: + handler_command_set_schedule(payload, controller); + break; + case COMMAND_CODE_GET_SCHEDULE: + break; + case COMMAND_CODE_SET_RELAY_NAME: + handler_command_set_relay_name(payload, controller); + break; + case COMMAND_CODE_GET_RELAY_NAME: + break; + } + + free(payload); + close(client_fd); +} diff --git a/handlers/discovery.c b/handlers/discovery.c new file mode 100644 index 0000000..03decfa --- /dev/null +++ b/handlers/discovery.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void +handler_discovery(int fd, controller_t *controller) +{ + ssize_t bytes_transferred; + uint16_t discovery_answer_port; + struct sockaddr_in si_other; + socklen_t slen = sizeof(si_other); + + if((bytes_transferred = recvfrom(fd, &discovery_answer_port, sizeof(discovery_answer_port), 0, (struct sockaddr *) &si_other, &slen)) <= 0) + { + LOG_ERROR("received invalid discovery from %s", inet_ntoa(si_other.sin_addr)); + return; + } + LOG_DEBUG("received discovery from %s for port %d", inet_ntoa(si_other.sin_addr), discovery_answer_port); + + if(discovery_answer_port == 0) + { + LOG_ERROR("invalid port received"); + return; + } + + binn *map = binn_map(); + + binn_map_set_blob(map, DISCOVERY_MAPPING_ID, &controller->id, sizeof(uuid_t)); + binn_map_set_str(map, DISCOVERY_MAPPING_NAME, controller->name); + binn_map_set_uint32(map, DISCOVERY_MAPPING_COMMAND_PORT, controller->command_port); + binn_map_set_uint8(map, DISCOVERY_MAPPING_RELAY_COUNT, controller->relay_count); + + void *payload = binn_ptr(map); + size_t payload_size = binn_size(map); + + int fd_answer = helper_connect_tcp_server(inet_ntoa(si_other.sin_addr), discovery_answer_port); + if(fd_answer == -1) + { + LOG_ERROR("error during connecting"); + binn_free(map); + return; + } + + if((bytes_transferred = send(fd_answer, &payload_size, sizeof(payload_size), 0)) <= 0) + { + LOG_ERROR("error during sending"); + binn_free(map); + return; + } + if((bytes_transferred = send(fd_answer, payload, payload_size, 0)) <= 0) + { + LOG_ERROR("error during sending"); + binn_free(map); + return; + } + + close(fd_answer); +} diff --git a/helpers/bind_server.c b/helpers/bind_server.c new file mode 100644 index 0000000..3d08ec6 --- /dev/null +++ b/helpers/bind_server.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +int +helper_bind_tcp_server(char* addr, uint16_t port, int max_client_backlog) +{ + char port_str[6]; + sprintf(port_str, "%d", port); + + struct addrinfo hints, *res; + int fd; + int status; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + if ((status = getaddrinfo(addr, port_str, &hints, &res)) != 0) + { + LOG_ERROR("getaddrinfo: %s", gai_strerror(status)); + return -1; + } + + fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + + if ((status = bind(fd, res->ai_addr, res->ai_addrlen)) == -1) + { + LOG_ERROR("error binding socket: %s", strerror(errno)); + freeaddrinfo(res); + return -1; + } + + if ((status = listen(fd, max_client_backlog)) == -1) + { + LOG_ERROR("error setting up listener: %s", strerror(errno)); + freeaddrinfo(res); + return -1; + } + + freeaddrinfo(res); + + return fd; +} diff --git a/helpers/connect_server.c b/helpers/connect_server.c index 8cf67ca..615056a 100644 --- a/helpers/connect_server.c +++ b/helpers/connect_server.c @@ -5,33 +5,35 @@ #include #include -#include +#include int -helper_connect_server(char* host, char* port) +helper_connect_tcp_server(char* host, uint16_t port) { - int s, status; - struct addrinfo hints, *res; - memset(&hints, 0, sizeof hints); - hints.ai_family = AF_INET; //set IP Protocol flag (IPv4 or IPv6 - we don't care) - hints.ai_socktype = SOCK_STREAM; //set socket flag + char port_str[6]; + sprintf(port_str, "%d", port); - if ((status = getaddrinfo(host, port, &hints, &res)) != 0) { //getaddrinfo() will evaluate the given address, using the hints-flags and port, and return an IP address and other server infos - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); - exit(EXIT_FAILURE); - } + int s, status; + struct addrinfo hints, *res; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; //set IP Protocol flag (IPv4 or IPv6 - we don't care) + hints.ai_socktype = SOCK_STREAM; //set socket flag - //res got filled out by getaddrinfo() for us - s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //creating Socket + if ((status = getaddrinfo(host, port_str, &hints, &res)) != 0) { //getaddrinfo() will evaluate the given address, using the hints-flags and port, and return an IP address and other server infos + LOG_ERROR("getaddrinfo: %s\n", gai_strerror(status)); + return -1; + } - if ((status = connect(s, res->ai_addr, res->ai_addrlen)) != 0) { - fprintf(stderr, "Keine Verbindung mit dem Netzwerk möglich.\n"); - freeaddrinfo(res); - exit(EXIT_FAILURE); - } + //res got filled out by getaddrinfo() for us + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //creating Socket - freeaddrinfo(res); + if ((status = connect(s, res->ai_addr, res->ai_addrlen)) != 0) { + LOG_ERROR("connect() failed"); + freeaddrinfo(res); + return -1; + } - return s; + freeaddrinfo(res); + + return s; } - diff --git a/helpers/get_port.c b/helpers/get_port.c new file mode 100644 index 0000000..edcd8a4 --- /dev/null +++ b/helpers/get_port.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +#include +#include + +uint16_t +helper_get_port(int sock) +{ + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + if (getsockname(sock, (struct sockaddr *)&sin, &len) == -1) + { + LOG_ERROR("could not get socket name for port: %s", strerror(errno)); + return 0; + } + else + { + return ntohs(sin.sin_port); + } +} diff --git a/helpers/open_discovery_socket.c b/helpers/open_discovery_socket.c new file mode 100644 index 0000000..0b0db89 --- /dev/null +++ b/helpers/open_discovery_socket.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +int +helper_open_discovery_socket(uint16_t discovery_port) +{ + struct addrinfo hints, *res; + int fd, status; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_INET; // use ipv4 + hints.ai_socktype = SOCK_DGRAM; //set socket flag + hints.ai_flags = AI_PASSIVE; // get my IP + + char discovery_port_str[6]; + sprintf(discovery_port_str, "%u", discovery_port); + + //get connection info for our computer + if ((status = getaddrinfo(NULL, discovery_port_str, &hints, &res)) != 0) + { + LOG_FATAL("getaddrinfo: %s", gai_strerror(status)); + freeaddrinfo(res); + exit(EXIT_FAILURE); + } + + //creating socket + fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + int yes = 1; + + // lose the pesky "Address already in use" error message + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) + { + LOG_FATAL("setsockopt: %s", strerror(errno)); + freeaddrinfo(res); + exit(EXIT_FAILURE); + } + + if (bind(fd, res->ai_addr, res->ai_addrlen) == -1) + { + LOG_FATAL("bind: %s", strerror(errno)); + freeaddrinfo(res); + exit(EXIT_FAILURE); + } + + freeaddrinfo(res); + + LOG_INFO("opened discovery socket on port %u", discovery_port); + + return fd; +} diff --git a/include/config.h b/include/config.h index de9c0fb..5f4c031 100644 --- a/include/config.h +++ b/include/config.h @@ -4,11 +4,11 @@ #include /** - * @brief Limit the maximum length of a controller name + * @brief Limit the maximum length of a controller/relay/etc name * - * The NULL terminator is not included. Arrays of length #CONTROLLER_NAME_LENGTH + 1 are required. + * The NULL terminator is not included. Arrays of length #MAX_NAME_LENGTH + 1 are required. */ -#define CONTROLLER_NAME_LENGTH 128 +#define MAX_NAME_LENGTH 128 /** * @brief Maximum number of dbs for the databases for the MDB_env diff --git a/include/constants.h b/include/constants.h new file mode 100644 index 0000000..5bfcb5b --- /dev/null +++ b/include/constants.h @@ -0,0 +1,3 @@ +#define SECONDS_PER_DAY 86400 // 60 * 60 * 24 + +#define SECONDS_PER_MINUTE 60 diff --git a/include/discovery.h b/include/discovery.h deleted file mode 100644 index 0e50f15..0000000 --- a/include/discovery.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CONTROLLER_DISCOVERY_H -#define CONTROLLER_DISCOVERY_H - -#include - -/** - * @brief Open socket for discovery - * - * Will exit program when unable to open socket. - * - * @param discovery_port Port number to listen on for discovery broadcasts - * - * @return Open socket to accept discovery broadcasts on - */ -int -discovery_socket_open(uint16_t discovery_port); - -/** - * @brief Handle the discovery processing - * - * @param fd File descriptor to receive initial data from - * @param cntrlr Controller to use for answering discovery - */ -void -discovery_handle_discover(int fd, controller *cntrlr); - -#endif /* CONTROLLER_DISCOVERY_H */ diff --git a/include/drivers.h b/include/drivers.h new file mode 100644 index 0000000..b33c140 --- /dev/null +++ b/include/drivers.h @@ -0,0 +1,14 @@ +#ifndef CONTROLLER_DRIVERS_H +#define CONTROLLER_DRIVERS_H + +#include + +#define DRIVER_PIFACE_GPIO_BASE 200 + +void +driver_piface_set(relay_t *relay, int value); + +void +driver_gpio_set(relay_t *relay, int value); + +#endif /* CONTROLLER_DRIVERS_H */ diff --git a/include/enums.h b/include/enums.h index 34d956f..9a03afe 100644 --- a/include/enums.h +++ b/include/enums.h @@ -1,8 +1,40 @@ #ifndef CONTROLLER_ENUMS_H #define CONTROLLER_ENUMS_H -enum poll_fgs { - POLL_FGS_DISCOVERY +enum poll_fgs +{ + POLL_FGS_DISCOVERY, + POLL_FGS_COMMAND +}; + +enum discovery_mapping +{ + DISCOVERY_MAPPING_ID = 0, + DISCOVERY_MAPPING_NAME = 1, + DISCOVERY_MAPPING_COMMAND_PORT = 2, + DISCOVERY_MAPPING_RELAY_COUNT = 3, +}; + +enum control_mapping +{ + COMMAND_MAPPING_CODE = 0, + COMMAND_MAPPING_NAME = 1, + COMMAND_MAPPING_RELAY_NUM = 2, + COMMAND_MAPPING_SCHEDULE_ID = 3, + COMMAND_MAPPING_PERIODS_COUNT = 4, + COMMAND_MAPPING_PERIODS_BLOB = 5, +}; + +enum command_code +{ + COMMAND_CODE_GET_TIME = 1, + COMMAND_CODE_GET_ID = 2, + COMMAND_CODE_SET_NAME = 100, + COMMAND_CODE_GET_NAME = 101, + COMMAND_CODE_SET_SCHEDULE = 102, + COMMAND_CODE_GET_SCHEDULE = 103, + COMMAND_CODE_SET_RELAY_NAME = 104, + COMMAND_CODE_GET_RELAY_NAME = 105, }; #endif /* CONTROLLER_ENUMS_H */ diff --git a/include/handlers.h b/include/handlers.h new file mode 100644 index 0000000..0b52d64 --- /dev/null +++ b/include/handlers.h @@ -0,0 +1,24 @@ +#ifndef CONTROLLER_HANDLERS_H +#define CONTROLLER_HANDLERS_H + +#include + +/** + * @brief Handle the command processing + * + * @param fd File descriptor to receive initial data from + * @param controller Controller to use for answering command + */ +void +handler_command(int fd, controller_t *controller); + +/** + * @brief Handle the discovery processing + * + * @param fd File descriptor to receive initial data from + * @param controller Controller to use for answering discovery + */ +void +handler_discovery(int fd, controller_t *controller); + +#endif /* CONTROLLER_HANDLERS_H */ diff --git a/include/helper.h b/include/helper.h deleted file mode 100644 index aa2a9f3..0000000 --- a/include/helper.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef CONTROLLER_HELPER_H -#define CONTROLLER_HELPER_H - -int -helper_connect_server(char* host, char* port); - -#endif /* CONTROLLER_HELPER_H */ diff --git a/include/helpers.h b/include/helpers.h new file mode 100644 index 0000000..b1bd8a5 --- /dev/null +++ b/include/helpers.h @@ -0,0 +1,25 @@ +#ifndef CONTROLLER_HELPERS_H +#define CONTROLLER_HELPERS_H + +int +helper_connect_tcp_server(char* host, uint16_t port); + +int +helper_bind_tcp_server(char* addr, uint16_t port, int max_client_backlog); + +uint16_t +helper_get_port(int sock); + +/** + * @brief Open socket for discovery + * + * Will exit program when unable to open socket. + * + * @param discovery_port Port number to listen on for discovery broadcasts + * + * @return Open socket to accept discovery broadcasts on + */ +int +helper_open_discovery_socket(uint16_t discovery_port); + +#endif /* CONTROLLER_HELPERS_H */ diff --git a/include/logger.h b/include/logger.h index d648fef..2573ec7 100644 --- a/include/logger.h +++ b/include/logger.h @@ -2,45 +2,52 @@ #define CONTROLLER_LOGGER_H #include +#include #include #include #include -#define _LOGGER_MESSAGE(msg) COLOR_NONE " %s:%s:%d: " msg "\n", __FILENAME__, __func__, __LINE__ +#define _LOGGER_TIMESTAMP_SIZE 32 +char _LOGGER_TIMESTAMP[_LOGGER_TIMESTAMP_SIZE]; + +char* +logger_get_timestamp(); + +#define _LOGGER_MESSAGE(msg) COLOR_NONE " %s %s:%d:%s: " msg "\n", logger_get_timestamp(), __FILENAME__, __LINE__, __func__ #if LOG_LEVEL >= LOG_LEVEL_TRACE - #define LOG_TRACE(msg, ...) printf(COLOR_GREEN "[TRACE]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) + #define LOG_TRACE(msg, ...) fprintf(stdout, COLOR_GREEN "[TRACE]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) #else #define LOG_TRACE(msg, ...) #endif #if LOG_LEVEL >= LOG_LEVEL_DEBUG - #define LOG_DEBUG(msg, ...) printf(COLOR_BLUE "[DEBUG]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) + #define LOG_DEBUG(msg, ...) fprintf(stdout, COLOR_BLUE "[DEBUG]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) #else #define LOG_DEBUG(msg, ...) #endif #if LOG_LEVEL >= LOG_LEVEL_INFO - #define LOG_INFO(msg, ...) printf(COLOR_CYAN "[ INFO] " _LOGGER_MESSAGE(msg), ##__VA_ARGS__) + #define LOG_INFO(msg, ...) fprintf(stdout, COLOR_CYAN "[ INFO]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) #else #define LOG_INFO(msg, ...) #endif #if LOG_LEVEL >= LOG_LEVEL_WARN - #define LOG_WARN(msg, ...) printf(COLOR_YELLOW "[ WARN] " _LOGGER_MESSAGE(msg), ##__VA_ARGS__) + #define LOG_WARN(msg, ...) fprintf(stdout, COLOR_YELLOW "[ WARN]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) #else #define LOG_WARN(msg, ...) #endif #if LOG_LEVEL >= LOG_LEVEL_ERROR - #define LOG_ERROR(msg, ...) printf(COLOR_RED "[ERROR]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) + #define LOG_ERROR(msg, ...) fprintf(stderr, COLOR_RED "[ERROR]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) #else #define LOG_ERROR(msg, ...) #endif #if LOG_LEVEL >= LOG_LEVEL_FATAL - #define LOG_FATAL(msg, ...) printf(COLOR_MAGENTA "[FATAL]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) + #define LOG_FATAL(msg, ...) fprintf(stderr, COLOR_MAGENTA "[FATAL]" _LOGGER_MESSAGE(msg), ##__VA_ARGS__) #else #define LOG_FATAL(msg, ...) #endif diff --git a/include/models/controller.h b/include/models/controller.h index 0a581a8..17f3d08 100644 --- a/include/models/controller.h +++ b/include/models/controller.h @@ -11,7 +11,7 @@ /** * @brief Information about this controller */ -typedef struct controller +typedef struct { /** * @brief A unique UUID for this controller @@ -22,7 +22,7 @@ typedef struct controller * * Includes a \0 terminator. */ - char name[CONTROLLER_NAME_LENGTH + 1]; + char name[MAX_NAME_LENGTH + 1]; /** * @brief The command port the controller was bound to */ @@ -35,22 +35,22 @@ typedef struct controller * @brief Amount of relays available to this controller */ uint8_t relay_count; - relay **relays; + relay_t **relays; -} controller; +} controller_t; /** * @brief Key to save controller information in database */ -typedef enum controller_db_key +typedef enum { - KEY_META_ID = 0, - KEY_META_NAME = 1, - KEY_META_COMMAND_PORT = 2, - KEY_META_DISCOVERY_PORT = 3, - KEY_META_RELAY_COUNT = 4, - KEY_META_RELAYS = 5, -} controller_db_key; + DB_KEY_CONTROLLER_ID = 0, + DB_KEY_CONTROLLER_NAME = 1, + DB_KEY_CONTROLLER_COMMAND_PORT = 2, + DB_KEY_CONTROLLER_DISCOVERY_PORT = 3, + DB_KEY_CONTROLLER_RELAY_COUNT = 4, + DB_KEY_CONTROLLER_RELAYS = 5, +} db_key_controller_e; /** * @brief Create a new instance of controller @@ -59,7 +59,7 @@ typedef enum controller_db_key * * @return A new instance of #controller */ -controller* +controller_t* controller_create(void); @@ -72,20 +72,32 @@ controller_create(void); * * @return A loaded or new instance of controller or NULL on database error */ -controller* +controller_t* controller_load(MDB_env *mdb_env); /** * @brief Save a controller to the database * - * @param cntrlr Instance of a controller + * @param controller Instance of a controller * @param mdb_env Already created MDB_env * * @return Indicator to show success (0) or failure (!0) */ int -controller_save(controller *cntrlr, MDB_env *mdb_env); +controller_save(controller_t *controller, MDB_env *mdb_env); +/** + * @brief Sets a name to a controller. + * This function won't perform any checks (e.g. no NULL checks) + * + * @param controller Set the name to this controller + * @param name Name to be set + */ +void +controller_set_name(controller_t *controller, char *name); + +void +controller_free(controller_t *controller); /** * @brief Debug an instance of #controller @@ -95,6 +107,6 @@ controller_save(controller *cntrlr, MDB_env *mdb_env); * @param cntrlr #controller to debug */ void -controller_debug(controller *cntrlr); +controller_debug(controller_t *controller); #endif //CONTROLLER_CONTROLLER_H diff --git a/include/models/period.h b/include/models/period.h new file mode 100644 index 0000000..38a7249 --- /dev/null +++ b/include/models/period.h @@ -0,0 +1,19 @@ +#ifndef CONTROLLER_PERIOD_H +#define CONTROLLER_PERIOD_H + +#include +#include + +typedef struct +{ + uint16_t start; + uint16_t end; +} period_t; + +period_t* +period_create(uint16_t start, uint16_t end); + +int +period_includes_time(period_t *period, uint16_t timestamp); + +#endif /* CONTROLLER_PERIOD_H */ diff --git a/include/models/relay.h b/include/models/relay.h index 2d8b710..8ce1a6f 100644 --- a/include/models/relay.h +++ b/include/models/relay.h @@ -2,16 +2,63 @@ #define CONTROLLER_RELAY_H #include +#include +#include #include +#include -typedef struct relay { - uint8_t index; - char name[128]; - uint16_t *schedule; -} relay; +typedef struct +{ + uint8_t number; + char name[MAX_NAME_LENGTH + 1]; + schedule_t *schedule; +} relay_t; -relay* -relay_init(uint8_t index); +/** + * @brief Key to save relay information in database + */ +typedef enum +{ + DB_KEY_RELAY_NAME = 0, + DB_KEY_RELAY_SCHEDULE_ID = 1, + DB_KEY_RELAY_SCHEDULE_PERIODS = 2, +} db_key_relay_e; + +relay_t* +relay_create(uint8_t number); + +void +relay_set_name(relay_t *relay, char *name); + +/** + * @brief Load a relay for database or create a new one + * + * @param mdb_env An opened MDB_env to load from + * + * @return A loaded or new instance of relay + */ +relay_t* +relay_load(MDB_env *mdb_env, uint8_t num); + +/** + * @brief Save a relay to the database + * + * @param relay Instance of a relay + * @param mdb_env Already created MDB_env + * + * @return Indicator to show success (0) or failure (!0) + */ +int +relay_save(relay_t *relay, MDB_env *mdb_env); + +int +relay_is_active(relay_t *relay, time_t timestamp_now); + +void +relay_free(relay_t *relay); + +void +relay_debug(relay_t *relay); #endif //CONTROLLER_RELAY_H diff --git a/include/models/schedule.h b/include/models/schedule.h new file mode 100644 index 0000000..15274d4 --- /dev/null +++ b/include/models/schedule.h @@ -0,0 +1,28 @@ +#ifndef CONTROLLER_SCHEDULE_H +#define CONTROLLER_SCHEDULE_H + +#include +#include + +#include + +typedef struct +{ + uuid_t id; + uint16_t length; + period_t **periods; +} schedule_t; + +schedule_t* +schedule_create(uuid_t id, uint16_t length, uint16_t *periods_blob); + +uint16_t* +schedule_periods_to_blob(schedule_t *schedule); + +void +schedule_free(schedule_t *schedule); + +void +schedule_debug(schedule_t *schedule); + +#endif /* CONTROLLER_SCHEDULE_H */ diff --git a/include/wiring_debug.h b/include/wiring_debug.h index ce9f750..290ede0 100644 --- a/include/wiring_debug.h +++ b/include/wiring_debug.h @@ -1,10 +1,15 @@ #ifndef CONTROLLER_WIRING_DEBUG_H #define CONTROLLER_WIRING_DEBUG_H +#include + #ifdef WIRING_PI_DEBUG - #define wiringPiSetup() LOG_INFO("wiringP wiringPiSetup()") + #define wiringPiSetup() LOG_INFO("wiringPi wiringPiSetup()") + #define wiringPiSetupSys() LOG_INFO("wiringPi wiringPiSetupSys()") #define pinMode(x,y) LOG_INFO("wiringPi pinMode(%d, %d)", x, y) #define digitalWrite(x,y) LOG_INFO("wiringPi digitalWrite(%d, %d)", x, y) + + #define piFaceSetup(x) LOG_INFO("wiringPi piFaceSetup(%d)", x) #endif #endif /* CONTROLLER_WIRING_DEBUG_H */ diff --git a/logger.c b/logger.c new file mode 100644 index 0000000..d7c8136 --- /dev/null +++ b/logger.c @@ -0,0 +1,10 @@ +#include + +char* +logger_get_timestamp() +{ + time_t rawtime; + time(&rawtime); + strftime(_LOGGER_TIMESTAMP, _LOGGER_TIMESTAMP_SIZE, "%Y-%m-%d %H:%M:%S", localtime(&rawtime)); + return _LOGGER_TIMESTAMP; +} diff --git a/main.c b/main.c index 9934e67..7d8e642 100644 --- a/main.c +++ b/main.c @@ -1,17 +1,72 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include #include -#include +#include +#include #include +#include +#include +#include +#include + +static MDB_env *mdb_env; +static int fd_discovery; +static int fd_command; +static controller_t *this_controller; + +static void +terminate(int signum) +{ + LOG_INFO("terminating controller (%d)", signum); + + close(fd_discovery); + close(fd_command); + + mdb_env_close(mdb_env); + + controller_free(this_controller); + + exit(signum); +} + +static void +handle_poll(struct pollfd *fds, int fd_count) +{ + /* An event on one of the fds has occurred. */ + for(int i = 0; i < fd_count; i++) { + if(fds[i].revents & POLLIN) + { + /* data may be read on device number i. */ + LOG_DEBUG("fd %i may read data", fds[i].fd); + switch(i) + { + case POLL_FGS_DISCOVERY: + handler_discovery(fd_discovery, this_controller); + break; + case POLL_FGS_COMMAND: + handler_command(fd_command, this_controller); + controller_save(this_controller, mdb_env); + break; + } + } + if(fds[i].revents & POLLHUP) + { + /* A hangup has occurred on device number i. */ + LOG_DEBUG("fd %i got closed", fds[i].fd); + } + } +} /** * @brief The main function @@ -27,18 +82,40 @@ main(int argc, char** argv) (void)argc; (void)argv; - MDB_env *mdb_env; + signal(SIGINT, terminate); + signal(SIGABRT, terminate); + signal(SIGTERM, terminate); + + if(sizeof(time_t) < 8) + { + LOG_WARN("this system is not using 8-bit time"); + } + + /******************** SETUP DATABASE AND THIS CONTROLLER ********************/ + database_setup(&mdb_env); - controller *this_cntrlr = controller_load(mdb_env); - controller_save(this_cntrlr, mdb_env); + this_controller = controller_load(mdb_env); - int fd_discovery = discovery_socket_open(this_cntrlr->discovery_port); + fd_discovery = helper_open_discovery_socket(this_controller->discovery_port); + fd_command = helper_bind_tcp_server("0.0.0.0", this_controller->command_port, 128); + + this_controller->command_port = helper_get_port(fd_command); + + controller_save(this_controller, mdb_env); + + + /******************** SETUP WIRINGPI ********************/ + + wiringPiSetupSys(); + piFaceSetup(200); + + + /******************** SETUP SOCKETS ********************/ struct pollfd fds[2]; int timeout_msecs = ACCEPT_TIMEOUT_MSECONDS; int ret; - int i; int fd_count = 0; /* Open STREAMS device. */ @@ -46,6 +123,12 @@ main(int argc, char** argv) fds[POLL_FGS_DISCOVERY].events = POLLIN; LOG_DEBUG("setup fd_discovery as %i on index %i", fd_discovery, fd_count); fd_count++; + fds[POLL_FGS_COMMAND].fd = fd_command; + fds[POLL_FGS_COMMAND].events = POLLIN; + LOG_DEBUG("setup fd_command as %i on index %i", fd_command, fd_count); + fd_count++; + + /******************** START MAIN LOOP ********************/ while(1) { @@ -54,27 +137,37 @@ main(int argc, char** argv) if(ret == 0) { LOG_TRACE("idle loop"); + for(uint_fast8_t i = 0; i < this_controller->relay_count; ++i) + { + relay_t *relay = this_controller->relays[i]; + if(relay_is_active(relay, time(NULL))) + { + LOG_DEBUG("relay %d is active", i); + if(relay->number >= 2) + { + driver_gpio_set(relay, HIGH); + } + else + { + driver_piface_set(relay, HIGH); + } + } + else + { + if(relay->number >= 2) + { + driver_gpio_set(relay, LOW); + } + else + { + driver_piface_set(relay, LOW); + } + } + } } if(ret > 0) { - /* An event on one of the fds has occurred. */ - for(i = 0; i < fd_count; i++) { - if(fds[i].revents & POLLIN) - { - /* Priority data may be read on device number i. */ - LOG_DEBUG("fd %i may read data", fds[i].fd); - switch(i) - { - case POLL_FGS_DISCOVERY: - discovery_handle_discover(fd_discovery, this_cntrlr); - } - } - if(fds[i].revents & POLLHUP) - { - /* A hangup has occurred on device number i. */ - LOG_DEBUG("fd %i got closed", fds[i].fd); - } - } + handle_poll(fds, fd_count); } } diff --git a/models/controller.c b/models/controller.c index 3b19de5..03a96d3 100644 --- a/models/controller.c +++ b/models/controller.c @@ -7,38 +7,62 @@ #include #include -controller* +controller_t* controller_create(void) { - controller *result = malloc(sizeof(*result)); - uuid_generate(result->id); + controller_t *new_controller = malloc(sizeof(*new_controller)); + uuid_generate(new_controller->id); - strcpy(result->name, "new emgauwa device"); - result->command_port = 0; - result->discovery_port = 4421; - result->relay_count = 10; + strcpy(new_controller->name, "new emgauwa device"); + new_controller->command_port = 0; + new_controller->discovery_port = 4421; + new_controller->relay_count = 10; - result->relays = malloc(sizeof(*result->relays) * result->relay_count); + new_controller->relays = malloc(sizeof(relay_t) * new_controller->relay_count); uint8_t i; - for(i = 0; i < result->relay_count; i++) + for(i = 0; i < new_controller->relay_count; ++i) { - result->relays[i] = relay_init(i); + new_controller->relays[i] = relay_create(i); } - return result; + return new_controller; } void -controller_debug(controller *cntrlr) +controller_set_name(controller_t *controller, char *name) { - if(cntrlr == NULL) + strncpy(controller->name, name, MAX_NAME_LENGTH); + controller->name[MAX_NAME_LENGTH] = '\0'; +} + +void +controller_free(controller_t *controller) +{ + for(int i = 0; i < controller->relay_count; ++i) + { + relay_free(controller->relays[i]); + } + free(controller->relays); + free(controller); +} + +void +controller_debug(controller_t *controller) +{ + if(controller == NULL) { LOG_DEBUG("controller is NULL"); + return; } char uuid_str[37]; - uuid_unparse(cntrlr->id, uuid_str); - LOG_DEBUG("(1/4) %s @ %p", uuid_str, cntrlr); - LOG_DEBUG("(2/4) name: %s", cntrlr->name); - LOG_DEBUG("(3/4) relays: %3d", cntrlr->relay_count); - LOG_DEBUG("(4/4) command_port: %5d discovery_port: %5d", cntrlr->command_port, cntrlr->discovery_port); + uuid_unparse(controller->id, uuid_str); + LOG_DEBUG("(1/5) %s @ %p", uuid_str, controller); + LOG_DEBUG("(2/5) name: %s", controller->name); + LOG_DEBUG("(3/5) command_port: %5d discovery_port: %5d", controller->command_port, controller->discovery_port); + LOG_DEBUG("(4/5) relay count: %3d", controller->relay_count); + LOG_DEBUG("(5/5) relays @ %p:", controller->relays); + for(uint8_t i = 0; i < controller->relay_count; ++i) + { + relay_debug(controller->relays[i]); + } } diff --git a/models/controller_load.c b/models/controller_load.c index 0660a0b..b6f862d 100644 --- a/models/controller_load.c +++ b/models/controller_load.c @@ -8,73 +8,79 @@ #include static void -controller_load_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, controller_db_key key_meta, MDB_val *value) +controller_load_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_controller_e key_controller, MDB_val *value) { int err; MDB_val key; - key.mv_size = sizeof(controller_db_key); - key.mv_data = &key_meta; + key.mv_size = sizeof(db_key_controller_e); + key.mv_data = &key_controller; if((err = mdb_get(mdb_txn, mdb_dbi, &key, value)) != 0) { - fprintf(stderr, "mdb_get error %s\n", mdb_strerror(err)); + LOG_ERROR("mdb_get error %s", mdb_strerror(err)); exit(1); } } -controller* +controller_t* controller_load(MDB_env *mdb_env) { int err; MDB_txn *mdb_txn; MDB_dbi mdb_dbi; - controller *new_controller; + controller_t *new_controller; if((err = mdb_txn_begin(mdb_env, NULL, MDB_RDONLY, &mdb_txn)) != 0) { - fprintf(stderr, "mdb_txn_begin error %s\n", mdb_strerror(err)); + LOG_ERROR("mdb_txn_begin error %s", mdb_strerror(err)); return NULL; } - if((err = mdb_dbi_open(mdb_txn, "meta", 0, &mdb_dbi)) != 0) + if((err = mdb_dbi_open(mdb_txn, "controller", 0, &mdb_dbi)) != 0) { switch(err) { case MDB_NOTFOUND: + LOG_INFO("no controller found in db. creating new one"); mdb_txn_abort(mdb_txn); - new_controller = controller_create(); + new_controller = controller_create(); controller_save(new_controller, mdb_env); return new_controller; default: - fprintf(stderr, "mdb_txn_begin error %s\n", mdb_strerror(err)); + LOG_ERROR("mdb_txn_begin error %s", mdb_strerror(err)); return NULL; } } - new_controller = malloc(sizeof(*new_controller)); + new_controller = malloc(sizeof(controller_t)); MDB_val value; - controller_load_single(mdb_txn, mdb_dbi, KEY_META_ID, &value); + controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_ID, &value); memmove(new_controller->id, (uuid_t*)value.mv_data, sizeof(uuid_t)); - controller_load_single(mdb_txn, mdb_dbi, KEY_META_NAME, &value); - strncpy(new_controller->name, (char*)value.mv_data, CONTROLLER_NAME_LENGTH); - new_controller->name[CONTROLLER_NAME_LENGTH] = '\0'; + controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_NAME, &value); + strncpy(new_controller->name, (char*)value.mv_data, MAX_NAME_LENGTH); + new_controller->name[MAX_NAME_LENGTH] = '\0'; - controller_load_single(mdb_txn, mdb_dbi, KEY_META_COMMAND_PORT, &value); + controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_COMMAND_PORT, &value); new_controller->command_port = ((uint16_t*)value.mv_data)[0]; - controller_load_single(mdb_txn, mdb_dbi, KEY_META_DISCOVERY_PORT, &value); + controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_DISCOVERY_PORT, &value); new_controller->discovery_port = ((uint16_t*)value.mv_data)[0]; - controller_load_single(mdb_txn, mdb_dbi, KEY_META_RELAY_COUNT, &value); + controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_RELAY_COUNT, &value); new_controller->relay_count = ((uint8_t*)value.mv_data)[0]; - controller_debug(new_controller); - mdb_txn_abort(mdb_txn); // transaction is read only + + new_controller->relays = malloc(sizeof(relay_t*) * new_controller->relay_count); + for(uint8_t i = 0; i < new_controller->relay_count; i++) + { + LOG_TRACE("loading relay %d", i); + new_controller->relays[i] = relay_load(mdb_env, i); + } return new_controller; } diff --git a/models/controller_save.c b/models/controller_save.c index b99c8a9..60881d3 100644 --- a/models/controller_save.c +++ b/models/controller_save.c @@ -8,17 +8,17 @@ #include int -controller_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, controller_db_key key_meta, MDB_val value) +controller_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_controller_e key_controller, MDB_val value) { int err; MDB_val key; - key.mv_size = sizeof(controller_db_key); - key.mv_data = &key_meta; + key.mv_size = sizeof(db_key_controller_e); + key.mv_data = &key_controller; if((err = mdb_put(mdb_txn, mdb_dbi, &key, &value, 0)) != 0) { - fprintf(stderr, "mdb_put error %s\n", mdb_strerror(err)); + LOG_ERROR("mdb_put error %s", mdb_strerror(err)); mdb_txn_abort(mdb_txn); return 1; } @@ -26,7 +26,7 @@ controller_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, controller_db_key key_ } int -controller_save(controller *cntrlr, MDB_env *mdb_env) +controller_save(controller_t *controller, MDB_env *mdb_env) { int err; @@ -34,56 +34,65 @@ controller_save(controller *cntrlr, MDB_env *mdb_env) MDB_dbi mdb_dbi; MDB_val value; - controller_debug(cntrlr); - if((err = mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn)) != 0) { - fprintf(stderr, "mdb_txn_begin error %s\n", mdb_strerror(err)); + LOG_ERROR("mdb_txn_begin error %s", mdb_strerror(err)); exit(1); } - if((err = mdb_dbi_open(mdb_txn, "meta", MDB_CREATE, &mdb_dbi)) != 0) + if((err = mdb_dbi_open(mdb_txn, "controller", MDB_CREATE, &mdb_dbi)) != 0) { - fprintf(stderr, "mdb_dbi_open error %s\n", mdb_strerror(err)); + LOG_ERROR("mdb_dbi_open error %s", mdb_strerror(err)); exit(1); } - value.mv_size = sizeof(cntrlr->id); - value.mv_data = cntrlr->id; - if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_ID, value)) + value.mv_size = sizeof(uuid_t); + value.mv_data = controller->id; + if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_ID, value)) { + LOG_ERROR("failed to save ID"); return 1; } - value.mv_size = sizeof(char) * (strlen(cntrlr->name) + 1); - value.mv_data = cntrlr->name; - if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_NAME, value)) + value.mv_size = sizeof(char) * (strlen(controller->name) + 1); + value.mv_data = controller->name; + if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_NAME, value)) { + LOG_ERROR("failed to save name"); return 1; } - value.mv_size = sizeof(cntrlr->command_port); - value.mv_data = &cntrlr->command_port; - if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_COMMAND_PORT, value)) + value.mv_size = sizeof(controller->command_port); + value.mv_data = &controller->command_port; + if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_COMMAND_PORT, value)) { + LOG_ERROR("failed to save command port"); return 1; } - value.mv_size = sizeof(cntrlr->discovery_port); - value.mv_data = &cntrlr->discovery_port; - if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_DISCOVERY_PORT, value)) + value.mv_size = sizeof(controller->discovery_port); + value.mv_data = &controller->discovery_port; + if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_DISCOVERY_PORT, value)) { + LOG_ERROR("failed to save discovery port"); return 1; } - value.mv_size = sizeof(cntrlr->relay_count); - value.mv_data = &cntrlr->relay_count; - if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_RELAY_COUNT, value)) + value.mv_size = sizeof(controller->relay_count); + value.mv_data = &controller->relay_count; + if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_RELAY_COUNT, value)) { + LOG_ERROR("failed to save relay count"); return 1; } mdb_txn_commit(mdb_txn); + for(uint8_t i = 0; i < controller->relay_count; ++i) + { + LOG_TRACE("saving relays[%d/%d]", i, controller->relay_count); + relay_save(controller->relays[i], mdb_env); + } + return 0; } diff --git a/models/period.c b/models/period.c new file mode 100644 index 0000000..75a4981 --- /dev/null +++ b/models/period.c @@ -0,0 +1,44 @@ +#include + +#include +#include +#include + +period_t* +period_create(uint16_t start, uint16_t end) +{ + period_t *new_period = malloc(sizeof(period_t)); + new_period->start = start; + new_period->end = end; + + return new_period; +} + +int +period_includes_time(period_t *period, uint16_t timestamp) +{ + uint16_t start = period->start; + uint16_t end = period->end; + + // "normal" timespan + if(start < end) + { + if(start <= timestamp && end > timestamp) + { + return 1; + } + return 0; + } + + // timespan goes through 00:00 + if(end < start) + { + if(start >= timestamp && end < timestamp) + { + return 1; + } + return 0; + } + + return 0; +} diff --git a/models/relay.c b/models/relay.c index ce0a213..ed9b73b 100644 --- a/models/relay.c +++ b/models/relay.c @@ -1,10 +1,83 @@ #include +#include +#include +#include +#include #include -relay* -relay_init(uint8_t index) +relay_t* +relay_create(uint8_t number) { - (void)index; - return NULL; + relay_t *new_relay = malloc(sizeof(relay_t)); + + new_relay->number = number; + new_relay->name[0] = '\0'; + + uuid_t off_id; + memset(off_id, 0, sizeof(uuid_t)); + memcpy(off_id, "off", 3); + + new_relay->schedule = schedule_create(off_id, 0, NULL); + + return new_relay; +} + +void +relay_set_name(relay_t *relay, char *name) +{ + strncpy(relay->name, name, MAX_NAME_LENGTH); + relay->name[MAX_NAME_LENGTH] = '\0'; +} + +int +relay_is_active(relay_t *relay, time_t timestamp_now) +{ + if(relay->schedule->length == 0) + { + return 0; + } + + // we don't need days. reduce to hours, minutes and seconds + timestamp_now %= SECONDS_PER_DAY; + // finally remove seconds + timestamp_now /= SECONDS_PER_MINUTE; + + for(uint16_t i = 0; i < relay->schedule->length; ++i) + { + if(period_includes_time(relay->schedule->periods[i], timestamp_now)) + { + return 1; + } + } + return 0; +} +//struct tm time_start, time_now, time_end; +//localtime_r(×tamp_start, &time_start); +//localtime_r(×tamp_now, &time_now); +//localtime_r(×tamp_end, &time_end); +//LOG_DEBUG("%02d:%02d - %02d:%02d - %02d:%02d", time_start.tm_hour, time_start.tm_min, time_now.tm_hour, time_now.tm_min, time_end.tm_hour, time_end.tm_min); + +void +relay_debug(relay_t *relay) +{ + if(relay == NULL) + { + LOG_DEBUG("relay is NULL"); + return; + } + LOG_DEBUG("(1/3) %d @ %p", relay->number, relay); + LOG_DEBUG("(2/3) name: %s", relay->name); + LOG_DEBUG("(3/3) schedule:"); + schedule_debug(relay->schedule); +} + +void +relay_free(relay_t *relay) +{ + if(relay->schedule) + { + schedule_free(relay->schedule); + } + free(relay); } diff --git a/models/relay_load.c b/models/relay_load.c new file mode 100644 index 0000000..1d4a76d --- /dev/null +++ b/models/relay_load.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +#include +#include + +static int +relay_load_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_relay_e key_relay, uint8_t num, MDB_val *value) +{ + int err; + + size_t key_size = sizeof(db_key_relay_e) + sizeof(uint8_t); + void *key_data = malloc(key_size); + memmove(key_data, &key_relay, sizeof(db_key_relay_e)); + memmove(key_data + sizeof(db_key_relay_e), &num, sizeof(uint8_t)); + + MDB_val key; + key.mv_size = key_size; + key.mv_data = key_data; + + if((err = mdb_get(mdb_txn, mdb_dbi, &key, value)) != 0) + { + LOG_ERROR("mdb_get error %s", mdb_strerror(err)); + mdb_txn_abort(mdb_txn); + free(key_data); + return 1; + } + free(key_data); + return 0; +} + +relay_t* +relay_load(MDB_env *mdb_env, uint8_t num) +{ + int err; + MDB_txn *mdb_txn; + MDB_dbi mdb_dbi; + + relay_t *new_relay; + + if((err = mdb_txn_begin(mdb_env, NULL, MDB_RDONLY, &mdb_txn)) != 0) + { + LOG_ERROR("mdb_txn_begin error %s", mdb_strerror(err)); + return relay_create(num); + } + + if((err = mdb_dbi_open(mdb_txn, "relays", 0, &mdb_dbi)) != 0) + { + switch(err) + { + case MDB_NOTFOUND: + LOG_INFO("no relay for num %d found in db. returning new one (no relays db)", num); + mdb_txn_abort(mdb_txn); + return relay_create(num); + default: + LOG_ERROR("mdb_txn_begin error %s", mdb_strerror(err)); + return relay_create(num); + } + } + + new_relay = malloc(sizeof(relay_t)); + new_relay->number = num; + + MDB_val value; + + if((err = relay_load_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_NAME, num, &value)) != 0) + { + LOG_INFO("no relay for num %d found in db. returning new one", num); + mdb_txn_abort(mdb_txn); // transaction is read only + return relay_create(num); + } + strncpy(new_relay->name, (char*)value.mv_data, MAX_NAME_LENGTH); + new_relay->name[MAX_NAME_LENGTH] = '\0'; + + if((err = relay_load_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_SCHEDULE_ID, num, &value)) != 0) + { + LOG_INFO("no relay for num %d found in db. returning new one", num); + mdb_txn_abort(mdb_txn); // transaction is read only + return relay_create(num); + } + uuid_t *schedule_id = (uuid_t*)value.mv_data; + + if((err = relay_load_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_SCHEDULE_PERIODS, num, &value)) != 0) + { + LOG_INFO("no relay for num %d found in db. returning new one", num); + mdb_txn_abort(mdb_txn); // transaction is read only + return relay_create(num); + } + uint16_t schedule_periods_length = ((uint16_t*)value.mv_data)[0]; + uint16_t *schedule_periods = ((uint16_t*)value.mv_data) + 1; + + new_relay->schedule = schedule_create(*schedule_id, schedule_periods_length, schedule_periods); + + mdb_txn_abort(mdb_txn); // transaction is read only + + return new_relay; +} diff --git a/models/relay_save.c b/models/relay_save.c new file mode 100644 index 0000000..e932940 --- /dev/null +++ b/models/relay_save.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +#include +#include + +static int +relay_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_relay_e key_relay, uint8_t num, MDB_val value) +{ + int err; + + size_t key_size = sizeof(db_key_relay_e) + sizeof(uint8_t); + void *key_data = malloc(key_size); + memmove(key_data, &key_relay, sizeof(db_key_relay_e)); + memmove(key_data + sizeof(db_key_relay_e), &num, sizeof(uint8_t)); + + MDB_val key; + key.mv_size = key_size; + key.mv_data = key_data; + + if((err = mdb_put(mdb_txn, mdb_dbi, &key, &value, 0)) != 0) + { + LOG_ERROR("mdb_put error %s", mdb_strerror(err)); + mdb_txn_abort(mdb_txn); + free(key_data); + return 1; + } + free(key_data); + return 0; +} + +int +relay_save(relay_t *relay, MDB_env *mdb_env) +{ + LOG_TRACE("saving relay %d @ %p", relay->number, relay); + int err; + + MDB_txn *mdb_txn; + MDB_dbi mdb_dbi; + MDB_val value; + + if((err = mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn)) != 0) + { + LOG_ERROR("mdb_txn_begin error %s", mdb_strerror(err)); + exit(1); + } + + if((err = mdb_dbi_open(mdb_txn, "relays", MDB_CREATE, &mdb_dbi)) != 0) + { + LOG_ERROR("mdb_dbi_open error %s", mdb_strerror(err)); + exit(1); + } + + value.mv_size = sizeof(char) * (strlen(relay->name) + 1); + value.mv_data = relay->name; + if(relay_save_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_NAME, relay->number, value)) + { + LOG_ERROR("failed to save name"); + return 1; + } + + value.mv_size = sizeof(uuid_t); + value.mv_data = relay->schedule->id; + if(relay_save_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_SCHEDULE_ID, relay->number, value)) + { + LOG_ERROR("failed to save ID"); + return 1; + } + + // save periods blob + uint16_t *periods_blob = schedule_periods_to_blob(relay->schedule); + value.mv_size = sizeof(uint16_t) * ((periods_blob[0] * 2) + 1); + value.mv_data = periods_blob; + if(relay_save_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_SCHEDULE_PERIODS, relay->number, value)) + { + free(periods_blob); + LOG_ERROR("failed to save periods"); + return 1; + } + free(periods_blob); + + mdb_txn_commit(mdb_txn); + + return 0; +} diff --git a/models/schedule.c b/models/schedule.c new file mode 100644 index 0000000..ee37df6 --- /dev/null +++ b/models/schedule.c @@ -0,0 +1,89 @@ +#include +#include + +#include +#include + +schedule_t* +schedule_create(uuid_t id, uint16_t length, uint16_t *periods_blob) +{ + schedule_t *new_schedule = malloc(sizeof(schedule_t)); + + memmove(new_schedule->id, id, sizeof(uuid_t)); + + new_schedule->length = length; + new_schedule->periods = NULL; + + if(length) + { + new_schedule->periods = malloc(sizeof(period_t*) * length); + + for(uint16_t i = 0; i < length; ++i) + { + uint16_t start = periods_blob[0 + (i * 2)]; + uint16_t end = periods_blob[1 + (i * 2)]; + new_schedule->periods[i] = period_create(start, end); + } + } + + return new_schedule; +} + +uint16_t* +schedule_periods_to_blob(schedule_t *schedule) +{ + uint16_t *periods_blob = malloc(sizeof(uint16_t) * ((2 * schedule->length) + 1)); + periods_blob[0] = schedule->length; + + for(uint16_t i = 0; i < schedule->length; ++i) + { + + periods_blob[1 + (i * 2)] = schedule->periods[i]->start; + periods_blob[2 + (i * 2)] = schedule->periods[i]->end; + } + + return periods_blob; +} + +void +schedule_free(schedule_t *schedule) +{ + for(uint16_t i = 0; i < schedule->length; ++i) + { + free(schedule->periods[i]); + } + free(schedule->periods); + free(schedule); +} + +void +schedule_debug(schedule_t *schedule) +{ + if(schedule == NULL) + { + LOG_DEBUG("schedule is NULL"); + return; + } + char uuid_str[37]; + uuid_unparse(schedule->id, uuid_str); + LOG_DEBUG("(1/3) %s @ %p", uuid_str, schedule); + LOG_DEBUG("(2/3) period count: %3d", schedule->length); + + // one block: "HH:MM-HH:MM, " --> size: 13 (14 with '\0') + char *periods_debug_str = malloc(sizeof(char) * ((schedule->length * 13) + 1)); + periods_debug_str[0] = '\0'; + + for(uint16_t i = 0; i < schedule->length; ++i) + { + sprintf( + periods_debug_str + (13 * i), + "%02d:%02d-%02d:%02d, ", + schedule->periods[i]->start / 60, + schedule->periods[i]->start % 60, + schedule->periods[i]->end / 60, + schedule->periods[i]->end % 60 + ); + } + + LOG_DEBUG("(3/3) periods: %s", periods_debug_str); +}