add: relay/piface support

This commit is contained in:
Tobias Reisinger 2020-04-14 00:50:55 +02:00
parent fa6ceb2bf4
commit db64e4f820
34 changed files with 1259 additions and 313 deletions

View file

@ -5,7 +5,7 @@ add_executable(controller main.c)
option(WIRING_PI_DEBUG "Use WiringPi Debugging Tool" OFF) 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) string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE)
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}") add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")
@ -16,14 +16,26 @@ if(WIRING_PI_DEBUG)
endif(WIRING_PI_DEBUG) endif(WIRING_PI_DEBUG)
aux_source_directory(. SRC_DIR) aux_source_directory(. SRC_DIR)
aux_source_directory(models MODEL_SRC) aux_source_directory(models MODELS_SRC)
aux_source_directory(helpers HELPER_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) target_include_directories(controller PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
add_custom_target(run 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 DEPENDS controller
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR} WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
) )

View file

@ -1,125 +0,0 @@
#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 <discovery.h>
#include <helper.h>
#include <binn.h>
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);
}

11
drivers/gpio.c Normal file
View file

@ -0,0 +1,11 @@
#include <wiringPi.h>
#include <piFace.h>
#include <wiring_debug.h>
#include <drivers.h>
void
driver_gpio_set(relay_t *relay, int value)
{
digitalWrite(relay->number, value);
}

11
drivers/piface.c Normal file
View file

@ -0,0 +1,11 @@
#include <wiringPi.h>
#include <piFace.h>
#include <wiring_debug.h>
#include <drivers.h>
void
driver_piface_set(relay_t *relay, int value)
{
digitalWrite(DRIVER_PIFACE_GPIO_BASE + relay->number, value);
}

113
handlers/command.c Normal file
View file

@ -0,0 +1,113 @@
#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 <handlers.h>
#include <helpers.h>
#include <enums.h>
#include <binn.h>
#include <models/schedule.h>
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);
}

72
handlers/discovery.c Normal file
View file

@ -0,0 +1,72 @@
#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 <handlers.h>
#include <helpers.h>
#include <binn.h>
#include <enums.h>
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);
}

51
helpers/bind_server.c Normal file
View file

@ -0,0 +1,51 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <errno.h>
#include <logger.h>
#include <helpers.h>
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;
}

View file

@ -5,33 +5,35 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <logger.h> #include <logger.h>
#include <helper.h> #include <helpers.h>
int int
helper_connect_server(char* host, char* port) helper_connect_tcp_server(char* host, uint16_t port)
{ {
int s, status; char port_str[6];
struct addrinfo hints, *res; sprintf(port_str, "%d", port);
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
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 int s, status;
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); struct addrinfo hints, *res;
exit(EXIT_FAILURE); 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 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
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //creating Socket LOG_ERROR("getaddrinfo: %s\n", gai_strerror(status));
return -1;
}
if ((status = connect(s, res->ai_addr, res->ai_addrlen)) != 0) { //res got filled out by getaddrinfo() for us
fprintf(stderr, "Keine Verbindung mit dem Netzwerk möglich.\n"); s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //creating Socket
freeaddrinfo(res);
exit(EXIT_FAILURE);
}
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;
} }

24
helpers/get_port.c Normal file
View file

@ -0,0 +1,24 @@
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <helpers.h>
#include <logger.h>
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);
}
}

View file

@ -0,0 +1,57 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
#include <logger.h>
#include <helpers.h>
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;
}

View file

@ -4,11 +4,11 @@
#include <log_levels.h> #include <log_levels.h>
/** /**
* @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 * @brief Maximum number of dbs for the databases for the MDB_env

3
include/constants.h Normal file
View file

@ -0,0 +1,3 @@
#define SECONDS_PER_DAY 86400 // 60 * 60 * 24
#define SECONDS_PER_MINUTE 60

View file

@ -1,27 +0,0 @@
#ifndef CONTROLLER_DISCOVERY_H
#define CONTROLLER_DISCOVERY_H
#include <models/controller.h>
/**
* @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 */

14
include/drivers.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef CONTROLLER_DRIVERS_H
#define CONTROLLER_DRIVERS_H
#include <models/relay.h>
#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 */

View file

@ -1,8 +1,40 @@
#ifndef CONTROLLER_ENUMS_H #ifndef CONTROLLER_ENUMS_H
#define CONTROLLER_ENUMS_H #define CONTROLLER_ENUMS_H
enum poll_fgs { enum poll_fgs
POLL_FGS_DISCOVERY {
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 */ #endif /* CONTROLLER_ENUMS_H */

24
include/handlers.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef CONTROLLER_HANDLERS_H
#define CONTROLLER_HANDLERS_H
#include <models/controller.h>
/**
* @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 */

View file

@ -1,7 +0,0 @@
#ifndef CONTROLLER_HELPER_H
#define CONTROLLER_HELPER_H
int
helper_connect_server(char* host, char* port);
#endif /* CONTROLLER_HELPER_H */

25
include/helpers.h Normal file
View file

@ -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 */

View file

@ -2,45 +2,52 @@
#define CONTROLLER_LOGGER_H #define CONTROLLER_LOGGER_H
#include <stdio.h> #include <stdio.h>
#include <time.h>
#include <colors.h> #include <colors.h>
#include <config.h> #include <config.h>
#include <macros.h> #include <macros.h>
#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 #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 #else
#define LOG_TRACE(msg, ...) #define LOG_TRACE(msg, ...)
#endif #endif
#if LOG_LEVEL >= LOG_LEVEL_DEBUG #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 #else
#define LOG_DEBUG(msg, ...) #define LOG_DEBUG(msg, ...)
#endif #endif
#if LOG_LEVEL >= LOG_LEVEL_INFO #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 #else
#define LOG_INFO(msg, ...) #define LOG_INFO(msg, ...)
#endif #endif
#if LOG_LEVEL >= LOG_LEVEL_WARN #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 #else
#define LOG_WARN(msg, ...) #define LOG_WARN(msg, ...)
#endif #endif
#if LOG_LEVEL >= LOG_LEVEL_ERROR #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 #else
#define LOG_ERROR(msg, ...) #define LOG_ERROR(msg, ...)
#endif #endif
#if LOG_LEVEL >= LOG_LEVEL_FATAL #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 #else
#define LOG_FATAL(msg, ...) #define LOG_FATAL(msg, ...)
#endif #endif

View file

@ -11,7 +11,7 @@
/** /**
* @brief Information about this controller * @brief Information about this controller
*/ */
typedef struct controller typedef struct
{ {
/** /**
* @brief A unique UUID for this controller * @brief A unique UUID for this controller
@ -22,7 +22,7 @@ typedef struct controller
* *
* Includes a \0 terminator. * 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 * @brief The command port the controller was bound to
*/ */
@ -35,22 +35,22 @@ typedef struct controller
* @brief Amount of relays available to this controller * @brief Amount of relays available to this controller
*/ */
uint8_t relay_count; uint8_t relay_count;
relay **relays; relay_t **relays;
} controller; } controller_t;
/** /**
* @brief Key to save controller information in database * @brief Key to save controller information in database
*/ */
typedef enum controller_db_key typedef enum
{ {
KEY_META_ID = 0, DB_KEY_CONTROLLER_ID = 0,
KEY_META_NAME = 1, DB_KEY_CONTROLLER_NAME = 1,
KEY_META_COMMAND_PORT = 2, DB_KEY_CONTROLLER_COMMAND_PORT = 2,
KEY_META_DISCOVERY_PORT = 3, DB_KEY_CONTROLLER_DISCOVERY_PORT = 3,
KEY_META_RELAY_COUNT = 4, DB_KEY_CONTROLLER_RELAY_COUNT = 4,
KEY_META_RELAYS = 5, DB_KEY_CONTROLLER_RELAYS = 5,
} controller_db_key; } db_key_controller_e;
/** /**
* @brief Create a new instance of controller * @brief Create a new instance of controller
@ -59,7 +59,7 @@ typedef enum controller_db_key
* *
* @return A new instance of #controller * @return A new instance of #controller
*/ */
controller* controller_t*
controller_create(void); controller_create(void);
@ -72,20 +72,32 @@ controller_create(void);
* *
* @return A loaded or new instance of controller or NULL on database error * @return A loaded or new instance of controller or NULL on database error
*/ */
controller* controller_t*
controller_load(MDB_env *mdb_env); controller_load(MDB_env *mdb_env);
/** /**
* @brief Save a controller to the database * @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 * @param mdb_env Already created MDB_env
* *
* @return Indicator to show success (0) or failure (!0) * @return Indicator to show success (0) or failure (!0)
*/ */
int 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 * @brief Debug an instance of #controller
@ -95,6 +107,6 @@ controller_save(controller *cntrlr, MDB_env *mdb_env);
* @param cntrlr #controller to debug * @param cntrlr #controller to debug
*/ */
void void
controller_debug(controller *cntrlr); controller_debug(controller_t *controller);
#endif //CONTROLLER_CONTROLLER_H #endif //CONTROLLER_CONTROLLER_H

19
include/models/period.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef CONTROLLER_PERIOD_H
#define CONTROLLER_PERIOD_H
#include <stdint.h>
#include <time.h>
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 */

View file

@ -2,16 +2,63 @@
#define CONTROLLER_RELAY_H #define CONTROLLER_RELAY_H
#include <stdint.h> #include <stdint.h>
#include <time.h>
#include <lmdb.h>
#include <config.h> #include <config.h>
#include <models/schedule.h>
typedef struct relay { typedef struct
uint8_t index; {
char name[128]; uint8_t number;
uint16_t *schedule; char name[MAX_NAME_LENGTH + 1];
} relay; 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 #endif //CONTROLLER_RELAY_H

28
include/models/schedule.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef CONTROLLER_SCHEDULE_H
#define CONTROLLER_SCHEDULE_H
#include <stdint.h>
#include <uuid/uuid.h>
#include <models/period.h>
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 */

View file

@ -1,10 +1,15 @@
#ifndef CONTROLLER_WIRING_DEBUG_H #ifndef CONTROLLER_WIRING_DEBUG_H
#define CONTROLLER_WIRING_DEBUG_H #define CONTROLLER_WIRING_DEBUG_H
#include <logger.h>
#ifdef WIRING_PI_DEBUG #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 pinMode(x,y) LOG_INFO("wiringPi pinMode(%d, %d)", x, y)
#define digitalWrite(x,y) LOG_INFO("wiringPi digitalWrite(%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
#endif /* CONTROLLER_WIRING_DEBUG_H */ #endif /* CONTROLLER_WIRING_DEBUG_H */

10
logger.c Normal file
View file

@ -0,0 +1,10 @@
#include <logger.h>
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;
}

141
main.c
View file

@ -1,17 +1,72 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <time.h>
#include <lmdb.h> #include <lmdb.h>
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <poll.h> #include <poll.h>
#include <signal.h>
#include <logger.h> #include <logger.h>
#include <models/controller.h> #include <models/controller.h>
#include <database.h> #include <database.h>
#include <config.h> #include <config.h>
#include <discovery.h> #include <handlers.h>
#include <drivers.h>
#include <enums.h> #include <enums.h>
#include <helpers.h>
#include <wiringPi.h>
#include <piFace.h>
#include <wiring_debug.h>
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 * @brief The main function
@ -27,18 +82,40 @@ main(int argc, char** argv)
(void)argc; (void)argc;
(void)argv; (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); database_setup(&mdb_env);
controller *this_cntrlr = controller_load(mdb_env); this_controller = controller_load(mdb_env);
controller_save(this_cntrlr, 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]; struct pollfd fds[2];
int timeout_msecs = ACCEPT_TIMEOUT_MSECONDS; int timeout_msecs = ACCEPT_TIMEOUT_MSECONDS;
int ret; int ret;
int i;
int fd_count = 0; int fd_count = 0;
/* Open STREAMS device. */ /* Open STREAMS device. */
@ -46,6 +123,12 @@ main(int argc, char** argv)
fds[POLL_FGS_DISCOVERY].events = POLLIN; fds[POLL_FGS_DISCOVERY].events = POLLIN;
LOG_DEBUG("setup fd_discovery as %i on index %i", fd_discovery, fd_count); LOG_DEBUG("setup fd_discovery as %i on index %i", fd_discovery, fd_count);
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) while(1)
{ {
@ -54,27 +137,37 @@ main(int argc, char** argv)
if(ret == 0) if(ret == 0)
{ {
LOG_TRACE("idle loop"); 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) if(ret > 0)
{ {
/* An event on one of the fds has occurred. */ handle_poll(fds, fd_count);
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);
}
}
} }
} }

View file

@ -7,38 +7,62 @@
#include <models/controller.h> #include <models/controller.h>
#include <macros.h> #include <macros.h>
controller* controller_t*
controller_create(void) controller_create(void)
{ {
controller *result = malloc(sizeof(*result)); controller_t *new_controller = malloc(sizeof(*new_controller));
uuid_generate(result->id); uuid_generate(new_controller->id);
strcpy(result->name, "new emgauwa device"); strcpy(new_controller->name, "new emgauwa device");
result->command_port = 0; new_controller->command_port = 0;
result->discovery_port = 4421; new_controller->discovery_port = 4421;
result->relay_count = 10; 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; 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 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"); LOG_DEBUG("controller is NULL");
return;
} }
char uuid_str[37]; char uuid_str[37];
uuid_unparse(cntrlr->id, uuid_str); uuid_unparse(controller->id, uuid_str);
LOG_DEBUG("(1/4) %s @ %p", uuid_str, cntrlr); LOG_DEBUG("(1/5) %s @ %p", uuid_str, controller);
LOG_DEBUG("(2/4) name: %s", cntrlr->name); LOG_DEBUG("(2/5) name: %s", controller->name);
LOG_DEBUG("(3/4) relays: %3d", cntrlr->relay_count); LOG_DEBUG("(3/5) command_port: %5d discovery_port: %5d", controller->command_port, controller->discovery_port);
LOG_DEBUG("(4/4) command_port: %5d discovery_port: %5d", cntrlr->command_port, cntrlr->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]);
}
} }

View file

@ -8,73 +8,79 @@
#include <macros.h> #include <macros.h>
static void 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; int err;
MDB_val key; MDB_val key;
key.mv_size = sizeof(controller_db_key); key.mv_size = sizeof(db_key_controller_e);
key.mv_data = &key_meta; key.mv_data = &key_controller;
if((err = mdb_get(mdb_txn, mdb_dbi, &key, value)) != 0) 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); exit(1);
} }
} }
controller* controller_t*
controller_load(MDB_env *mdb_env) controller_load(MDB_env *mdb_env)
{ {
int err; int err;
MDB_txn *mdb_txn; MDB_txn *mdb_txn;
MDB_dbi mdb_dbi; MDB_dbi mdb_dbi;
controller *new_controller; controller_t *new_controller;
if((err = mdb_txn_begin(mdb_env, NULL, MDB_RDONLY, &mdb_txn)) != 0) 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; 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) switch(err)
{ {
case MDB_NOTFOUND: case MDB_NOTFOUND:
LOG_INFO("no controller found in db. creating new one");
mdb_txn_abort(mdb_txn); mdb_txn_abort(mdb_txn);
new_controller = controller_create(); new_controller = controller_create();
controller_save(new_controller, mdb_env); controller_save(new_controller, mdb_env);
return new_controller; return new_controller;
default: default:
fprintf(stderr, "mdb_txn_begin error %s\n", mdb_strerror(err)); LOG_ERROR("mdb_txn_begin error %s", mdb_strerror(err));
return NULL; return NULL;
} }
} }
new_controller = malloc(sizeof(*new_controller)); new_controller = malloc(sizeof(controller_t));
MDB_val value; 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)); memmove(new_controller->id, (uuid_t*)value.mv_data, sizeof(uuid_t));
controller_load_single(mdb_txn, mdb_dbi, KEY_META_NAME, &value); controller_load_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_NAME, &value);
strncpy(new_controller->name, (char*)value.mv_data, CONTROLLER_NAME_LENGTH); strncpy(new_controller->name, (char*)value.mv_data, MAX_NAME_LENGTH);
new_controller->name[CONTROLLER_NAME_LENGTH] = '\0'; 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]; 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]; 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]; new_controller->relay_count = ((uint8_t*)value.mv_data)[0];
controller_debug(new_controller);
mdb_txn_abort(mdb_txn); // transaction is read only 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; return new_controller;
} }

View file

@ -8,17 +8,17 @@
#include <macros.h> #include <macros.h>
int 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; int err;
MDB_val key; MDB_val key;
key.mv_size = sizeof(controller_db_key); key.mv_size = sizeof(db_key_controller_e);
key.mv_data = &key_meta; key.mv_data = &key_controller;
if((err = mdb_put(mdb_txn, mdb_dbi, &key, &value, 0)) != 0) 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); mdb_txn_abort(mdb_txn);
return 1; return 1;
} }
@ -26,7 +26,7 @@ controller_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, controller_db_key key_
} }
int int
controller_save(controller *cntrlr, MDB_env *mdb_env) controller_save(controller_t *controller, MDB_env *mdb_env)
{ {
int err; int err;
@ -34,56 +34,65 @@ controller_save(controller *cntrlr, MDB_env *mdb_env)
MDB_dbi mdb_dbi; MDB_dbi mdb_dbi;
MDB_val value; MDB_val value;
controller_debug(cntrlr);
if((err = mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn)) != 0) 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); 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); exit(1);
} }
value.mv_size = sizeof(cntrlr->id); value.mv_size = sizeof(uuid_t);
value.mv_data = cntrlr->id; value.mv_data = controller->id;
if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_ID, value)) if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_ID, value))
{ {
LOG_ERROR("failed to save ID");
return 1; return 1;
} }
value.mv_size = sizeof(char) * (strlen(cntrlr->name) + 1); value.mv_size = sizeof(char) * (strlen(controller->name) + 1);
value.mv_data = cntrlr->name; value.mv_data = controller->name;
if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_NAME, value)) if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_NAME, value))
{ {
LOG_ERROR("failed to save name");
return 1; return 1;
} }
value.mv_size = sizeof(cntrlr->command_port); value.mv_size = sizeof(controller->command_port);
value.mv_data = &cntrlr->command_port; value.mv_data = &controller->command_port;
if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_COMMAND_PORT, value)) if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_COMMAND_PORT, value))
{ {
LOG_ERROR("failed to save command port");
return 1; return 1;
} }
value.mv_size = sizeof(cntrlr->discovery_port); value.mv_size = sizeof(controller->discovery_port);
value.mv_data = &cntrlr->discovery_port; value.mv_data = &controller->discovery_port;
if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_DISCOVERY_PORT, value)) if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_DISCOVERY_PORT, value))
{ {
LOG_ERROR("failed to save discovery port");
return 1; return 1;
} }
value.mv_size = sizeof(cntrlr->relay_count); value.mv_size = sizeof(controller->relay_count);
value.mv_data = &cntrlr->relay_count; value.mv_data = &controller->relay_count;
if(controller_save_single(mdb_txn, mdb_dbi, KEY_META_RELAY_COUNT, value)) if(controller_save_single(mdb_txn, mdb_dbi, DB_KEY_CONTROLLER_RELAY_COUNT, value))
{ {
LOG_ERROR("failed to save relay count");
return 1; return 1;
} }
mdb_txn_commit(mdb_txn); 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; return 0;
} }

44
models/period.c Normal file
View file

@ -0,0 +1,44 @@
#include <stdlib.h>
#include <logger.h>
#include <constants.h>
#include <models/period.h>
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;
}

View file

@ -1,10 +1,83 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <uuid/uuid.h>
#include <constants.h>
#include <logger.h>
#include <models/relay.h> #include <models/relay.h>
relay* relay_t*
relay_init(uint8_t index) relay_create(uint8_t number)
{ {
(void)index; relay_t *new_relay = malloc(sizeof(relay_t));
return NULL;
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(&timestamp_start, &time_start);
//localtime_r(&timestamp_now, &time_now);
//localtime_r(&timestamp_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);
} }

100
models/relay_load.c Normal file
View file

@ -0,0 +1,100 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <uuid/uuid.h>
#include <models/relay.h>
#include <macros.h>
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;
}

88
models/relay_save.c Normal file
View file

@ -0,0 +1,88 @@
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <uuid/uuid.h>
#include <models/relay.h>
#include <macros.h>
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;
}

89
models/schedule.c Normal file
View file

@ -0,0 +1,89 @@
#include <stdlib.h>
#include <string.h>
#include <logger.h>
#include <models/schedule.h>
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);
}