Prepare go-rewrite
This commit is contained in:
parent
c49ada88c3
commit
c515f55b8b
57 changed files with 103 additions and 42639 deletions
|
@ -1,94 +0,0 @@
|
|||
cmake_minimum_required (VERSION 3.7)
|
||||
project(controller
|
||||
VERSION 0.4.0
|
||||
LANGUAGES C)
|
||||
|
||||
add_executable(controller src/main.c)
|
||||
|
||||
target_link_libraries(controller -lwiringPi)
|
||||
target_link_libraries(controller -lwiringPiDev)
|
||||
target_link_libraries(controller -lsqlite3)
|
||||
target_link_libraries(controller -luuid)
|
||||
|
||||
option(WIRING_PI_DEBUG "Use WiringPi Debugging Tool (OFF)" OFF)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} $ENV{CFLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D'__FILENAME__=\"$(subst $(realpath ${CMAKE_SOURCE_DIR}/src/)/,,$(abspath $<))\"'")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -std=gnu99 -Wpedantic -Werror -Wall -Wextra")
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage")
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
|
||||
file(GLOB_RECURSE ALL_SOURCE_FILES src/*.c)
|
||||
|
||||
if(WIRING_PI_DEBUG)
|
||||
message("Showing wiringPi calls as debug")
|
||||
add_definitions("-DWIRING_PI_DEBUG")
|
||||
endif(WIRING_PI_DEBUG)
|
||||
|
||||
aux_source_directory(src/ SRC_DIR)
|
||||
aux_source_directory(src/models MODELS_SRC)
|
||||
aux_source_directory(src/helpers HELPERS_SRC)
|
||||
aux_source_directory(src/handlers HANDLERS_SRC)
|
||||
aux_source_directory(src/drivers DRIVERS_SRC)
|
||||
aux_source_directory(src/runners RUNNERS_SRC)
|
||||
aux_source_directory(vendor VENDOR_SRC)
|
||||
|
||||
add_dependencies(controller sql)
|
||||
|
||||
configure_file("version.h.in" "version.h" @ONLY)
|
||||
|
||||
|
||||
target_sources(controller PRIVATE ${SRC_DIR} ${MODELS_SRC} ${HELPERS_SRC} ${HANDLERS_SRC} ${DRIVERS_SRC} ${RUNNERS_SRC} ${VENDOR_SRC})
|
||||
target_include_directories(controller PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_include_directories(controller PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor)
|
||||
target_include_directories(controller PRIVATE ${CMAKE_BINARY_DIR})
|
||||
|
||||
add_custom_target(sql
|
||||
COMMAND ./compile_sql.sh
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_custom_target(run
|
||||
COMMAND ${CMAKE_BINARY_DIR}/controller
|
||||
DEPENDS controller
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_custom_target(docs
|
||||
COMMAND doxygen
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
IF(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
message(STATUS "loading debug targets")
|
||||
add_custom_target(debug
|
||||
COMMAND gdb ${CMAKE_BINARY_DIR}/controller
|
||||
DEPENDS controller
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
add_custom_target(valgrind
|
||||
COMMAND valgrind -s ${CMAKE_BINARY_DIR}/controller
|
||||
DEPENDS controller
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
add_custom_target(valgrind-leak
|
||||
COMMAND valgrind --leak-check=full --show-leak-kinds=all ${CMAKE_BINARY_DIR}/controller
|
||||
DEPENDS controller
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
clang-tidy
|
||||
COMMAND /usr/bin/clang-tidy
|
||||
${ALL_SOURCE_FILES}
|
||||
--header-filter=${CMAKE_SOURCE_DIR}/include/*
|
||||
--
|
||||
-std=gnu99
|
||||
-I${CMAKE_SOURCE_DIR}/include
|
||||
-I${CMAKE_SOURCE_DIR}/vendor
|
||||
-I${CMAKE_BINARY_DIR}
|
||||
)
|
||||
ENDIF(CMAKE_BUILD_TYPE MATCHES Debug)
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
|||
module emgauwa.app/controller
|
||||
|
||||
go 1.15
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef CONTROLLER_CLI_H
|
||||
#define CONTROLLER_CLI_H
|
||||
|
||||
typedef struct cli
|
||||
{
|
||||
const char *config_file;
|
||||
int demo_mode;
|
||||
} cli_t;
|
||||
|
||||
void
|
||||
cli_parse(int argc, const char **argv, cli_t *cli);
|
||||
|
||||
#endif /* CONTROLLER_CLI_H */
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef CONTROLLER_COLORS_H
|
||||
#define CONTROLLER_COLORS_H
|
||||
|
||||
#define COLOR_RED "\033[0;31m"
|
||||
#define COLORB_RED "\033[1;31m"
|
||||
#define COLOR_GREEN "\033[0;32m"
|
||||
#define COLORB_GREEN "\033[1;32m"
|
||||
#define COLOR_YELLOW "\033[0;33m"
|
||||
#define COLORB_YELLOW "\033[1;33m"
|
||||
#define COLOR_BLUE "\033[0;34m"
|
||||
#define COLORB_BLUE "\033[1;34m"
|
||||
#define COLOR_MAGENTA "\033[0;35m"
|
||||
#define COLORB_MAGENTA "\033[1;35m"
|
||||
#define COLOR_CYAN "\033[0;36m"
|
||||
#define COLORB_CYAN "\033[1;36m"
|
||||
#define COLOR_NONE "\033[0m"
|
||||
|
||||
#endif //CONTROLLER_COLORS_H
|
|
@ -1,66 +0,0 @@
|
|||
#ifndef CONTROLLER_CONFIG_H
|
||||
#define CONTROLLER_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <toml.h>
|
||||
|
||||
#include <constants.h>
|
||||
#include <enums.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t pin;
|
||||
int inverted;
|
||||
int init;
|
||||
relay_driver_t driver;
|
||||
uint8_t pulse_duration;
|
||||
} config_relay_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
char *database;
|
||||
char *user;
|
||||
char *group;
|
||||
char *mqtt_host;
|
||||
char *include;
|
||||
int relays_init;
|
||||
uint8_t relay_count;
|
||||
|
||||
struct
|
||||
{
|
||||
int level;
|
||||
FILE *file;
|
||||
} logging;
|
||||
|
||||
struct
|
||||
{
|
||||
uint16_t discovery;
|
||||
uint16_t mqtt;
|
||||
} ports;
|
||||
|
||||
config_relay_t *relay_configs;
|
||||
} config_t;
|
||||
|
||||
extern config_t *global_config;
|
||||
|
||||
void
|
||||
config_init();
|
||||
|
||||
void
|
||||
config_free();
|
||||
|
||||
void
|
||||
config_load_string(char **holder, const char *value);
|
||||
|
||||
void
|
||||
config_load(config_t *config, const char *cli_config_file);
|
||||
|
||||
void
|
||||
config_load_file(config_t *config, const char *file_name);
|
||||
|
||||
void
|
||||
config_load_directory(config_t *config, const char *directory_name);
|
||||
|
||||
#endif //CONTROLLER_CONFIG_H
|
|
@ -1,17 +0,0 @@
|
|||
#ifndef CONTROLLER_CONNECTIONS_H
|
||||
#define CONTROLLER_CONNECTIONS_H
|
||||
|
||||
#include <mongoose.h>
|
||||
|
||||
struct mg_connection*
|
||||
connection_discovery_bind(struct mg_mgr *mgr);
|
||||
|
||||
struct mg_connection*
|
||||
connection_command_bind(struct mg_mgr *mgr);
|
||||
|
||||
struct mg_connection*
|
||||
connection_mqtt_connect(struct mg_mgr *mgr);
|
||||
|
||||
extern struct mg_connection *global_connection_mqtt;
|
||||
|
||||
#endif /* CONTROLLER_CONNECTIONS_H */
|
|
@ -1,32 +0,0 @@
|
|||
#ifndef CONTROLLER_CONTANTS_H
|
||||
#define CONTROLLER_CONTANTS_H
|
||||
|
||||
#define SECONDS_PER_DAY 86400 // 60 * 60 * 24
|
||||
|
||||
#define SECONDS_PER_MINUTE 60
|
||||
|
||||
#define POLL_FDS_COUNT 2
|
||||
|
||||
/**
|
||||
* @brief Limit the maximum length of a controller/relay/etc name
|
||||
*
|
||||
* The NULL terminator is not included. Arrays of length #MAX_NAME_LENGTH + 1 are required.
|
||||
*/
|
||||
#define MAX_NAME_LENGTH 128
|
||||
|
||||
/**
|
||||
* @brief How many milli seconds to wait until poll timeout in main loop
|
||||
*/
|
||||
#define ACCEPT_TIMEOUT_MSECONDS 1000
|
||||
|
||||
#define PIFACE_GPIO_BASE 200
|
||||
|
||||
#define DEFAULT_CONTROLLER_NAME "emgauwa-controller"
|
||||
#define DEFAULT_CONFIG_PATH "emgauwa-controller.conf"
|
||||
#define DEFAULT_GLOBAL_CONFIG_PATH "/etc/emgauwa/controller.conf"
|
||||
#define DEFAULT_DATABASE_PATH "emgauwa-controller.sqlite"
|
||||
#define DEFAULT_DISCOVERY_PORT 4421
|
||||
#define DEFAULT_MQTT_PORT 1885
|
||||
#define DEFAULT_MQTT_HOST "127.0.0.1"
|
||||
|
||||
#endif /* CONTROLLER_CONTANTS_H */
|
|
@ -1,40 +0,0 @@
|
|||
#ifndef CONTROLLER_DATABASE_H
|
||||
#define CONTROLLER_DATABASE_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
extern sqlite3 *global_database;
|
||||
|
||||
void
|
||||
database_init();
|
||||
|
||||
void
|
||||
database_free();
|
||||
|
||||
void
|
||||
database_migrate();
|
||||
|
||||
|
||||
int
|
||||
database_transaction_begin();
|
||||
|
||||
void
|
||||
database_transaction_commit();
|
||||
|
||||
void
|
||||
database_transaction_rollback();
|
||||
|
||||
|
||||
int
|
||||
database_helper_get_id(sqlite3_stmt *stmt);
|
||||
|
||||
int*
|
||||
database_helper_get_ids(sqlite3_stmt *stmt);
|
||||
|
||||
char*
|
||||
database_helper_get_string(sqlite3_stmt *stmt);
|
||||
|
||||
char**
|
||||
database_helper_get_strings(sqlite3_stmt *stmt);
|
||||
|
||||
#endif /* CONTROLLER_DATABASE_H */
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef CONTROLLER_DRIVERS_H
|
||||
#define CONTROLLER_DRIVERS_H
|
||||
|
||||
#include <models/relay.h>
|
||||
#include <enums.h>
|
||||
|
||||
void
|
||||
driver_piface_set(int pin, int value);
|
||||
|
||||
void
|
||||
driver_gpio_set(int pin, int value);
|
||||
|
||||
#endif /* CONTROLLER_DRIVERS_H */
|
|
@ -1,25 +0,0 @@
|
|||
#ifndef CONTROLLER_ENUMS_H
|
||||
#define CONTROLLER_ENUMS_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
POLL_FDS_DISCOVERY,
|
||||
POLL_FDS_COMMAND
|
||||
} poll_fds_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DISCOVERY_MAPPING_ID = 0,
|
||||
DISCOVERY_MAPPING_NAME = 1,
|
||||
DISCOVERY_MAPPING_COMMAND_PORT = 2,
|
||||
DISCOVERY_MAPPING_RELAY_COUNT = 3,
|
||||
} discovery_mapping_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RELAY_DRIVER_NONE,
|
||||
RELAY_DRIVER_GPIO,
|
||||
RELAY_DRIVER_PIFACE,
|
||||
} relay_driver_t;
|
||||
|
||||
#endif /* CONTROLLER_ENUMS_H */
|
|
@ -1,32 +0,0 @@
|
|||
#ifndef CONTROLLER_HANDLERS_H
|
||||
#define CONTROLLER_HANDLERS_H
|
||||
|
||||
#include <mongoose.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(struct mg_connection *c, int ev, void *ev_data);
|
||||
|
||||
/**
|
||||
* @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(struct mg_connection *c, int ev, void *ev_data);
|
||||
|
||||
void
|
||||
handler_mqtt(struct mg_connection *c, int ev, void *ev_data);
|
||||
|
||||
void
|
||||
handler_loop(struct mg_connection *c_mqtt);
|
||||
|
||||
#endif /* CONTROLLER_HANDLERS_H */
|
|
@ -1,34 +0,0 @@
|
|||
#ifndef CONTROLLER_HELPERS_H
|
||||
#define CONTROLLER_HELPERS_H
|
||||
|
||||
#include <time.h>
|
||||
#include <config.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);
|
||||
|
||||
int
|
||||
helper_get_weekday(const struct tm *time_struct);
|
||||
|
||||
int
|
||||
helper_drop_privileges();
|
||||
|
||||
#endif /* CONTROLLER_HELPERS_H */
|
|
@ -1,22 +0,0 @@
|
|||
#ifndef EMGAUWA_LOGGER_H
|
||||
#define EMGAUWA_LOGGER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <colors.h>
|
||||
#include <config.h>
|
||||
|
||||
void
|
||||
logger_log(int level, const char *filename, int line, const char *func, const char *msg, ...);
|
||||
|
||||
#define LOGGER_DEBUG(...) logger_log(LOG_DEBUG , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
#define LOGGER_INFO(...) logger_log(LOG_INFO , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
#define LOGGER_NOTICE(...) logger_log(LOG_NOTICE , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
#define LOGGER_WARNING(...) logger_log(LOG_WARNING, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
#define LOGGER_ERR(...) logger_log(LOG_ERR , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
#define LOGGER_CRIT(...) logger_log(LOG_CRIT , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
#define LOGGER_EMERG(...) logger_log(LOG_EMERG , __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||
|
||||
#endif //EMGAUWA_LOGGER_H
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef CONTROLLER_MACROS_H
|
||||
#define CONTROLLER_MACROS_H
|
||||
|
||||
#define STRLEN(s) ((sizeof(s)/sizeof(s[0])) - sizeof(s[0]))
|
||||
|
||||
#endif /* CONTROLLER_MACROS_H */
|
|
@ -1,107 +0,0 @@
|
|||
#ifndef CONTROLLER_CONTROLLER_H
|
||||
#define CONTROLLER_CONTROLLER_H
|
||||
|
||||
#include <uuid/uuid.h>
|
||||
#include <stdint.h>
|
||||
#include <lmdb.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <models/relay.h>
|
||||
|
||||
/**
|
||||
* @brief Information about this controller
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
|
||||
/**
|
||||
* @brief A unique UUID for this controller
|
||||
*/
|
||||
uuid_t uid;
|
||||
/**
|
||||
* @brief The name of this controller
|
||||
*
|
||||
* Includes a \0 terminator.
|
||||
*/
|
||||
char name[MAX_NAME_LENGTH + 1];
|
||||
/**
|
||||
* @brief The command port the controller was bound to
|
||||
*/
|
||||
uint16_t command_port;
|
||||
|
||||
relay_t **relays;
|
||||
|
||||
} controller_t;
|
||||
|
||||
/**
|
||||
* @brief Key to save controller information in database
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
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_e;
|
||||
|
||||
/**
|
||||
* @brief Create a new instance of controller
|
||||
*
|
||||
* This should not fail. The instance will be created with malloc and genric default values
|
||||
*
|
||||
* @return A new instance of #controller
|
||||
*/
|
||||
controller_t*
|
||||
controller_create(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Load a controller for database or create a new one
|
||||
*
|
||||
* Will return NULL when transaction can't start.
|
||||
*
|
||||
* @param mdb_env An opened MDB_env to load from
|
||||
*
|
||||
* @return A loaded or new instance of controller or NULL on database error
|
||||
*/
|
||||
controller_t*
|
||||
controller_load();
|
||||
|
||||
/**
|
||||
* @brief Save a controller to the database
|
||||
*
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* @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, const char *name);
|
||||
|
||||
void
|
||||
controller_free(controller_t *controller);
|
||||
|
||||
/**
|
||||
* @brief Debug an instance of #controller
|
||||
*
|
||||
* Will use #LOG_DEBUG to log. So log will be depending on #LOG_LEVEL
|
||||
*
|
||||
* @param cntrlr #controller to debug
|
||||
*/
|
||||
void
|
||||
controller_debug(controller_t *controller);
|
||||
|
||||
extern controller_t *global_controller;
|
||||
|
||||
#endif //CONTROLLER_CONTROLLER_H
|
|
@ -1,16 +0,0 @@
|
|||
#ifndef CONTROLLER_MODELS_JUNCTION_RELAY_SCHEDULE_H
|
||||
#define CONTROLLER_MODELS_JUNCTION_RELAY_SCHEDULE_H
|
||||
|
||||
int
|
||||
junction_relay_schedule_insert(uint8_t weekday, int relay_id, int schedule_id);
|
||||
|
||||
int
|
||||
junction_relay_schedule_remove_for_relay(int relay_id);
|
||||
|
||||
int
|
||||
junction_relay_schedule_insert_weekdays(int relay_id, int *schedule_ids);
|
||||
|
||||
int*
|
||||
junction_relay_schedule_get_relay_ids_with_schedule(int schedule_id);
|
||||
|
||||
#endif /* CONTROLLER_MODELS_JUNCTION_RELAY_SCHEDULE_H */
|
|
@ -1,16 +0,0 @@
|
|||
#ifndef CONTROLLER_PERIOD_H
|
||||
#define CONTROLLER_PERIOD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
} period_t;
|
||||
|
||||
int
|
||||
period_includes_time(period_t period, struct tm *time_struct);
|
||||
|
||||
#endif /* CONTROLLER_PERIOD_H */
|
|
@ -1,54 +0,0 @@
|
|||
#ifndef CONTROLLER_RELAY_H
|
||||
#define CONTROLLER_RELAY_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <lmdb.h>
|
||||
|
||||
#include <constants.h>
|
||||
#include <models/schedule.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
uint8_t number;
|
||||
int is_on;
|
||||
int pulse_timer;
|
||||
int sent_to_broker;
|
||||
char name[MAX_NAME_LENGTH + 1];
|
||||
schedule_t *schedules[7];
|
||||
} relay_t;
|
||||
|
||||
/**
|
||||
* @brief Key to save relay information in database
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
DB_KEY_RELAY_NAME = 0,
|
||||
} db_key_relay_e;
|
||||
|
||||
relay_t*
|
||||
relay_create(uint8_t number);
|
||||
|
||||
void
|
||||
relay_set_name(relay_t *relay, const char *name);
|
||||
|
||||
relay_t*
|
||||
relay_load(uint8_t number);
|
||||
|
||||
int
|
||||
relay_save(relay_t *relay);
|
||||
|
||||
void
|
||||
relay_reload_schedules(relay_t *relay);
|
||||
|
||||
int
|
||||
relay_is_on_schedule(relay_t *relay, struct tm *time_struct);
|
||||
|
||||
void
|
||||
relay_free(relay_t *relay);
|
||||
|
||||
void
|
||||
relay_debug(relay_t *relay);
|
||||
|
||||
#endif //CONTROLLER_RELAY_H
|
|
@ -1,58 +0,0 @@
|
|||
#ifndef CONTROLLER_SCHEDULE_H
|
||||
#define CONTROLLER_SCHEDULE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <lmdb.h>
|
||||
|
||||
#include <models/period.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
uuid_t uid;
|
||||
uint8_t weekday;
|
||||
uint16_t periods_count;
|
||||
period_t *periods;
|
||||
} schedule_t;
|
||||
|
||||
/**
|
||||
* @brief Key to save schedule information in database
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
DB_KEY_SCHEDULE_ID = 0,
|
||||
DB_KEY_SCHEDULE_PERIODS = 1,
|
||||
} db_key_schedule_e;
|
||||
|
||||
schedule_t*
|
||||
schedule_create(uuid_t uid, uint16_t length, uint16_t *periods_blob);
|
||||
|
||||
int
|
||||
schedule_save(schedule_t *schedule);
|
||||
|
||||
schedule_t*
|
||||
schedule_get_by_uid(uuid_t uid);
|
||||
|
||||
schedule_t**
|
||||
schedule_get_relay_weekdays(int relay_id);
|
||||
|
||||
uint16_t*
|
||||
schedule_periods_to_blob(schedule_t *schedule);
|
||||
|
||||
void
|
||||
schedule_free(schedule_t *schedule);
|
||||
|
||||
void
|
||||
schedule_free_list(schedule_t **schedules_list);
|
||||
|
||||
void
|
||||
schedule_debug(schedule_t *schedule);
|
||||
|
||||
int
|
||||
schedule_uid_parse(const char *uid_str, uuid_t result);
|
||||
|
||||
void
|
||||
schedule_uid_unparse(const uuid_t uid, char *result);
|
||||
|
||||
#endif /* CONTROLLER_SCHEDULE_H */
|
|
@ -1,11 +0,0 @@
|
|||
#ifndef CONTROLLER_RUNNERS_H
|
||||
#define CONTROLLER_RUNNERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <models/controller.h>
|
||||
|
||||
void
|
||||
runner_test();
|
||||
|
||||
#endif /* CONTROLLER_RUNNERS_H */
|
|
@ -1,16 +0,0 @@
|
|||
#ifndef CONTROLLER_WIRING_DEBUG_H
|
||||
#define CONTROLLER_WIRING_DEBUG_H
|
||||
|
||||
#include <logger.h>
|
||||
|
||||
#ifdef WIRING_PI_DEBUG
|
||||
#define LOG_WIRING_PI LOGGER_DEBUG
|
||||
#define wiringPiSetup() LOG_WIRING_PI("wiringPi wiringPiSetup()\n")
|
||||
#define wiringPiSetupSys() LOG_WIRING_PI("wiringPi wiringPiSetupSys()\n")
|
||||
#define pinMode(x,y) LOG_WIRING_PI("wiringPi pinMode(%d, %d)\n", x, y)
|
||||
#define digitalWrite(x,y) LOG_WIRING_PI("wiringPi digitalWrite(%d, %d)\n", x, y)
|
||||
|
||||
#define piFaceSetup(x) LOG_WIRING_PI("wiringPi piFaceSetup(%d)\n", x)
|
||||
#endif
|
||||
|
||||
#endif /* CONTROLLER_WIRING_DEBUG_H */
|
100
main.go
Normal file
100
main.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package main
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -g -Wall
|
||||
#cgo LDFLAGS: -lwiringPi -lwiringPiDev
|
||||
#include <wiringPi.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"emgauwa.app/emgauwa-core/models"
|
||||
"emgauwa.app/emgauwa-core/utils"
|
||||
)
|
||||
|
||||
type (
|
||||
EmgauwaValidator struct {
|
||||
validator *validator.Validate
|
||||
}
|
||||
)
|
||||
|
||||
func (cv *EmgauwaValidator) Validate(i interface{}) error {
|
||||
return cv.validator.Struct(i)
|
||||
}
|
||||
|
||||
func SkipperSwagger(c echo.Context) bool {
|
||||
if strings.Contains(c.Request().URL.Path, "swagger") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
utils.ConfigInit()
|
||||
utils.LoggerInit()
|
||||
models.DatabaseInit()
|
||||
}
|
||||
|
||||
// @title Emgauwa API
|
||||
// @version 1.0
|
||||
// @description This is a sample server server.
|
||||
// @termsOfService http://swagger.io/terms/
|
||||
|
||||
// @contact.name Tobias Reisinger
|
||||
// @contact.url https://serguzim.me
|
||||
// @contact.email tobias@msrg.cc
|
||||
func main() {
|
||||
|
||||
C.wiringPiSetup()
|
||||
|
||||
e := echo.New()
|
||||
|
||||
v := validator.New()
|
||||
v.RegisterValidation("hhmmformat", utils.ValidatorsHHMMFormat)
|
||||
e.Validator = &EmgauwaValidator{validator: v}
|
||||
|
||||
e.Pre(middleware.RemoveTrailingSlash())
|
||||
|
||||
e.Use(middleware.CORS())
|
||||
e.Use(middleware.GzipWithConfig(middleware.GzipConfig{
|
||||
Skipper: SkipperSwagger,
|
||||
}))
|
||||
e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{
|
||||
Format: "${time_rfc3339} [${method}] ${uri} > ${status} (${latency_human})\n",
|
||||
}))
|
||||
e.Use(middleware.Recover())
|
||||
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
|
||||
Skipper: SkipperSwagger,
|
||||
Root: viper.GetString("content-dir"),
|
||||
HTML5: true,
|
||||
}))
|
||||
|
||||
e.GET("api/swagger/*", echoSwagger.WrapHandler)
|
||||
e.GET("api/swagger", func(c echo.Context) error {
|
||||
return c.Redirect(http.StatusMovedPermanently, "/api/swagger/index.html")
|
||||
})
|
||||
|
||||
r_api_v1 := e.Group("api/v1")
|
||||
{
|
||||
|
||||
r_api_v1.GET("/schedules", api_v1.Schedules_GET)
|
||||
r_api_v1.POST("/schedules", api_v1.Schedules_POST)
|
||||
r_api_v1.GET("/schedules/:schedule_uid", api_v1.Schedules_UID_GET)
|
||||
r_api_v1.PUT("/schedules/:schedule_uid", api_v1.Schedules_UID_PUT)
|
||||
r_api_v1.DELETE("/schedules/:schedule_uid", api_v1.Schedules_UID_DELETE)
|
||||
r_api_v1.GET("/schedules/tag/:tag", api_v1.Schedules_Tag_TAG_GET)
|
||||
r_api_v1.POST("/schedules/list", api_v1.Schedules_List_POST)
|
||||
|
||||
r_api_v1.GET("/controllers", api_v1.Controllers_GET)
|
||||
|
||||
r_api_v1.GET("/tags", api_v1.Tags_GET)
|
||||
r_api_v1.GET("/ws/relays", api_v1.WS_Relays_GET)
|
||||
}
|
||||
|
||||
e.Start(viper.GetString("bind.http"))
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
create table controllers
|
||||
(
|
||||
id INTEGER
|
||||
PRIMARY KEY
|
||||
AUTOINCREMENT,
|
||||
uid BLOB
|
||||
NOT NULL
|
||||
UNIQUE,
|
||||
name VARCHAR(128),
|
||||
command_port INTEGER
|
||||
);
|
||||
|
||||
create table relays
|
||||
(
|
||||
id INTEGER
|
||||
PRIMARY KEY
|
||||
AUTOINCREMENT,
|
||||
number INTEGER
|
||||
NOT NULL,
|
||||
name VARCHAR(128)
|
||||
);
|
||||
|
||||
create table schedules
|
||||
(
|
||||
id INTEGER
|
||||
PRIMARY KEY
|
||||
AUTOINCREMENT,
|
||||
uid BLOB
|
||||
NOT NULL
|
||||
UNIQUE,
|
||||
name VARCHAR(128),
|
||||
periods BLOB
|
||||
);
|
||||
|
||||
create table junction_relay_schedule
|
||||
(
|
||||
weekday SMALLINT
|
||||
NOT NULL,
|
||||
relay_id INTEGER
|
||||
REFERENCES relays (id)
|
||||
ON DELETE CASCADE,
|
||||
schedule_id INTEGER
|
||||
DEFAULT 1
|
||||
REFERENCES schedules (id)
|
||||
ON DELETE SET DEFAULT
|
||||
);
|
||||
|
||||
INSERT INTO schedules (uid, name, periods) VALUES (x'6f666600000000000000000000000000', 'off', x'00');
|
||||
INSERT INTO schedules (uid, name, periods) VALUES (x'6f6e0000000000000000000000000000', 'on', x'010000009F05');
|
49
src/cli.c
49
src/cli.c
|
@ -1,49 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <argparse.h>
|
||||
|
||||
#include <cli.h>
|
||||
#include <config.h>
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
#include <version.h>
|
||||
|
||||
static const char *const usage[] = {
|
||||
"controller [options]",
|
||||
NULL,
|
||||
};
|
||||
|
||||
void
|
||||
cli_parse(int argc, const char **argv, cli_t *cli)
|
||||
{
|
||||
cli->config_file = NULL;
|
||||
cli->demo_mode = 0;
|
||||
|
||||
int version = 0;
|
||||
struct argparse_option options[] =
|
||||
{
|
||||
OPT_HELP(),
|
||||
OPT_GROUP("Basic options"),
|
||||
OPT_STRING('c', "config", &cli->config_file, "path to config file", NULL, 0, OPT_NONEG),
|
||||
OPT_BOOLEAN('d', "demo", &cli->demo_mode, "demo mode", NULL, 0, OPT_NONEG),
|
||||
OPT_BOOLEAN('v', "version", &version, "print version", NULL, 0, OPT_NONEG),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
struct argparse argparse;
|
||||
argparse_init(&argparse, options, usage, 0);
|
||||
argparse_describe(
|
||||
&argparse,
|
||||
"\nA brief description of what the program does and how it works.",
|
||||
"\nAdditional description of the program after the description of the arguments."
|
||||
);
|
||||
argparse_parse(&argparse, argc, argv);
|
||||
|
||||
if(version)
|
||||
{
|
||||
printf("%s\n", EMGAUWA_CONTROLLER_VERSION);
|
||||
exit(0);
|
||||
}
|
||||
}
|
442
src/config.c
442
src/config.c
|
@ -1,442 +0,0 @@
|
|||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <constants.h>
|
||||
#include <logger.h>
|
||||
|
||||
config_t *global_config;
|
||||
|
||||
static int
|
||||
config_load_log_level(config_t *config, char *value)
|
||||
{
|
||||
if(strcmp(value, "debug") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_DEBUG));
|
||||
config->logging.level = LOG_DEBUG;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(value, "info") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_INFO));
|
||||
config->logging.level = LOG_INFO;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(value, "notice") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_NOTICE));
|
||||
config->logging.level = LOG_NOTICE;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(value, "warning") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_WARNING));
|
||||
config->logging.level = LOG_WARNING;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(value, "err") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_ERR));
|
||||
config->logging.level = LOG_ERR;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(value, "crit") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_CRIT));
|
||||
config->logging.level = LOG_CRIT;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(value, "emerg") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_EMERG));
|
||||
config->logging.level = LOG_EMERG;
|
||||
return 0;
|
||||
}
|
||||
LOGGER_WARNING("invalid log-level '%s'\n", value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
config_load_log_file(config_t *config, char *value)
|
||||
{
|
||||
if(strcmp(value, "stdout") == 0)
|
||||
{
|
||||
config->logging.file = stdout;
|
||||
return 0;
|
||||
}
|
||||
if(strcmp(value, "stderr") == 0)
|
||||
{
|
||||
config->logging.file = stderr;
|
||||
return 0;
|
||||
}
|
||||
config->logging.file = fopen(value, "a+");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
config_init_relay_configs(config_t *config)
|
||||
{
|
||||
config->relay_configs = malloc(sizeof(config_relay_t) * config->relay_count);
|
||||
for(uint8_t i = 0; i < config->relay_count; ++i)
|
||||
{
|
||||
config->relay_configs[i].driver = RELAY_DRIVER_NONE;
|
||||
config->relay_configs[i].inverted = 0;
|
||||
config->relay_configs[i].init = -1;
|
||||
config->relay_configs[i].pin = 0;
|
||||
config->relay_configs[i].pulse_duration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config_load_section_controller(config_t *config, toml_table_t* controller)
|
||||
{
|
||||
toml_datum_t config_entry;
|
||||
|
||||
config_entry = toml_string_in(controller, "name");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->name, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(controller, "database");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->database, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(controller, "user");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->user, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(controller, "group");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->group, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(controller, "mqtt-host");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->mqtt_host, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_int_in(controller, "relays-init");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->relays_init = config_entry.u.i;
|
||||
}
|
||||
|
||||
|
||||
config_entry = toml_string_in(controller, "include");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_string(&config->include, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config_load_section_logging(config_t *config, toml_table_t* logging)
|
||||
{
|
||||
toml_datum_t config_entry;
|
||||
|
||||
config_entry = toml_string_in(logging, "level");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_log_level(config, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(logging, "file");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config_load_log_file(config, config_entry.u.s);
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config_load_section_ports(config_t *config, toml_table_t* ports)
|
||||
{
|
||||
toml_datum_t config_entry;
|
||||
|
||||
config_entry = toml_int_in(ports, "discovery");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->ports.discovery = config_entry.u.i;
|
||||
}
|
||||
|
||||
config_entry = toml_int_in(ports, "mqtt");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->ports.mqtt = config_entry.u.i;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
config_load_section_relay(config_t *config, toml_table_t* relay, int relay_num)
|
||||
{
|
||||
toml_datum_t config_entry;
|
||||
|
||||
config_entry = toml_int_in(relay, "pin");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->relay_configs[relay_num].pin = config_entry.u.i;
|
||||
}
|
||||
|
||||
config_entry = toml_int_in(relay, "inverted");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->relay_configs[relay_num].inverted = config_entry.u.i;
|
||||
}
|
||||
|
||||
config_entry = toml_int_in(relay, "init");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->relay_configs[relay_num].init = config_entry.u.i;
|
||||
}
|
||||
|
||||
config_entry = toml_int_in(relay, "pulse-duration");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
config->relay_configs[relay_num].pulse_duration = config_entry.u.i;
|
||||
}
|
||||
|
||||
config_entry = toml_string_in(relay, "driver");
|
||||
if(config_entry.ok)
|
||||
{
|
||||
for(int i = 0; config_entry.u.s[i] != '\0'; ++i)
|
||||
{
|
||||
config_entry.u.s[i] = tolower(config_entry.u.s[i]);
|
||||
}
|
||||
|
||||
if(strcmp(config_entry.u.s, "gpio") == 0)
|
||||
{
|
||||
config->relay_configs[relay_num].driver = RELAY_DRIVER_GPIO;
|
||||
}
|
||||
else if(strcmp(config_entry.u.s, "piface") == 0)
|
||||
{
|
||||
config->relay_configs[relay_num].driver = RELAY_DRIVER_PIFACE;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_WARNING("invalid driver '%s' in section for relay %d\n", config_entry.u.s, relay_num);
|
||||
}
|
||||
free(config_entry.u.s);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
config_init()
|
||||
{
|
||||
global_config = calloc(1, sizeof(config_t));
|
||||
|
||||
config_load_string(&global_config->name, DEFAULT_CONTROLLER_NAME);
|
||||
|
||||
config_load_string(&global_config->database, DEFAULT_DATABASE_PATH);
|
||||
|
||||
config_load_string(&global_config->mqtt_host, DEFAULT_MQTT_HOST);
|
||||
|
||||
|
||||
global_config->user = NULL;
|
||||
global_config->group = NULL;
|
||||
|
||||
global_config->include = NULL;
|
||||
|
||||
global_config->relays_init = 0;
|
||||
global_config->relay_count = 0;
|
||||
|
||||
config_load_string(&global_config->mqtt_host, "127.0.0.1");
|
||||
|
||||
global_config->ports.mqtt = DEFAULT_MQTT_PORT;
|
||||
global_config->ports.discovery = DEFAULT_DISCOVERY_PORT;
|
||||
|
||||
global_config->logging.level = LOG_DEBUG;
|
||||
global_config->logging.file = stdout;
|
||||
}
|
||||
|
||||
void
|
||||
config_free()
|
||||
{
|
||||
free(global_config->name);
|
||||
free(global_config->database);
|
||||
free(global_config->user);
|
||||
free(global_config->group);
|
||||
free(global_config->mqtt_host);
|
||||
free(global_config->include);
|
||||
|
||||
free(global_config);
|
||||
}
|
||||
|
||||
void
|
||||
config_load_string(char **holder, const char *value)
|
||||
{
|
||||
if(*holder)
|
||||
{
|
||||
free(*holder);
|
||||
}
|
||||
size_t value_len = strlen(value);
|
||||
|
||||
char *new_holder = malloc(sizeof(char) * (value_len + 1));
|
||||
strcpy(new_holder, value);
|
||||
new_holder[value_len] = '\0';
|
||||
|
||||
*holder = new_holder;
|
||||
}
|
||||
|
||||
static int
|
||||
config_try_file(const char *path)
|
||||
{
|
||||
if(access(path, F_OK) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if(access(path, R_OK) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
config_load(config_t *config, const char *cli_config_file)
|
||||
{
|
||||
if(cli_config_file)
|
||||
{
|
||||
if(config_try_file(cli_config_file) == 0)
|
||||
{
|
||||
config_load_file(config, cli_config_file);
|
||||
return;
|
||||
}
|
||||
LOGGER_CRIT("unable to open the passed config file '%s'\n", cli_config_file);
|
||||
exit(1);
|
||||
}
|
||||
if(config_try_file(DEFAULT_CONFIG_PATH) == 0)
|
||||
{
|
||||
config_load_file(config, DEFAULT_CONFIG_PATH);
|
||||
return;
|
||||
}
|
||||
if(config_try_file(DEFAULT_GLOBAL_CONFIG_PATH) == 0)
|
||||
{
|
||||
config_load_file(config, DEFAULT_GLOBAL_CONFIG_PATH);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
config_load_file(config_t *config, const char *file_name)
|
||||
{
|
||||
FILE *fp;
|
||||
toml_table_t* config_toml;
|
||||
char errbuf[256];
|
||||
|
||||
/* Open the file and parse content */
|
||||
fp = fopen(file_name, "r");
|
||||
if(fp == NULL) {
|
||||
LOGGER_CRIT("unable to open config file '%s'\n", file_name);
|
||||
exit(1);
|
||||
}
|
||||
config_toml = toml_parse_file(fp, errbuf, sizeof(errbuf));
|
||||
fclose(fp);
|
||||
if(config_toml == NULL) {
|
||||
LOGGER_CRIT("unable to parse config file '%s': %s\n", file_name, errbuf);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
toml_table_t* controller = toml_table_in(config_toml, "controller");
|
||||
if(controller)
|
||||
{
|
||||
config_load_section_controller(config, controller);
|
||||
}
|
||||
|
||||
toml_table_t* logging = toml_table_in(config_toml, "logging");
|
||||
if(logging)
|
||||
{
|
||||
config_load_section_logging(config, logging);
|
||||
}
|
||||
|
||||
toml_table_t* ports = toml_table_in(config_toml, "ports");
|
||||
if(ports)
|
||||
{
|
||||
config_load_section_ports(config, ports);
|
||||
}
|
||||
|
||||
toml_array_t* relays = toml_array_in(config_toml, "relays");
|
||||
if(relays)
|
||||
{
|
||||
config->relay_count = toml_array_nelem(relays);
|
||||
config_init_relay_configs(config);
|
||||
|
||||
for(int i = 0; i < config->relay_count; ++i)
|
||||
{
|
||||
config_load_section_relay(config, toml_table_at(relays, i), i);
|
||||
}
|
||||
}
|
||||
|
||||
toml_free(config_toml);
|
||||
|
||||
LOGGER_DEBUG("Loaded config from %s\n", file_name);
|
||||
}
|
||||
|
||||
void
|
||||
config_load_directory(config_t *config, const char *directory_name)
|
||||
{
|
||||
struct dirent *directory_entry;
|
||||
DIR *directory;
|
||||
|
||||
(void)config;
|
||||
|
||||
directory = opendir(directory_name);
|
||||
if(directory == NULL)
|
||||
{
|
||||
LOGGER_CRIT("cannot open directory '%s': %s\n", directory_name, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while((directory_entry = readdir(directory)) != NULL)
|
||||
{
|
||||
struct stat sb;
|
||||
const char *entry_name = directory_entry->d_name;
|
||||
|
||||
size_t copied = 0;
|
||||
|
||||
// Add 2 for '/' and '\0'.
|
||||
size_t path_len = strlen(directory_name) + strlen(entry_name) + 1;
|
||||
char *path = malloc(sizeof(char) * (path_len + 1));
|
||||
path[0] = '\0';
|
||||
|
||||
strncat(path + copied, directory_name, path_len - copied);
|
||||
copied = strlen(path);
|
||||
if(path[copied - 1] != '/')
|
||||
{
|
||||
strncat(path + copied, "/", path_len - copied);
|
||||
copied = strlen(path);
|
||||
}
|
||||
strncat(path + copied, entry_name, path_len - copied);
|
||||
|
||||
if(stat(path, &sb))
|
||||
{
|
||||
LOGGER_WARNING("failed to get info for '%s': %s\n", path, strerror(errno));
|
||||
}
|
||||
if(S_ISREG(sb.st_mode))
|
||||
{
|
||||
config_load_file(config, path);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
closedir(directory);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include <connections.h>
|
||||
#include <models/controller.h>
|
||||
#include <config.h>
|
||||
#include <handlers.h>
|
||||
|
||||
struct mg_connection *global_connection_mqtt;
|
||||
|
||||
struct mg_connection*
|
||||
connection_discovery_bind(struct mg_mgr *mgr)
|
||||
{
|
||||
char address[32];
|
||||
sprintf(address, "udp://0.0.0.0:%u", global_config->ports.discovery);
|
||||
struct mg_connection *c = mg_bind(mgr, address, handler_discovery);
|
||||
return c;
|
||||
}
|
||||
|
||||
struct mg_connection*
|
||||
connection_command_bind(struct mg_mgr *mgr)
|
||||
{
|
||||
char address[32];
|
||||
sprintf(address, "tcp://0.0.0.0:%u", global_controller->command_port);
|
||||
struct mg_connection *c = mg_bind(mgr, address, handler_command);
|
||||
return c;
|
||||
}
|
||||
|
||||
struct mg_connection*
|
||||
connection_mqtt_connect(struct mg_mgr *mgr)
|
||||
{
|
||||
char address[512];
|
||||
sprintf(address, "tcp://%s:%u", global_config->mqtt_host, global_config->ports.mqtt);
|
||||
struct mg_connection *c = mg_connect(mgr, address, handler_mqtt);
|
||||
return c;
|
||||
}
|
264
src/database.c
264
src/database.c
|
@ -1,264 +0,0 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <database.h>
|
||||
|
||||
#include <sql/migration_0.h>
|
||||
|
||||
sqlite3 *global_database;
|
||||
static int in_transaction;
|
||||
|
||||
void
|
||||
database_init()
|
||||
{
|
||||
int rc = sqlite3_open(global_config->database, &global_database);
|
||||
|
||||
if(rc)
|
||||
{
|
||||
LOGGER_CRIT("can't open database: %s\n", sqlite3_errmsg(global_database));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
database_migrate();
|
||||
|
||||
sqlite3_exec(global_database, "PRAGMA foreign_keys = ON", 0, 0, 0);
|
||||
in_transaction = 0;
|
||||
}
|
||||
|
||||
void
|
||||
database_free()
|
||||
{
|
||||
sqlite3_close(global_database);
|
||||
}
|
||||
|
||||
void
|
||||
database_migrate()
|
||||
{
|
||||
uint16_t version_num = 0;
|
||||
int s, rc;
|
||||
sqlite3_stmt *stmt;
|
||||
sqlite3_prepare_v2(global_database, "PRAGMA user_version;", -1, &stmt, NULL);
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
version_num = sqlite3_column_int(stmt, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
version_num = 0;
|
||||
}
|
||||
|
||||
uint16_t new_version_num = version_num;
|
||||
char* err_msg;
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
switch(version_num)
|
||||
{
|
||||
case 0:
|
||||
LOGGER_INFO("migrating LEVEL 0\n");
|
||||
rc = sqlite3_exec(global_database, (const char *)sql_migration_0_sql, NULL, NULL, &err_msg);
|
||||
if(rc)
|
||||
{
|
||||
LOGGER_CRIT("couldn't migrate LEVEL 0 (%s)\n", err_msg);
|
||||
exit(1);
|
||||
}
|
||||
new_version_num = 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
char pragma_query[32];
|
||||
sprintf(pragma_query, "PRAGMA user_version=%d;", new_version_num);
|
||||
sqlite3_exec(global_database, pragma_query, 0, 0, 0);
|
||||
LOGGER_DEBUG("storing new user_version %d\n", new_version_num);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
database_transaction_begin()
|
||||
{
|
||||
if(!in_transaction)
|
||||
{
|
||||
LOGGER_DEBUG("beginning transaction\n");
|
||||
sqlite3_exec(global_database, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
||||
in_transaction = 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
database_transaction_commit()
|
||||
{
|
||||
LOGGER_DEBUG("commiting transaction\n");
|
||||
sqlite3_exec(global_database, "COMMIT TRANSACTION;", NULL, NULL, NULL);
|
||||
in_transaction = 0;
|
||||
}
|
||||
|
||||
void
|
||||
database_transaction_rollback()
|
||||
{
|
||||
LOGGER_DEBUG("rolling back transaction\n");
|
||||
sqlite3_exec(global_database, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
|
||||
in_transaction = 0;
|
||||
}
|
||||
|
||||
int
|
||||
database_helper_get_id(sqlite3_stmt *stmt)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
result = sqlite3_column_int(stmt, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s == SQLITE_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error selecting id from database: %s\n", sqlite3_errstr(s));
|
||||
sqlite3_finalize(stmt);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int*
|
||||
database_helper_get_ids(sqlite3_stmt *stmt)
|
||||
{
|
||||
int *result = malloc(sizeof(int));
|
||||
int new_id;
|
||||
|
||||
int row = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
new_id = sqlite3_column_int(stmt, 0);
|
||||
if(new_id != 0) // found row for other target (relay <> schedule)
|
||||
{
|
||||
row++;
|
||||
|
||||
result = (int*)realloc(result, sizeof(int) * (row + 1));
|
||||
result[row - 1] = new_id;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s == SQLITE_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error selecting ids from database: %s\n", sqlite3_errstr(s));
|
||||
sqlite3_finalize(stmt);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
result[row] = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char*
|
||||
database_helper_get_string(sqlite3_stmt *stmt)
|
||||
{
|
||||
char *result = NULL;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
const char *found_string = (const char *)sqlite3_column_text(stmt, 0);
|
||||
result = (char*)malloc(sizeof(char) * (strlen(found_string) + 1));
|
||||
strcpy(result, found_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s == SQLITE_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error selecting string from database: %s\n", sqlite3_errstr(s));
|
||||
sqlite3_finalize(stmt);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
char**
|
||||
database_helper_get_strings(sqlite3_stmt *stmt)
|
||||
{
|
||||
char **result = malloc(sizeof(char*));
|
||||
|
||||
int row = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
const char *new_string = (const char *)sqlite3_column_text(stmt, 0);
|
||||
int new_string_len = strlen(new_string);
|
||||
row++;
|
||||
|
||||
result = (char**)realloc(result, sizeof(char*) * (row + 1));
|
||||
result[row - 1] = malloc(sizeof(char) * (new_string_len + 1));
|
||||
strcpy(result[row - 1], new_string);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(s == SQLITE_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error selecting strings from database: %s\n", sqlite3_errstr(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
result[row] = NULL;
|
||||
return result;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#include <wiringPi.h>
|
||||
#include <piFace.h>
|
||||
#include <wiring_debug.h>
|
||||
|
||||
#include <drivers.h>
|
||||
|
||||
void
|
||||
driver_gpio_set(int pin, int value)
|
||||
{
|
||||
digitalWrite(pin, value);
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#include <wiringPi.h>
|
||||
#include <piFace.h>
|
||||
#include <wiring_debug.h>
|
||||
|
||||
#include <drivers.h>
|
||||
|
||||
void
|
||||
driver_piface_set(int pin, int value)
|
||||
{
|
||||
digitalWrite(PIFACE_GPIO_BASE + pin, value);
|
||||
}
|
|
@ -1,251 +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 <database.h>
|
||||
#include <handlers.h>
|
||||
#include <helpers.h>
|
||||
#include <mpack.h>
|
||||
#include <models/controller.h>
|
||||
#include <models/relay.h>
|
||||
#include <models/schedule.h>
|
||||
#include <models/junction_relay_schedule.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COMMAND_CODE_CONTROLLER_ID_GET = 0,
|
||||
COMMAND_CODE_CONTROLLER_TIME_GET = 1,
|
||||
COMMAND_CODE_CONTROLLER_NAME_SET = 2,
|
||||
COMMAND_CODE_CONTROLLER_NAME_GET = 3,
|
||||
|
||||
COMMAND_CODE_RELAY_SCHEDULES_SET = 100,
|
||||
COMMAND_CODE_RELAY_SCHEDULES_GET = 101,
|
||||
COMMAND_CODE_RELAY_NAME_SET = 102,
|
||||
COMMAND_CODE_RELAY_NAME_GET = 103,
|
||||
COMMAND_CODE_RELAY_PULSE = 200,
|
||||
|
||||
COMMAND_CODE_SCHEDULE_UPDATE = 300
|
||||
} command_code_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
COMMAND_MAPPING_CODE = 0,
|
||||
COMMAND_MAPPING_NAME = 1,
|
||||
COMMAND_MAPPING_RELAY_NUM = 2,
|
||||
COMMAND_MAPPING_SCHEDULES_ARRAY = 3,
|
||||
COMMAND_MAPPING_SCHEDULE_ID = 4,
|
||||
COMMAND_MAPPING_PERIODS_COUNT = 5,
|
||||
COMMAND_MAPPING_PERIODS_BLOB = 6,
|
||||
COMMAND_MAPPING_PULSE_DURATION = 7,
|
||||
} control_mapping_t;
|
||||
|
||||
static void
|
||||
handler_command_relay_pulse(mpack_node_t map)
|
||||
{
|
||||
uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM));
|
||||
|
||||
if(relay_num > global_config->relay_count)
|
||||
{
|
||||
LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config->relay_count);
|
||||
return;
|
||||
}
|
||||
|
||||
relay_t *target_relay = global_controller->relays[relay_num];
|
||||
(void)target_relay;
|
||||
|
||||
int duration = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_PULSE_DURATION));
|
||||
if(duration == 0)
|
||||
{
|
||||
duration = global_config->relay_configs[relay_num].pulse_duration;
|
||||
}
|
||||
target_relay->pulse_timer = duration;
|
||||
LOGGER_DEBUG("pulsing relay %d for %ds\n", relay_num, duration);
|
||||
}
|
||||
|
||||
static void
|
||||
handler_command_controller_name_set(mpack_node_t map)
|
||||
{
|
||||
char name_buffer[MAX_NAME_LENGTH + 1];
|
||||
mpack_node_copy_cstr(mpack_node_map_uint(map, COMMAND_MAPPING_NAME), name_buffer, MAX_NAME_LENGTH + 1);
|
||||
controller_set_name(global_controller, name_buffer);
|
||||
LOGGER_DEBUG("setting new name %s for controller\n", name_buffer);
|
||||
controller_save();
|
||||
}
|
||||
|
||||
static void
|
||||
handler_command_relay_name_set(mpack_node_t map)
|
||||
{
|
||||
uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM));
|
||||
const char *relay_name = mpack_node_str(mpack_node_map_uint(map, COMMAND_MAPPING_NAME));
|
||||
|
||||
if(relay_num > global_config->relay_count)
|
||||
{
|
||||
LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config->relay_count);
|
||||
return;
|
||||
}
|
||||
relay_set_name(global_controller->relays[relay_num], relay_name);
|
||||
LOGGER_DEBUG("setting new name %s for relay %d\n", relay_name, relay_num);
|
||||
relay_save(global_controller->relays[relay_num]);
|
||||
}
|
||||
|
||||
static void
|
||||
handler_command_schedule_update(mpack_node_t map)
|
||||
{
|
||||
uuid_t schedule_uid;
|
||||
memcpy(schedule_uid, mpack_node_data(mpack_node_map_uint(map, COMMAND_MAPPING_SCHEDULE_ID)), sizeof(uuid_t));
|
||||
|
||||
uint16_t periods_count = mpack_node_u16(mpack_node_map_uint(map, COMMAND_MAPPING_PERIODS_COUNT));
|
||||
uint16_t *periods = (uint16_t*)mpack_node_bin_data(mpack_node_map_uint(map, COMMAND_MAPPING_PERIODS_BLOB));
|
||||
|
||||
schedule_t *schedule = schedule_get_by_uid(schedule_uid);
|
||||
|
||||
schedule_t *new_schedule = schedule_create(schedule_uid, periods_count, periods);
|
||||
|
||||
if(schedule)
|
||||
{
|
||||
new_schedule->id = schedule->id;
|
||||
schedule_free(schedule);
|
||||
}
|
||||
|
||||
schedule_save(new_schedule);
|
||||
|
||||
int *relay_ids = junction_relay_schedule_get_relay_ids_with_schedule(new_schedule->id);
|
||||
|
||||
for(int i = 0; i < global_config->relay_count; ++i)
|
||||
{
|
||||
for(int j = 0; relay_ids[j] != 0; ++j)
|
||||
{
|
||||
if(global_controller->relays[i]->id == relay_ids[j])
|
||||
{
|
||||
relay_reload_schedules(global_controller->relays[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(relay_ids);
|
||||
schedule_free(new_schedule);
|
||||
}
|
||||
|
||||
static void
|
||||
handler_command_relay_schedules_set(mpack_node_t map)
|
||||
{
|
||||
uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM));
|
||||
|
||||
if(relay_num > global_config->relay_count)
|
||||
{
|
||||
LOGGER_WARNING("relay %d is not available (relay count: %d\n", relay_num, global_config->relay_count);
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER_DEBUG("setting schedules for relay %d\n", relay_num);
|
||||
relay_t *target_relay = global_controller->relays[relay_num];
|
||||
|
||||
database_transaction_begin();
|
||||
|
||||
junction_relay_schedule_remove_for_relay(target_relay->id);
|
||||
|
||||
mpack_node_t schedules_array = mpack_node_map_uint(map, COMMAND_MAPPING_SCHEDULES_ARRAY);
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
mpack_node_t schedule_map = mpack_node_array_at(schedules_array, i);
|
||||
|
||||
uuid_t schedule_uid;
|
||||
memcpy(schedule_uid, mpack_node_data(mpack_node_map_uint(schedule_map, COMMAND_MAPPING_SCHEDULE_ID)), sizeof(uuid_t));
|
||||
|
||||
uint16_t periods_count = mpack_node_u16(mpack_node_map_uint(schedule_map, COMMAND_MAPPING_PERIODS_COUNT));
|
||||
uint16_t *periods = (uint16_t*)mpack_node_bin_data(mpack_node_map_uint(schedule_map, COMMAND_MAPPING_PERIODS_BLOB));
|
||||
|
||||
schedule_t *schedule = schedule_get_by_uid(schedule_uid);
|
||||
|
||||
schedule_t *new_schedule = schedule_create(schedule_uid, periods_count, periods);
|
||||
|
||||
if(schedule)
|
||||
{
|
||||
new_schedule->id = schedule->id;
|
||||
schedule_free(schedule);
|
||||
}
|
||||
|
||||
schedule_save(new_schedule);
|
||||
|
||||
junction_relay_schedule_insert(i, target_relay->id, new_schedule->id);
|
||||
}
|
||||
|
||||
relay_reload_schedules(target_relay);
|
||||
|
||||
database_transaction_commit();
|
||||
}
|
||||
|
||||
void
|
||||
handler_command(struct mg_connection *c, int ev, void *ev_data)
|
||||
{
|
||||
(void)ev_data;
|
||||
if(ev != MG_EV_RECV)
|
||||
{
|
||||
return;
|
||||
}
|
||||
uint32_t payload_length = *((uint32_t*)c->recv_mbuf.buf);
|
||||
|
||||
if(c->recv_mbuf.len < payload_length + sizeof(payload_length))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char *payload = c->recv_mbuf.buf + sizeof(payload_length);
|
||||
|
||||
mpack_tree_t tree;
|
||||
mpack_tree_init_data(&tree, payload, payload_length);
|
||||
mpack_tree_parse(&tree);
|
||||
mpack_node_t root = mpack_tree_root(&tree);
|
||||
|
||||
uint16_t command_code = mpack_node_u16(mpack_node_map_uint(root, COMMAND_MAPPING_CODE));
|
||||
|
||||
LOGGER_DEBUG("received command %d\n", command_code);
|
||||
|
||||
switch(command_code)
|
||||
{
|
||||
case COMMAND_CODE_CONTROLLER_ID_GET:
|
||||
break;
|
||||
case COMMAND_CODE_CONTROLLER_TIME_GET:
|
||||
break;
|
||||
case COMMAND_CODE_CONTROLLER_NAME_SET:
|
||||
handler_command_controller_name_set(root);
|
||||
break;
|
||||
case COMMAND_CODE_CONTROLLER_NAME_GET:
|
||||
break;
|
||||
case COMMAND_CODE_RELAY_SCHEDULES_SET:
|
||||
handler_command_relay_schedules_set(root);
|
||||
break;
|
||||
case COMMAND_CODE_RELAY_SCHEDULES_GET:
|
||||
break;
|
||||
case COMMAND_CODE_RELAY_NAME_SET:
|
||||
handler_command_relay_name_set(root);
|
||||
break;
|
||||
case COMMAND_CODE_RELAY_NAME_GET:
|
||||
break;
|
||||
case COMMAND_CODE_RELAY_PULSE:
|
||||
handler_command_relay_pulse(root);
|
||||
break;
|
||||
case COMMAND_CODE_SCHEDULE_UPDATE:
|
||||
handler_command_schedule_update(root);
|
||||
break;
|
||||
default:
|
||||
LOGGER_ERR("received invalid command\n");
|
||||
}
|
||||
|
||||
if(mpack_tree_destroy(&tree) != mpack_ok)
|
||||
{
|
||||
LOGGER_WARNING("error when destroying mpack tree\n");
|
||||
}
|
||||
LOGGER_DEBUG("done with command %d - closing connection\n", command_code);
|
||||
c->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
|
@ -1,95 +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 <mongoose.h>
|
||||
#include <handlers.h>
|
||||
#include <helpers.h>
|
||||
#include <mpack.h>
|
||||
#include <enums.h>
|
||||
|
||||
void
|
||||
handler_discovery(struct mg_connection *c, int ev, void *ev_data)
|
||||
{
|
||||
(void)ev_data;
|
||||
if(ev == MG_EV_RECV)
|
||||
{
|
||||
uint16_t discovery_answer_port;
|
||||
char ip_buf[32];
|
||||
mg_conn_addr_to_str(c, ip_buf, sizeof(ip_buf), MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_REMOTE);
|
||||
|
||||
if(c->recv_mbuf.len != sizeof(discovery_answer_port))
|
||||
{
|
||||
LOGGER_ERR("received invalid discovery from %s\n", ip_buf);
|
||||
return;
|
||||
}
|
||||
discovery_answer_port = *((uint16_t*)c->recv_mbuf.buf);
|
||||
|
||||
LOGGER_INFO("received discovery from %s:%d\n", ip_buf, discovery_answer_port);
|
||||
|
||||
if(discovery_answer_port == 0)
|
||||
{
|
||||
LOGGER_ERR("invalid port received\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char* payload;
|
||||
size_t payload_size;
|
||||
mpack_writer_t writer;
|
||||
mpack_writer_init_growable(&writer, &payload, &payload_size);
|
||||
|
||||
mpack_start_map(&writer, 4);
|
||||
mpack_write_uint(&writer, DISCOVERY_MAPPING_ID);
|
||||
mpack_write_bin(&writer, (char*)global_controller->uid, sizeof(uuid_t));
|
||||
mpack_write_uint(&writer, DISCOVERY_MAPPING_COMMAND_PORT);
|
||||
mpack_write_u16(&writer, global_controller->command_port);
|
||||
mpack_write_uint(&writer, DISCOVERY_MAPPING_RELAY_COUNT);
|
||||
mpack_write_u8(&writer, global_config->relay_count);
|
||||
mpack_write_uint(&writer, DISCOVERY_MAPPING_NAME);
|
||||
mpack_write_cstr(&writer, global_controller->name);
|
||||
mpack_finish_map(&writer);
|
||||
|
||||
// finish writing
|
||||
if(mpack_writer_destroy(&writer) != mpack_ok)
|
||||
{
|
||||
LOGGER_ERR("error writing discovery answer payload\n");
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
size_t bytes_transferred;
|
||||
int fd_answer = helper_connect_tcp_server(ip_buf, discovery_answer_port);
|
||||
if(fd_answer == -1)
|
||||
{
|
||||
LOGGER_ERR("error during connecting\n");
|
||||
free(payload);
|
||||
return;
|
||||
}
|
||||
|
||||
if((bytes_transferred = send(fd_answer, &payload_size, sizeof(payload_size), 0)) <= 0)
|
||||
{
|
||||
LOGGER_ERR("error during sending\n");
|
||||
free(payload);
|
||||
close(fd_answer);
|
||||
return;
|
||||
}
|
||||
if((bytes_transferred = send(fd_answer, payload, payload_size, 0)) <= 0)
|
||||
{
|
||||
LOGGER_ERR("error during sending\n");
|
||||
free(payload);
|
||||
close(fd_answer);
|
||||
return;
|
||||
}
|
||||
|
||||
free(payload);
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <models/controller.h>
|
||||
#include <handlers.h>
|
||||
#include <drivers.h>
|
||||
#include <enums.h>
|
||||
#include <helpers.h>
|
||||
#include <wiringPi.h>
|
||||
#include <wiring_debug.h>
|
||||
|
||||
void
|
||||
handler_loop(struct mg_connection *c_mqtt)
|
||||
{
|
||||
char topic_buf[100];
|
||||
char payload_buf[2];
|
||||
char controller_uid[UUID_STR_LEN];
|
||||
uuid_unparse(global_controller->uid, controller_uid);
|
||||
|
||||
struct tm time_last, time_now;
|
||||
time_t timestamp;
|
||||
|
||||
timestamp = time(NULL) - 1;
|
||||
localtime_r(×tamp, &time_last);
|
||||
timestamp = time(NULL);
|
||||
localtime_r(×tamp, &time_now);
|
||||
for(uint_fast8_t i = 0; i < global_config->relay_count; ++i)
|
||||
{
|
||||
relay_t *relay = global_controller->relays[i];
|
||||
int is_on = 0;
|
||||
|
||||
int is_on_schedule = relay_is_on_schedule(relay, &time_now);
|
||||
int pulse_relay = global_config->relay_configs[i].pulse_duration;
|
||||
|
||||
if(is_on_schedule)
|
||||
{
|
||||
if(!pulse_relay)
|
||||
{
|
||||
is_on = 1;
|
||||
LOGGER_DEBUG("relay %d is active\n", i);
|
||||
}
|
||||
if(pulse_relay && relay->pulse_timer)
|
||||
{
|
||||
is_on = 1;
|
||||
--relay->pulse_timer;
|
||||
LOGGER_DEBUG("relay %d is pulsing for %d more seconds\n", i, relay->pulse_timer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
relay->pulse_timer = 0;
|
||||
}
|
||||
|
||||
if(relay->is_on != is_on)
|
||||
{
|
||||
relay->sent_to_broker = 0;
|
||||
}
|
||||
if(!relay->sent_to_broker && c_mqtt)
|
||||
{
|
||||
sprintf(topic_buf, "controller/%s/relay/%u", controller_uid, i);
|
||||
sprintf(payload_buf, "%u", is_on);
|
||||
mg_mqtt_publish(c_mqtt, topic_buf, 0, MG_MQTT_QOS(0), payload_buf, strlen(payload_buf));
|
||||
relay->sent_to_broker = (rand() % 45) + 15;
|
||||
LOGGER_DEBUG("sent relay %d status (%d) to mqtt broker\n", i, is_on);
|
||||
}
|
||||
if(relay->sent_to_broker)
|
||||
{
|
||||
--relay->sent_to_broker;
|
||||
}
|
||||
relay->is_on = is_on;
|
||||
|
||||
if(global_config->relay_configs[i].inverted)
|
||||
{
|
||||
is_on = !is_on;
|
||||
}
|
||||
switch(global_config->relay_configs[i].driver)
|
||||
{
|
||||
case RELAY_DRIVER_GPIO:
|
||||
driver_gpio_set(global_config->relay_configs[i].pin, is_on);
|
||||
break;
|
||||
case RELAY_DRIVER_PIFACE:
|
||||
driver_piface_set(global_config->relay_configs[i].pin, is_on);
|
||||
break;
|
||||
default:
|
||||
LOGGER_WARNING("relay %d is not using a driver\n", i);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
#include <logger.h>
|
||||
#include <handlers.h>
|
||||
#include <connections.h>
|
||||
#include <models/controller.h>
|
||||
|
||||
void
|
||||
handler_mqtt(struct mg_connection *nc, int ev, void *p) {
|
||||
struct mg_mqtt_message *msg = (struct mg_mqtt_message *) p;
|
||||
(void) nc;
|
||||
|
||||
switch (ev)
|
||||
{
|
||||
case MG_EV_CONNECT:
|
||||
{
|
||||
struct mg_send_mqtt_handshake_opts opts;
|
||||
memset(&opts, 0, sizeof(opts));
|
||||
// TODO add password
|
||||
|
||||
mg_set_protocol_mqtt(nc);
|
||||
mg_send_mqtt_handshake_opt(nc, global_controller->name, opts);
|
||||
break;
|
||||
}
|
||||
case MG_EV_MQTT_CONNACK:
|
||||
if(msg->connack_ret_code != MG_EV_MQTT_CONNACK_ACCEPTED)
|
||||
{
|
||||
LOGGER_INFO("Got MQTT connection error: %d\n", msg->connack_ret_code);
|
||||
break;
|
||||
}
|
||||
if(!global_connection_mqtt)
|
||||
{
|
||||
LOGGER_DEBUG("connected to MQTT server\n");
|
||||
global_connection_mqtt = nc;
|
||||
}
|
||||
break;
|
||||
case MG_EV_CLOSE:
|
||||
if(global_connection_mqtt)
|
||||
{
|
||||
LOGGER_DEBUG("disconnected from MQTT server\n");
|
||||
}
|
||||
global_connection_mqtt = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.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)
|
||||
{
|
||||
LOGGER_ERR("getaddrinfo: %s\n", 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)
|
||||
{
|
||||
LOGGER_ERR("error binding socket: %s\n", strerror(errno));
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((status = listen(fd, max_client_backlog)) == -1)
|
||||
{
|
||||
LOGGER_ERR("error setting up listener: %s\n", strerror(errno));
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
return fd;
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
|
||||
int
|
||||
helper_connect_tcp_server(char* host, uint16_t port)
|
||||
{
|
||||
char port_str[6];
|
||||
sprintf(port_str, "%d", 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
|
||||
|
||||
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
|
||||
LOGGER_ERR("getaddrinfo: %s\n", gai_strerror(status));
|
||||
return -1;
|
||||
}
|
||||
|
||||
//res got filled out by getaddrinfo() for us
|
||||
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //creating Socket
|
||||
|
||||
if ((status = connect(s, res->ai_addr, res->ai_addrlen)) != 0) {
|
||||
LOGGER_ERR("connect() failed\n");
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
return s;
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <logger.h>
|
||||
|
||||
static uid_t
|
||||
get_uid_for_user(char *user)
|
||||
{
|
||||
if(user == NULL || user[0] == '\0')
|
||||
{
|
||||
return getuid();
|
||||
}
|
||||
struct passwd *pwd = calloc(1, sizeof(struct passwd));
|
||||
size_t buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char);
|
||||
char *buffer = malloc(buffer_len);
|
||||
getpwnam_r(user, pwd, buffer, buffer_len, &pwd);
|
||||
|
||||
if(pwd == NULL)
|
||||
{
|
||||
LOGGER_CRIT("couldn't find user to drop privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
uid_t result = pwd->pw_uid;
|
||||
free(buffer);
|
||||
free(pwd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static gid_t
|
||||
get_gid_for_group(char *group)
|
||||
{
|
||||
if(group == NULL || group[0] == '\0')
|
||||
{
|
||||
return getgid();
|
||||
}
|
||||
struct group *grp = calloc(1, sizeof(struct group));
|
||||
size_t buffer_len = sysconf(_SC_GETPW_R_SIZE_MAX) * sizeof(char);
|
||||
char *buffer = malloc(buffer_len);
|
||||
getgrnam_r(group, grp, buffer, buffer_len, &grp);
|
||||
|
||||
if(grp == NULL)
|
||||
{
|
||||
LOGGER_CRIT("couldn't find group to drop privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
gid_t result = grp->gr_gid;
|
||||
free(buffer);
|
||||
free(grp);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
helper_drop_privileges()
|
||||
{
|
||||
uid_t uid = get_uid_for_user(global_config->user);
|
||||
gid_t gid = get_gid_for_group(global_config->group);
|
||||
|
||||
LOGGER_DEBUG("drop privileges to %lu:%lu\n", uid, gid);
|
||||
|
||||
if (setgid(gid) == -1)
|
||||
{
|
||||
LOGGER_CRIT("failed to drop group privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
if (setuid(uid) == -1)
|
||||
{
|
||||
LOGGER_CRIT("failed to drop user privileges\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
#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)
|
||||
{
|
||||
LOGGER_ERR("could not get socket name for port: %s\n", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ntohs(sin.sin_port);
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#include <time.h>
|
||||
|
||||
#include <helpers.h>
|
||||
|
||||
int
|
||||
helper_get_weekday(const struct tm *time_struct)
|
||||
{
|
||||
int wday_sun_sat = time_struct->tm_wday;
|
||||
int wday_mon_sun = (wday_sun_sat + 6) % 7;
|
||||
return wday_mon_sun;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
#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)
|
||||
{
|
||||
LOGGER_CRIT("getaddrinfo: %s\n", 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)
|
||||
{
|
||||
LOGGER_CRIT("setsockopt: %s\n", strerror(errno));
|
||||
freeaddrinfo(res);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (bind(fd, res->ai_addr, res->ai_addrlen) == -1)
|
||||
{
|
||||
LOGGER_CRIT("bind: %s\n", strerror(errno));
|
||||
freeaddrinfo(res);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
LOGGER_INFO("opened discovery socket on port %u\n", discovery_port);
|
||||
|
||||
return fd;
|
||||
}
|
96
src/logger.c
96
src/logger.c
|
@ -1,96 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <logger.h>
|
||||
|
||||
const char *COLOR_DEBUG = COLOR_GREEN;
|
||||
const char *COLOR_INFO = COLOR_CYAN;
|
||||
const char *COLOR_NOTICE = COLOR_CYAN;
|
||||
const char *COLOR_WARNING = COLOR_YELLOW;
|
||||
const char *COLOR_ERR = COLOR_RED;
|
||||
const char *COLOR_CRIT = COLOR_MAGENTA;
|
||||
const char *COLOR_EMERG = COLOR_MAGENTA;
|
||||
|
||||
void
|
||||
logger_log(int level, const char *filename, int line, const char *func, const char *msg, ...)
|
||||
{
|
||||
if(global_config->logging.level < level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
va_list args;
|
||||
const char *level_str;
|
||||
const char *color;
|
||||
|
||||
switch(level)
|
||||
{
|
||||
case LOG_DEBUG:
|
||||
color = COLOR_DEBUG;
|
||||
level_str = "DEBUG";
|
||||
break;
|
||||
case LOG_INFO:
|
||||
color = COLOR_INFO;
|
||||
level_str = "INFO";
|
||||
break;
|
||||
case LOG_NOTICE:
|
||||
color = COLOR_NOTICE;
|
||||
level_str = "NOTE";
|
||||
break;
|
||||
case LOG_WARNING:
|
||||
color = COLOR_WARNING;
|
||||
level_str = "WARN";
|
||||
break;
|
||||
case LOG_ERR:
|
||||
color = COLOR_ERR;
|
||||
level_str = "ERROR";
|
||||
break;
|
||||
case LOG_CRIT:
|
||||
color = COLOR_CRIT;
|
||||
level_str = "CRIT";
|
||||
break;
|
||||
case LOG_EMERG:
|
||||
color = COLOR_EMERG;
|
||||
level_str = "EMERG";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
char timestamp_str[32];
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
strftime(timestamp_str, 32, "%Y-%m-%d %H:%M:%S", localtime(&rawtime));
|
||||
size_t timestamp_len = strlen(timestamp_str);
|
||||
|
||||
size_t buffer_size = 128;
|
||||
buffer_size += timestamp_len;
|
||||
buffer_size += strlen(filename);
|
||||
buffer_size += strlen(func);
|
||||
buffer_size += strlen(msg);
|
||||
|
||||
char *buffer = malloc(sizeof(char) * (buffer_size));
|
||||
sprintf(buffer, "%s %s[%5s] %s:%d:%s " COLOR_NONE "%s", timestamp_str, color, level_str, filename, line, func, msg);
|
||||
|
||||
// start arg va_list and find log_len
|
||||
va_start(args, msg);
|
||||
size_t log_len = vsnprintf(NULL, 0, buffer, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
|
||||
va_end(args);
|
||||
|
||||
char *log_line = malloc(sizeof(char) * (log_len + 1));
|
||||
|
||||
// start arg va_list again and write log_line
|
||||
va_start(args, msg);
|
||||
vsprintf(log_line, buffer, args); // NOLINT(clang-analyzer-valist.Uninitialized): clang-tidy bug
|
||||
va_end(args);
|
||||
|
||||
syslog(level, "%s", log_line + timestamp_len + 1);
|
||||
|
||||
fprintf(global_config->logging.file, "%s", log_line);
|
||||
fflush(global_config->logging.file);
|
||||
|
||||
free(buffer);
|
||||
free(log_line);
|
||||
}
|
189
src/main.c
189
src/main.c
|
@ -1,189 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <lmdb.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include <cli.h>
|
||||
#include <logger.h>
|
||||
#include <mongoose.h>
|
||||
#include <models/controller.h>
|
||||
#include <database.h>
|
||||
#include <config.h>
|
||||
#include <connections.h>
|
||||
#include <constants.h>
|
||||
#include <handlers.h>
|
||||
#include <drivers.h>
|
||||
#include <enums.h>
|
||||
#include <runners.h>
|
||||
#include <helpers.h>
|
||||
#include <wiringPi.h>
|
||||
#include <piFace.h>
|
||||
#include <wiring_debug.h>
|
||||
|
||||
static struct mg_mgr mgr;
|
||||
|
||||
static void
|
||||
terminate(int signum)
|
||||
{
|
||||
LOGGER_INFO("terminating controller (%d)\n", signum);
|
||||
|
||||
// TODO fix mg_mgr_free() causing loop (can't terminate)
|
||||
//LOGGER_DEBUG("freeing mongoose manager\n");
|
||||
//mg_mgr_free(&mgr);
|
||||
|
||||
LOGGER_DEBUG("closing database\n");
|
||||
database_free();
|
||||
|
||||
LOGGER_DEBUG("freeing global controller\n");
|
||||
controller_free(global_controller);
|
||||
|
||||
LOGGER_DEBUG("freeing relay configs config\n");
|
||||
free(global_config->relay_configs);
|
||||
|
||||
exit(signum);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The main function
|
||||
*
|
||||
* @param argc UNUSED
|
||||
* @param argv UNUSED
|
||||
*
|
||||
* @return Statuscode to indicate success (0) or failure (!0)
|
||||
*/
|
||||
int
|
||||
main(int argc, const char** argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
signal(SIGINT, terminate);
|
||||
signal(SIGABRT, terminate);
|
||||
signal(SIGTERM, terminate);
|
||||
|
||||
openlog("emgauwa-controller", 0, LOG_USER);
|
||||
setlogmask(LOG_UPTO(LOG_INFO));
|
||||
|
||||
|
||||
/******************** LOAD CONFIG ********************/
|
||||
|
||||
config_init();
|
||||
|
||||
cli_t cli;
|
||||
cli_parse(argc, argv, &cli);
|
||||
|
||||
config_load(global_config, cli.config_file);
|
||||
|
||||
if(global_config->logging.file == NULL)
|
||||
{
|
||||
global_config->logging.file = stdout;
|
||||
}
|
||||
|
||||
if(global_config->include)
|
||||
{
|
||||
config_load_directory(global_config, global_config->include);
|
||||
}
|
||||
|
||||
if(sizeof(time_t) < 8)
|
||||
{
|
||||
LOGGER_WARNING("this system is not using 8-bit time\n");
|
||||
}
|
||||
|
||||
|
||||
/******************** INIT DATABASE, SOCKETS AND THIS CONTROLLER ********************/
|
||||
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
|
||||
database_init();
|
||||
|
||||
global_controller = controller_load();
|
||||
if(!global_controller)
|
||||
{
|
||||
global_controller = controller_create();
|
||||
controller_save();
|
||||
}
|
||||
|
||||
connection_discovery_bind(&mgr);
|
||||
connection_mqtt_connect(&mgr);
|
||||
struct mg_connection *c_command = connection_command_bind(&mgr);
|
||||
|
||||
if(global_controller->command_port == 0)
|
||||
{
|
||||
global_controller->command_port = helper_get_port(c_command->sock);
|
||||
controller_save();
|
||||
}
|
||||
|
||||
controller_debug(global_controller);
|
||||
|
||||
helper_drop_privileges();
|
||||
|
||||
|
||||
/******************** SETUP WIRINGPI ********************/
|
||||
|
||||
wiringPiSetup();
|
||||
|
||||
int piface_setup = 0;
|
||||
|
||||
for(uint_fast8_t i = 0; i < global_config->relay_count; ++i)
|
||||
{
|
||||
int relay_default = global_config->relay_configs[i].init;
|
||||
if(relay_default == -1)
|
||||
{
|
||||
relay_default = global_config->relays_init;
|
||||
}
|
||||
if(relay_default == -1)
|
||||
{
|
||||
relay_default = global_config->relay_configs[i].inverted;
|
||||
}
|
||||
|
||||
if(global_config->relay_configs[i].driver == RELAY_DRIVER_GPIO)
|
||||
{
|
||||
pinMode(global_config->relay_configs[i].pin, OUTPUT);
|
||||
driver_gpio_set(global_config->relay_configs[i].pin, relay_default);
|
||||
}
|
||||
if(global_config->relay_configs[i].driver == RELAY_DRIVER_PIFACE)
|
||||
{
|
||||
if(!piface_setup)
|
||||
{
|
||||
piFaceSetup(PIFACE_GPIO_BASE);
|
||||
piface_setup = 1;
|
||||
}
|
||||
driver_piface_set(global_config->relay_configs[i].pin, relay_default);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******************** CHECK FOR TESTING RUN ********************/
|
||||
|
||||
if(cli.demo_mode)
|
||||
{
|
||||
runner_test(global_controller);
|
||||
terminate(0);
|
||||
}
|
||||
|
||||
|
||||
/******************** START MAIN LOOP ********************/
|
||||
|
||||
time_t timer = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
mg_mgr_poll(&mgr, 200);
|
||||
if(time(NULL) - timer >= 1)
|
||||
{
|
||||
if(!global_connection_mqtt)
|
||||
{
|
||||
connection_mqtt_connect(&mgr);
|
||||
}
|
||||
handler_loop(global_connection_mqtt);
|
||||
timer = time(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
terminate(0);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,256 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <models/controller.h>
|
||||
#include <logger.h>
|
||||
#include <config.h>
|
||||
#include <constants.h>
|
||||
#include <database.h>
|
||||
|
||||
controller_t *global_controller;
|
||||
|
||||
static int
|
||||
db_update_insert(controller_t *controller, sqlite3_stmt *stmt)
|
||||
{
|
||||
LOGGER_DEBUG("saving controller '%s' into database (id: %d)\n", controller->name, controller->id);
|
||||
int rc;
|
||||
|
||||
sqlite3_bind_int(stmt, 1, controller->id);
|
||||
sqlite3_bind_blob(stmt, 2, controller->uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 3, controller->name, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int(stmt, 4, controller->command_port);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc != SQLITE_DONE;
|
||||
}
|
||||
|
||||
static controller_t*
|
||||
controller_db_select_mapper(sqlite3_stmt *stmt)
|
||||
{
|
||||
controller_t *new_controller = malloc(sizeof(controller_t));
|
||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||
{
|
||||
const char *name = sqlite3_column_name(stmt, i);
|
||||
switch(name[0])
|
||||
{
|
||||
case 'i': // id
|
||||
new_controller->id = sqlite3_column_int(stmt, i);
|
||||
break;
|
||||
case 'c': // command_port
|
||||
new_controller->command_port = sqlite3_column_int(stmt, i);
|
||||
break;
|
||||
case 'n': // name
|
||||
strncpy(new_controller->name, (const char*)sqlite3_column_text(stmt, i), 127);
|
||||
break;
|
||||
case 'u': // uid
|
||||
uuid_copy(new_controller->uid, (const unsigned char*)sqlite3_column_blob(stmt, i));
|
||||
break;
|
||||
default: // ignore columns not implemented
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_controller->relays = malloc(sizeof(relay_t) * global_config->relay_count);
|
||||
uint8_t i;
|
||||
for(i = 0; i < global_config->relay_count; ++i)
|
||||
{
|
||||
new_controller->relays[i] = relay_load(i);
|
||||
if(!new_controller->relays[i])
|
||||
{
|
||||
new_controller->relays[i] = relay_create(i);
|
||||
relay_save(new_controller->relays[i]);
|
||||
}
|
||||
}
|
||||
return new_controller;
|
||||
}
|
||||
|
||||
static controller_t**
|
||||
controller_db_select(sqlite3_stmt *stmt)
|
||||
{
|
||||
controller_t **all_controllers = malloc(sizeof(controller_t*));
|
||||
|
||||
int row = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
controller_t *new_controller = controller_db_select_mapper(stmt);
|
||||
row++;
|
||||
|
||||
all_controllers = (controller_t**)realloc(all_controllers, sizeof(controller_t*) * (row + 1));
|
||||
all_controllers[row - 1] = new_controller;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(s == SQLITE_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error selecting controllers from database: %s\n", sqlite3_errstr(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
all_controllers[row] = NULL;
|
||||
return all_controllers;
|
||||
}
|
||||
|
||||
int
|
||||
controller_save()
|
||||
{
|
||||
int opened_transaction = database_transaction_begin();
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
if(global_controller->id)
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "UPDATE controllers set uid = ?2, name = ?3, command_port = ?4 WHERE id = ?1;", -1, &stmt, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "INSERT INTO controllers(uid, name, command_port) values (?2, ?3, ?4);", -1, &stmt, NULL);
|
||||
}
|
||||
|
||||
int result = db_update_insert(global_controller, stmt);
|
||||
|
||||
if(result)
|
||||
{
|
||||
if(global_controller->id)
|
||||
{
|
||||
LOGGER_ERR("error inserting data: %s\n", sqlite3_errmsg(global_database));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
||||
}
|
||||
|
||||
if(opened_transaction)
|
||||
{
|
||||
database_transaction_rollback();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!global_controller->id)
|
||||
{
|
||||
global_controller->id = sqlite3_last_insert_rowid(global_database);
|
||||
}
|
||||
|
||||
if(opened_transaction)
|
||||
{
|
||||
database_transaction_commit();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
controller_t*
|
||||
controller_load()
|
||||
{
|
||||
LOGGER_DEBUG("getting controller from database\n");
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM controllers LIMIT 1;", -1, &stmt, NULL);
|
||||
|
||||
controller_t **sql_result = controller_db_select(stmt);
|
||||
|
||||
controller_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
controller_remove(controller_t *controller)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
if(!controller->id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3_prepare_v2(global_database, "DELETE FROM controllers WHERE id=?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, controller->id);
|
||||
|
||||
int rc = sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc != SQLITE_DONE;
|
||||
}
|
||||
|
||||
controller_t*
|
||||
controller_create(void)
|
||||
{
|
||||
controller_t *new_controller = malloc(sizeof(*new_controller));
|
||||
new_controller->id = 0;
|
||||
uuid_generate(new_controller->uid);
|
||||
|
||||
strncpy(new_controller->name, global_config->name, MAX_NAME_LENGTH);
|
||||
new_controller->name[MAX_NAME_LENGTH] = '\0';
|
||||
|
||||
new_controller->command_port = 0;
|
||||
|
||||
new_controller->relays = malloc(sizeof(relay_t) * global_config->relay_count);
|
||||
uint8_t i;
|
||||
for(i = 0; i < global_config->relay_count; ++i)
|
||||
{
|
||||
new_controller->relays[i] = relay_load(i);
|
||||
if(!new_controller->relays[i])
|
||||
{
|
||||
new_controller->relays[i] = relay_create(i);
|
||||
relay_save(new_controller->relays[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return new_controller;
|
||||
}
|
||||
|
||||
void
|
||||
controller_set_name(controller_t *controller, const char *name)
|
||||
{
|
||||
strncpy(controller->name, name, MAX_NAME_LENGTH);
|
||||
controller->name[MAX_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
void
|
||||
controller_free(controller_t *controller)
|
||||
{
|
||||
for(int i = 0; i < global_config->relay_count; ++i)
|
||||
{
|
||||
relay_free(controller->relays[i]);
|
||||
}
|
||||
free(controller->relays);
|
||||
free(controller);
|
||||
}
|
||||
|
||||
void
|
||||
controller_debug(controller_t *controller)
|
||||
{
|
||||
if(controller == NULL)
|
||||
{
|
||||
LOGGER_DEBUG("controller is NULL\n");
|
||||
return;
|
||||
}
|
||||
char uuid_str[37];
|
||||
uuid_unparse(controller->uid, uuid_str);
|
||||
LOGGER_DEBUG("(1/3) %s @ %p\n", uuid_str, (void*)controller);
|
||||
LOGGER_DEBUG("(2/3) name: %s\n", controller->name);
|
||||
LOGGER_DEBUG("(3/3) relays @ %p:\n", (void*)controller->relays);
|
||||
for(int i = 0; i < global_config->relay_count; ++i)
|
||||
{
|
||||
relay_debug(controller->relays[i]);
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <models/junction_relay_schedule.h>
|
||||
#include <logger.h>
|
||||
#include <macros.h>
|
||||
#include <database.h>
|
||||
|
||||
int
|
||||
junction_relay_schedule_insert(uint8_t weekday, int relay_id, int schedule_id)
|
||||
{
|
||||
int rc;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "INSERT INTO junction_relay_schedule(weekday, schedule_id, relay_id) values (?1, ?2, ?3);", -1, &stmt, NULL);
|
||||
|
||||
sqlite3_bind_int(stmt, 1, weekday);
|
||||
sqlite3_bind_int(stmt, 2, schedule_id);
|
||||
sqlite3_bind_int(stmt, 3, relay_id);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE)
|
||||
{
|
||||
LOGGER_ERR("error inserting data: %s\n", sqlite3_errmsg(global_database));
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
junction_relay_schedule_insert_weekdays(int relay_id, int *schedule_ids)
|
||||
{
|
||||
int rc;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
static const char query_base[] = "INSERT INTO junction_relay_schedule (weekday, schedule_id, relay_id) VALUES";
|
||||
static const char query_extender[] = " (?, ?, ?)";
|
||||
|
||||
size_t query_len = STRLEN(query_base) + (7 * (STRLEN(query_extender) + 1)) + 1;
|
||||
char *query = malloc(sizeof(char) * query_len + 1);
|
||||
strncpy(query, query_base, query_len);
|
||||
query_len -= STRLEN(query_base);
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
strncat(query, query_extender, query_len);
|
||||
query_len -= STRLEN(query_extender);
|
||||
char *query_divider = (i < 7 - 1) ? "," : ";";
|
||||
strncat(query, query_divider, query_len);
|
||||
query_len -= 1;
|
||||
}
|
||||
|
||||
sqlite3_prepare_v2(global_database, query, -1, &stmt, NULL);
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
sqlite3_bind_int(stmt, i * 3 + 1, i);
|
||||
sqlite3_bind_int(stmt, i * 3 + 2, schedule_ids[i]);
|
||||
sqlite3_bind_int(stmt, i * 3 + 3, relay_id);
|
||||
}
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc != SQLITE_DONE)
|
||||
{
|
||||
LOGGER_ERR("error inserting data: %s", sqlite3_errmsg(global_database));
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
junction_relay_schedule_remove_for_relay(int relay_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "DELETE FROM junction_relay_schedule WHERE relay_id=?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, relay_id);
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc == SQLITE_DONE;
|
||||
}
|
||||
|
||||
|
||||
int*
|
||||
junction_relay_schedule_get_relay_ids_with_schedule(int schedule_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT DISTINCT relay_id FROM junction_relay_schedule WHERE schedule_id=?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||
|
||||
return database_helper_get_ids(stmt);
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <constants.h>
|
||||
#include <models/period.h>
|
||||
|
||||
int
|
||||
period_includes_time(period_t period, struct tm *time_struct)
|
||||
{
|
||||
uint16_t start = period.start;
|
||||
uint16_t end = period.end;
|
||||
|
||||
time_t timestamp = time_struct->tm_hour * 60;
|
||||
timestamp += time_struct->tm_min;
|
||||
|
||||
// "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;
|
||||
}
|
|
@ -1,293 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
#include <database.h>
|
||||
#include <models/relay.h>
|
||||
#include <models/junction_relay_schedule.h>
|
||||
|
||||
static int
|
||||
db_update_insert(relay_t *relay, sqlite3_stmt *stmt)
|
||||
{
|
||||
LOGGER_DEBUG("saving relay '%s' into database (id: %d)\n", relay->name, relay->id);
|
||||
int rc;
|
||||
|
||||
sqlite3_bind_int(stmt, 1, relay->id);
|
||||
sqlite3_bind_int(stmt, 2, relay->number);
|
||||
sqlite3_bind_text(stmt, 3, relay->name, -1, SQLITE_STATIC);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc != SQLITE_DONE;
|
||||
}
|
||||
static relay_t*
|
||||
relay_db_select_mapper(sqlite3_stmt *stmt)
|
||||
{
|
||||
relay_t *new_relay = malloc(sizeof(relay_t));
|
||||
new_relay->is_on = 0;
|
||||
|
||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||
{
|
||||
const char *name = sqlite3_column_name(stmt, i);
|
||||
switch(name[0])
|
||||
{
|
||||
case 'i':
|
||||
new_relay->id = sqlite3_column_int(stmt, i);
|
||||
break;
|
||||
case 'n':
|
||||
switch(name[1])
|
||||
{
|
||||
case 'a': // name
|
||||
strncpy(new_relay->name, (const char*)sqlite3_column_text(stmt, i), 127);
|
||||
break;
|
||||
case 'u': // number
|
||||
new_relay->number = sqlite3_column_int(stmt, i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default: // ignore columns not implemented
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
memset(new_relay->schedules, 0, sizeof(schedule_t*) * 7);
|
||||
relay_reload_schedules(new_relay);
|
||||
|
||||
new_relay->is_on = -1;
|
||||
new_relay->pulse_timer = 0;
|
||||
new_relay->sent_to_broker = 0;
|
||||
|
||||
return new_relay;
|
||||
}
|
||||
|
||||
static relay_t**
|
||||
relay_db_select(sqlite3_stmt *stmt)
|
||||
{
|
||||
relay_t **all_relays = malloc(sizeof(relay_t*));
|
||||
|
||||
int row = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
relay_t *new_relay = relay_db_select_mapper(stmt);
|
||||
row++;
|
||||
|
||||
all_relays = (relay_t**)realloc(all_relays, sizeof(relay_t*) * (row + 1));
|
||||
all_relays[row - 1] = new_relay;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(s == SQLITE_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error selecting relays from database: %s\n", sqlite3_errstr(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
all_relays[row] = NULL;
|
||||
return all_relays;
|
||||
}
|
||||
|
||||
int
|
||||
relay_save(relay_t *relay)
|
||||
{
|
||||
int opened_transaction = database_transaction_begin();
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
if(relay->id)
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "UPDATE relays set number = ?2, name = ?3 WHERE id = ?1;", -1, &stmt, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "INSERT INTO relays(number, name) values (?2, ?3);", -1, &stmt, NULL);
|
||||
}
|
||||
|
||||
int result = db_update_insert(relay, stmt);
|
||||
|
||||
if(result)
|
||||
{
|
||||
if(relay->id)
|
||||
{
|
||||
LOGGER_ERR("error inserting data: %s\n", sqlite3_errmsg(global_database));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
||||
}
|
||||
|
||||
if(opened_transaction)
|
||||
{
|
||||
database_transaction_rollback();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(relay->id == 0)
|
||||
{
|
||||
relay->id = sqlite3_last_insert_rowid(global_database);
|
||||
LOGGER_DEBUG("new relay - new id: %d\n", relay->id);
|
||||
}
|
||||
|
||||
LOGGER_DEBUG("cleaning relay_schedule junction\n");
|
||||
junction_relay_schedule_remove_for_relay(relay->id);
|
||||
|
||||
LOGGER_DEBUG("rebuilding relay_schedule junction\n");
|
||||
int schedule_ids[7];
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
schedule_ids[i] = relay->schedules[i]->id;
|
||||
}
|
||||
junction_relay_schedule_insert_weekdays(relay->id, schedule_ids);
|
||||
|
||||
if(opened_transaction)
|
||||
{
|
||||
database_transaction_commit();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
relay_t*
|
||||
relay_create(uint8_t number)
|
||||
{
|
||||
relay_t *new_relay = malloc(sizeof(relay_t));
|
||||
|
||||
new_relay->id = 0;
|
||||
new_relay->number = number;
|
||||
new_relay->name[0] = '\0';
|
||||
|
||||
new_relay->is_on = -1;
|
||||
new_relay->pulse_timer = 0;
|
||||
new_relay->sent_to_broker = 0;
|
||||
|
||||
uuid_t off_id;
|
||||
memset(off_id, 0, sizeof(uuid_t));
|
||||
memcpy(off_id, "off", 3);
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
new_relay->schedules[i] = schedule_get_by_uid(off_id);
|
||||
}
|
||||
|
||||
return new_relay;
|
||||
}
|
||||
|
||||
relay_t*
|
||||
relay_load(uint8_t number)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM relays WHERE number = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, number);
|
||||
|
||||
relay_t **sql_result = relay_db_select(stmt);
|
||||
|
||||
relay_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
relay_reload_schedules(relay_t *relay)
|
||||
{
|
||||
schedule_t **schedules = schedule_get_relay_weekdays(relay->id);
|
||||
|
||||
uuid_t off_id;
|
||||
memset(off_id, 0, sizeof(uuid_t));
|
||||
memcpy(off_id, "off", 3);
|
||||
|
||||
int fill_with_off = 0;
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
if(schedules[i] == NULL || fill_with_off)
|
||||
{
|
||||
LOGGER_WARNING("got only %d/7 schedules for relay_id %d\n", i, relay->id);
|
||||
relay->schedules[i] = schedule_get_by_uid(off_id);
|
||||
|
||||
fill_with_off = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(relay->schedules[i])
|
||||
{
|
||||
schedule_free(relay->schedules[i]);
|
||||
}
|
||||
relay->schedules[i] = schedules[i];
|
||||
}
|
||||
}
|
||||
|
||||
free(schedules); // don't free list, because contents are kept in relay->schedules
|
||||
}
|
||||
|
||||
void
|
||||
relay_set_name(relay_t *relay, const char *name)
|
||||
{
|
||||
strncpy(relay->name, name, MAX_NAME_LENGTH);
|
||||
relay->name[MAX_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
int
|
||||
relay_is_on_schedule(relay_t *relay, struct tm *time_struct)
|
||||
{
|
||||
schedule_t *schedule = relay->schedules[helper_get_weekday(time_struct)];
|
||||
if(schedule->periods_count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(uint16_t i = 0; i < schedule->periods_count; ++i)
|
||||
{
|
||||
if(period_includes_time(schedule->periods[i], time_struct))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
relay_debug(relay_t *relay)
|
||||
{
|
||||
if(relay == NULL)
|
||||
{
|
||||
LOGGER_DEBUG("relay is NULL\n");
|
||||
return;
|
||||
}
|
||||
LOGGER_DEBUG("(1/3) %3d @ %p\n", relay->number, (void*)relay);
|
||||
LOGGER_DEBUG("(2/3) id: %3d; name: %s\n", relay->id, relay->name);
|
||||
LOGGER_DEBUG("(3/3) schedules @ %p:\n", (void*)relay->schedules);
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
schedule_debug(relay->schedules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
relay_free(relay_t *relay)
|
||||
{
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
schedule_free(relay->schedules[i]);
|
||||
}
|
||||
free(relay);
|
||||
}
|
|
@ -1,322 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <logger.h>
|
||||
#include <database.h>
|
||||
#include <models/schedule.h>
|
||||
#include <models/junction_relay_schedule.h>
|
||||
|
||||
static int
|
||||
db_update_insert(schedule_t *schedule, sqlite3_stmt *stmt)
|
||||
{
|
||||
char uuid_str[UUID_STR_LEN];
|
||||
uuid_unparse(schedule->uid, uuid_str);
|
||||
LOGGER_DEBUG("saving schedule '%s' into database (id: %d)\n", uuid_str, schedule->id);
|
||||
|
||||
int rc;
|
||||
uint16_t *periods_blob = schedule_periods_to_blob(schedule);
|
||||
int blob_size = (int)sizeof(uint16_t) * ((periods_blob[0] * 2) + 1);
|
||||
|
||||
sqlite3_bind_int(stmt, 1, schedule->id);
|
||||
sqlite3_bind_blob(stmt, 2, schedule->uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||
sqlite3_bind_blob(stmt, 3, periods_blob, blob_size, SQLITE_STATIC);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
free(periods_blob);
|
||||
|
||||
return rc != SQLITE_DONE;
|
||||
}
|
||||
static schedule_t*
|
||||
schedule_db_select_mapper(sqlite3_stmt *stmt)
|
||||
{
|
||||
const uint16_t *periods_blob;
|
||||
schedule_t *new_schedule = malloc(sizeof(schedule_t));
|
||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||
{
|
||||
const char *name = sqlite3_column_name(stmt, i);
|
||||
switch(name[0])
|
||||
{
|
||||
case 'i': // id
|
||||
new_schedule->id = sqlite3_column_int(stmt, i);
|
||||
break;
|
||||
case 'p': // periods
|
||||
periods_blob = sqlite3_column_blob(stmt, i);
|
||||
new_schedule->periods_count = periods_blob[0];
|
||||
new_schedule->periods = malloc(sizeof(period_t) * periods_blob[0]);
|
||||
|
||||
for(int i = 0; i < periods_blob[0]; ++i)
|
||||
{
|
||||
new_schedule->periods[i].start = periods_blob[(i * 2) + 1];
|
||||
new_schedule->periods[i].end = periods_blob[(i * 2) + 2];
|
||||
}
|
||||
|
||||
break;
|
||||
case 'u': // uid
|
||||
uuid_copy(new_schedule->uid, (const unsigned char*)sqlite3_column_blob(stmt, i));
|
||||
break;
|
||||
default: // ignore columns not implemented
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new_schedule;
|
||||
}
|
||||
|
||||
static schedule_t**
|
||||
schedule_db_select(sqlite3_stmt *stmt)
|
||||
{
|
||||
schedule_t **all_schedules = malloc(sizeof(schedule_t*));
|
||||
|
||||
int row = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
schedule_t *new_schedule = schedule_db_select_mapper(stmt);
|
||||
row++;
|
||||
|
||||
all_schedules = (schedule_t**)realloc(all_schedules, sizeof(schedule_t*) * (row + 1));
|
||||
all_schedules[row - 1] = new_schedule;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(s == SQLITE_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error selecting schedules from database: %s\n", sqlite3_errstr(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
all_schedules[row] = NULL;
|
||||
return all_schedules;
|
||||
}
|
||||
|
||||
int
|
||||
schedule_save(schedule_t *schedule)
|
||||
{
|
||||
int opened_transaction = database_transaction_begin();
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
if(schedule->id)
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "UPDATE schedules SET uid = ?2, periods = ?3 WHERE id=?1;", -1, &stmt, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "INSERT INTO schedules(uid, periods) values (?2, ?3);", -1, &stmt, NULL);
|
||||
}
|
||||
|
||||
int result = db_update_insert(schedule, stmt);
|
||||
|
||||
if(result)
|
||||
{
|
||||
if(schedule->id)
|
||||
{
|
||||
LOGGER_ERR("error inserting data: %s\n", sqlite3_errmsg(global_database));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
||||
}
|
||||
|
||||
if(opened_transaction)
|
||||
{
|
||||
database_transaction_rollback();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!schedule->id)
|
||||
{
|
||||
schedule->id = sqlite3_last_insert_rowid(global_database);
|
||||
}
|
||||
|
||||
if(opened_transaction)
|
||||
{
|
||||
database_transaction_commit();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
schedule_t**
|
||||
schedule_get_relay_weekdays(int relay_id)
|
||||
{
|
||||
LOGGER_DEBUG("getting schedules [relay_id=%d] from database\n", relay_id);
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT schedules.* FROM schedules INNER JOIN junction_relay_schedule ON schedules.id == junction_relay_schedule.schedule_id WHERE junction_relay_schedule.relay_id = ?1 ORDER BY junction_relay_schedule.weekday ASC", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, relay_id);
|
||||
|
||||
return schedule_db_select(stmt);
|
||||
}
|
||||
|
||||
schedule_t*
|
||||
schedule_get_by_uid(uuid_t uid)
|
||||
{
|
||||
char uuid_str[UUID_STR_LEN];
|
||||
schedule_uid_unparse(uid, uuid_str);
|
||||
LOGGER_DEBUG("getting schedule [uid=%s] from database\n", uuid_str);
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules WHERE uid = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||
|
||||
schedule_t **sql_result = schedule_db_select(stmt);
|
||||
|
||||
schedule_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
schedule_t*
|
||||
schedule_create(uuid_t uid, uint16_t length, uint16_t *periods_blob)
|
||||
{
|
||||
schedule_t *new_schedule = malloc(sizeof(schedule_t));
|
||||
|
||||
new_schedule->id = 0;
|
||||
memmove(new_schedule->uid, uid, sizeof(uuid_t));
|
||||
|
||||
new_schedule->periods_count = length;
|
||||
new_schedule->periods = NULL;
|
||||
|
||||
if(length)
|
||||
{
|
||||
new_schedule->periods = malloc(sizeof(period_t) * length);
|
||||
|
||||
for(uint16_t i = 0; i < length; ++i)
|
||||
{
|
||||
new_schedule->periods[i].start = periods_blob[0 + (i * 2)];
|
||||
new_schedule->periods[i].end = periods_blob[1 + (i * 2)];
|
||||
}
|
||||
}
|
||||
|
||||
return new_schedule;
|
||||
}
|
||||
|
||||
uint16_t*
|
||||
schedule_periods_to_blob(schedule_t *schedule)
|
||||
{
|
||||
uint16_t *periods_blob = malloc(sizeof(uint16_t) * ((2 * schedule->periods_count) + 1));
|
||||
periods_blob[0] = schedule->periods_count;
|
||||
|
||||
for(uint16_t i = 0; i < schedule->periods_count; ++i)
|
||||
{
|
||||
|
||||
periods_blob[1 + (i * 2)] = schedule->periods[i].start;
|
||||
periods_blob[2 + (i * 2)] = schedule->periods[i].end;
|
||||
}
|
||||
|
||||
return periods_blob;
|
||||
}
|
||||
|
||||
int
|
||||
schedule_uid_parse(const char *uid_str, uuid_t result)
|
||||
{
|
||||
if(strcmp("off", uid_str) == 0)
|
||||
{
|
||||
memset(result, 0, sizeof(uuid_t));
|
||||
memcpy(result, "off", 3);
|
||||
return 0;
|
||||
}
|
||||
if(strcmp("on", uid_str) == 0)
|
||||
{
|
||||
memset(result, 0, sizeof(uuid_t));
|
||||
memcpy(result, "on", 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(uuid_parse(uid_str, result))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
schedule_uid_unparse(const uuid_t uid, char *result)
|
||||
{
|
||||
uuid_t tmp_uuid;
|
||||
|
||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||
memcpy(tmp_uuid, "off", 3);
|
||||
if(uuid_compare(uid, tmp_uuid) == 0)
|
||||
{
|
||||
strcpy(result, "off");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||
memcpy(tmp_uuid, "on", 2);
|
||||
if(uuid_compare(uid, tmp_uuid) == 0)
|
||||
{
|
||||
strcpy(result, "on");
|
||||
return;
|
||||
}
|
||||
|
||||
uuid_unparse(uid, result);
|
||||
}
|
||||
|
||||
void
|
||||
schedule_free(schedule_t *schedule)
|
||||
{
|
||||
free(schedule->periods);
|
||||
free(schedule);
|
||||
}
|
||||
|
||||
void
|
||||
schedule_free_list(schedule_t **schedules)
|
||||
{
|
||||
for(int i = 0; schedules[i] != NULL; ++i)
|
||||
{
|
||||
schedule_free(schedules[i]);
|
||||
}
|
||||
free(schedules);
|
||||
}
|
||||
|
||||
void
|
||||
schedule_debug(schedule_t *schedule)
|
||||
{
|
||||
if(schedule == NULL)
|
||||
{
|
||||
LOGGER_DEBUG("schedule is NULL\n");
|
||||
return;
|
||||
}
|
||||
char uuid_str[UUID_STR_LEN];
|
||||
schedule_uid_unparse(schedule->uid, uuid_str);
|
||||
LOGGER_DEBUG("(1/3) %s @ %p\n", uuid_str, (void*)schedule);
|
||||
LOGGER_DEBUG("(2/3) id: %3d; period count: %3d\n", schedule->id, schedule->periods_count);
|
||||
|
||||
// one block: "HH:MM-HH:MM, " --> size: 13 (14 with '\0')
|
||||
char *periods_debug_str = malloc(sizeof(char) * ((schedule->periods_count * 13) + 1));
|
||||
periods_debug_str[0] = '\0';
|
||||
|
||||
for(uint16_t i = 0; i < schedule->periods_count; ++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
|
||||
);
|
||||
}
|
||||
|
||||
LOGGER_DEBUG("(3/3) periods: %s\n", periods_debug_str);
|
||||
|
||||
free(periods_debug_str);
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include <runners.h>
|
||||
#include <logger.h>
|
||||
#include <drivers.h>
|
||||
#include <drivers.h>
|
||||
|
||||
void
|
||||
runner_test()
|
||||
{
|
||||
// from x down to 0 to turn all relays off in the end
|
||||
for(uint_fast8_t i = 0; i < global_config->relay_count; ++i)
|
||||
{
|
||||
for(int test_run = 2; test_run >= 0; --test_run)
|
||||
{
|
||||
int is_active = test_run % 2;
|
||||
if(global_config->relay_configs[i].inverted)
|
||||
{
|
||||
is_active = !is_active;
|
||||
}
|
||||
switch(global_config->relay_configs[i].driver)
|
||||
{
|
||||
case RELAY_DRIVER_GPIO:
|
||||
driver_gpio_set(global_config->relay_configs[i].pin, is_active);
|
||||
break;
|
||||
case RELAY_DRIVER_PIFACE:
|
||||
driver_piface_set(global_config->relay_configs[i].pin, is_active);
|
||||
break;
|
||||
default:
|
||||
LOGGER_WARNING("relay %d is not using a driver\n", i);
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
384
vendor/argparse.c
vendored
384
vendor/argparse.c
vendored
|
@ -1,384 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style license that can be found
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include "argparse.h"
|
||||
|
||||
#define OPT_UNSET 1
|
||||
#define OPT_LONG (1 << 1)
|
||||
|
||||
static const char *
|
||||
prefix_skip(const char *str, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
return strncmp(str, prefix, len) ? NULL : str + len;
|
||||
}
|
||||
|
||||
static int
|
||||
prefix_cmp(const char *str, const char *prefix)
|
||||
{
|
||||
for (;; str++, prefix++)
|
||||
if (!*prefix) {
|
||||
return 0;
|
||||
} else if (*str != *prefix) {
|
||||
return (unsigned char)*prefix - (unsigned char)*str;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
argparse_error(struct argparse *self, const struct argparse_option *opt,
|
||||
const char *reason, int flags)
|
||||
{
|
||||
(void)self;
|
||||
if (flags & OPT_LONG) {
|
||||
fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason);
|
||||
} else {
|
||||
fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
argparse_getvalue(struct argparse *self, const struct argparse_option *opt,
|
||||
int flags)
|
||||
{
|
||||
const char *s = NULL;
|
||||
if (!opt->value)
|
||||
goto skipped;
|
||||
switch (opt->type) {
|
||||
case ARGPARSE_OPT_BOOLEAN:
|
||||
if (flags & OPT_UNSET) {
|
||||
*(int *)opt->value = *(int *)opt->value - 1;
|
||||
} else {
|
||||
*(int *)opt->value = *(int *)opt->value + 1;
|
||||
}
|
||||
if (*(int *)opt->value < 0) {
|
||||
*(int *)opt->value = 0;
|
||||
}
|
||||
break;
|
||||
case ARGPARSE_OPT_BIT:
|
||||
if (flags & OPT_UNSET) {
|
||||
*(int *)opt->value &= ~opt->data;
|
||||
} else {
|
||||
*(int *)opt->value |= opt->data;
|
||||
}
|
||||
break;
|
||||
case ARGPARSE_OPT_STRING:
|
||||
if (self->optvalue) {
|
||||
*(const char **)opt->value = self->optvalue;
|
||||
self->optvalue = NULL;
|
||||
} else if (self->argc > 1) {
|
||||
self->argc--;
|
||||
*(const char **)opt->value = *++self->argv;
|
||||
} else {
|
||||
argparse_error(self, opt, "requires a value", flags);
|
||||
}
|
||||
break;
|
||||
case ARGPARSE_OPT_INTEGER:
|
||||
errno = 0;
|
||||
if (self->optvalue) {
|
||||
*(int *)opt->value = strtol(self->optvalue, (char **)&s, 0);
|
||||
self->optvalue = NULL;
|
||||
} else if (self->argc > 1) {
|
||||
self->argc--;
|
||||
*(int *)opt->value = strtol(*++self->argv, (char **)&s, 0);
|
||||
} else {
|
||||
argparse_error(self, opt, "requires a value", flags);
|
||||
}
|
||||
if (errno)
|
||||
argparse_error(self, opt, strerror(errno), flags);
|
||||
if (s[0] != '\0')
|
||||
argparse_error(self, opt, "expects an integer value", flags);
|
||||
break;
|
||||
case ARGPARSE_OPT_FLOAT:
|
||||
errno = 0;
|
||||
if (self->optvalue) {
|
||||
*(float *)opt->value = strtof(self->optvalue, (char **)&s);
|
||||
self->optvalue = NULL;
|
||||
} else if (self->argc > 1) {
|
||||
self->argc--;
|
||||
*(float *)opt->value = strtof(*++self->argv, (char **)&s);
|
||||
} else {
|
||||
argparse_error(self, opt, "requires a value", flags);
|
||||
}
|
||||
if (errno)
|
||||
argparse_error(self, opt, strerror(errno), flags);
|
||||
if (s[0] != '\0')
|
||||
argparse_error(self, opt, "expects a numerical value", flags);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
skipped:
|
||||
if (opt->callback) {
|
||||
return opt->callback(self, opt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
argparse_options_check(const struct argparse_option *options)
|
||||
{
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
switch (options->type) {
|
||||
case ARGPARSE_OPT_END:
|
||||
case ARGPARSE_OPT_BOOLEAN:
|
||||
case ARGPARSE_OPT_BIT:
|
||||
case ARGPARSE_OPT_INTEGER:
|
||||
case ARGPARSE_OPT_FLOAT:
|
||||
case ARGPARSE_OPT_STRING:
|
||||
case ARGPARSE_OPT_GROUP:
|
||||
continue;
|
||||
default:
|
||||
fprintf(stderr, "wrong option type: %d", options->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
argparse_short_opt(struct argparse *self, const struct argparse_option *options)
|
||||
{
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
if (options->short_name == *self->optvalue) {
|
||||
self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL;
|
||||
return argparse_getvalue(self, options, 0);
|
||||
}
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
static int
|
||||
argparse_long_opt(struct argparse *self, const struct argparse_option *options)
|
||||
{
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
const char *rest;
|
||||
int opt_flags = 0;
|
||||
if (!options->long_name)
|
||||
continue;
|
||||
|
||||
rest = prefix_skip(self->argv[0] + 2, options->long_name);
|
||||
if (!rest) {
|
||||
// negation disabled?
|
||||
if (options->flags & OPT_NONEG) {
|
||||
continue;
|
||||
}
|
||||
// only OPT_BOOLEAN/OPT_BIT supports negation
|
||||
if (options->type != ARGPARSE_OPT_BOOLEAN && options->type !=
|
||||
ARGPARSE_OPT_BIT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prefix_cmp(self->argv[0] + 2, "no-")) {
|
||||
continue;
|
||||
}
|
||||
rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name);
|
||||
if (!rest)
|
||||
continue;
|
||||
opt_flags |= OPT_UNSET;
|
||||
}
|
||||
if (*rest) {
|
||||
if (*rest != '=')
|
||||
continue;
|
||||
self->optvalue = rest + 1;
|
||||
}
|
||||
return argparse_getvalue(self, options, opt_flags | OPT_LONG);
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
int
|
||||
argparse_init(struct argparse *self, struct argparse_option *options,
|
||||
const char *const *usages, int flags)
|
||||
{
|
||||
memset(self, 0, sizeof(*self));
|
||||
self->options = options;
|
||||
self->usages = usages;
|
||||
self->flags = flags;
|
||||
self->description = NULL;
|
||||
self->epilog = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
argparse_describe(struct argparse *self, const char *description,
|
||||
const char *epilog)
|
||||
{
|
||||
self->description = description;
|
||||
self->epilog = epilog;
|
||||
}
|
||||
|
||||
int
|
||||
argparse_parse(struct argparse *self, int argc, const char **argv)
|
||||
{
|
||||
self->argc = argc - 1;
|
||||
self->argv = argv + 1;
|
||||
self->out = argv;
|
||||
|
||||
argparse_options_check(self->options);
|
||||
|
||||
for (; self->argc; self->argc--, self->argv++) {
|
||||
const char *arg = self->argv[0];
|
||||
if (arg[0] != '-' || !arg[1]) {
|
||||
if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) {
|
||||
goto end;
|
||||
}
|
||||
// if it's not option or is a single char '-', copy verbatim
|
||||
self->out[self->cpidx++] = self->argv[0];
|
||||
continue;
|
||||
}
|
||||
// short option
|
||||
if (arg[1] != '-') {
|
||||
self->optvalue = arg + 1;
|
||||
switch (argparse_short_opt(self, self->options)) {
|
||||
case -1:
|
||||
break;
|
||||
case -2:
|
||||
goto unknown;
|
||||
}
|
||||
while (self->optvalue) {
|
||||
switch (argparse_short_opt(self, self->options)) {
|
||||
case -1:
|
||||
break;
|
||||
case -2:
|
||||
goto unknown;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// if '--' presents
|
||||
if (!arg[2]) {
|
||||
self->argc--;
|
||||
self->argv++;
|
||||
break;
|
||||
}
|
||||
// long option
|
||||
switch (argparse_long_opt(self, self->options)) {
|
||||
case -1:
|
||||
break;
|
||||
case -2:
|
||||
goto unknown;
|
||||
}
|
||||
continue;
|
||||
|
||||
unknown:
|
||||
fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]);
|
||||
argparse_usage(self);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
end:
|
||||
memmove(self->out + self->cpidx, self->argv,
|
||||
self->argc * sizeof(*self->out));
|
||||
self->out[self->cpidx + self->argc] = NULL;
|
||||
|
||||
return self->cpidx + self->argc;
|
||||
}
|
||||
|
||||
void
|
||||
argparse_usage(struct argparse *self)
|
||||
{
|
||||
if (self->usages) {
|
||||
fprintf(stdout, "Usage: %s\n", *self->usages++);
|
||||
while (*self->usages && **self->usages)
|
||||
fprintf(stdout, " or: %s\n", *self->usages++);
|
||||
} else {
|
||||
fprintf(stdout, "Usage:\n");
|
||||
}
|
||||
|
||||
// print description
|
||||
if (self->description)
|
||||
fprintf(stdout, "%s\n", self->description);
|
||||
|
||||
fputc('\n', stdout);
|
||||
|
||||
const struct argparse_option *options;
|
||||
|
||||
// figure out best width
|
||||
size_t usage_opts_width = 0;
|
||||
size_t len;
|
||||
options = self->options;
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
len = 0;
|
||||
if ((options)->short_name) {
|
||||
len += 2;
|
||||
}
|
||||
if ((options)->short_name && (options)->long_name) {
|
||||
len += 2; // separator ", "
|
||||
}
|
||||
if ((options)->long_name) {
|
||||
len += strlen((options)->long_name) + 2;
|
||||
}
|
||||
if (options->type == ARGPARSE_OPT_INTEGER) {
|
||||
len += strlen("=<int>");
|
||||
}
|
||||
if (options->type == ARGPARSE_OPT_FLOAT) {
|
||||
len += strlen("=<flt>");
|
||||
} else if (options->type == ARGPARSE_OPT_STRING) {
|
||||
len += strlen("=<str>");
|
||||
}
|
||||
len = (len + 3) - ((len + 3) & 3);
|
||||
if (usage_opts_width < len) {
|
||||
usage_opts_width = len;
|
||||
}
|
||||
}
|
||||
usage_opts_width += 4; // 4 spaces prefix
|
||||
|
||||
options = self->options;
|
||||
for (; options->type != ARGPARSE_OPT_END; options++) {
|
||||
size_t pos = 0;
|
||||
int pad = 0;
|
||||
if (options->type == ARGPARSE_OPT_GROUP) {
|
||||
fputc('\n', stdout);
|
||||
fprintf(stdout, "%s", options->help);
|
||||
fputc('\n', stdout);
|
||||
continue;
|
||||
}
|
||||
pos = fprintf(stdout, " ");
|
||||
if (options->short_name) {
|
||||
pos += fprintf(stdout, "-%c", options->short_name);
|
||||
}
|
||||
if (options->long_name && options->short_name) {
|
||||
pos += fprintf(stdout, ", ");
|
||||
}
|
||||
if (options->long_name) {
|
||||
pos += fprintf(stdout, "--%s", options->long_name);
|
||||
}
|
||||
if (options->type == ARGPARSE_OPT_INTEGER) {
|
||||
pos += fprintf(stdout, "=<int>");
|
||||
} else if (options->type == ARGPARSE_OPT_FLOAT) {
|
||||
pos += fprintf(stdout, "=<flt>");
|
||||
} else if (options->type == ARGPARSE_OPT_STRING) {
|
||||
pos += fprintf(stdout, "=<str>");
|
||||
}
|
||||
if (pos <= usage_opts_width) {
|
||||
pad = usage_opts_width - pos;
|
||||
} else {
|
||||
fputc('\n', stdout);
|
||||
pad = usage_opts_width;
|
||||
}
|
||||
fprintf(stdout, "%*s%s\n", pad + 2, "", options->help);
|
||||
}
|
||||
|
||||
// print epilog
|
||||
if (self->epilog)
|
||||
fprintf(stdout, "%s\n", self->epilog);
|
||||
}
|
||||
|
||||
int
|
||||
argparse_help_cb(struct argparse *self, const struct argparse_option *option)
|
||||
{
|
||||
(void)option;
|
||||
argparse_usage(self);
|
||||
exit(0);
|
||||
}
|
130
vendor/argparse.h
vendored
130
vendor/argparse.h
vendored
|
@ -1,130 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2012-2015 Yecheng Fu <cofyc.jackson at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style license that can be found
|
||||
* in the LICENSE file.
|
||||
*/
|
||||
#ifndef ARGPARSE_H
|
||||
#define ARGPARSE_H
|
||||
|
||||
/* For c++ compatibility */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct argparse;
|
||||
struct argparse_option;
|
||||
|
||||
typedef int argparse_callback (struct argparse *self,
|
||||
const struct argparse_option *option);
|
||||
|
||||
enum argparse_flag {
|
||||
ARGPARSE_STOP_AT_NON_OPTION = 1,
|
||||
};
|
||||
|
||||
enum argparse_option_type {
|
||||
/* special */
|
||||
ARGPARSE_OPT_END,
|
||||
ARGPARSE_OPT_GROUP,
|
||||
/* options with no arguments */
|
||||
ARGPARSE_OPT_BOOLEAN,
|
||||
ARGPARSE_OPT_BIT,
|
||||
/* options with arguments (optional or required) */
|
||||
ARGPARSE_OPT_INTEGER,
|
||||
ARGPARSE_OPT_FLOAT,
|
||||
ARGPARSE_OPT_STRING,
|
||||
};
|
||||
|
||||
enum argparse_option_flags {
|
||||
OPT_NONEG = 1, /* disable negation */
|
||||
};
|
||||
|
||||
/**
|
||||
* argparse option
|
||||
*
|
||||
* `type`:
|
||||
* holds the type of the option, you must have an ARGPARSE_OPT_END last in your
|
||||
* array.
|
||||
*
|
||||
* `short_name`:
|
||||
* the character to use as a short option name, '\0' if none.
|
||||
*
|
||||
* `long_name`:
|
||||
* the long option name, without the leading dash, NULL if none.
|
||||
*
|
||||
* `value`:
|
||||
* stores pointer to the value to be filled.
|
||||
*
|
||||
* `help`:
|
||||
* the short help message associated to what the option does.
|
||||
* Must never be NULL (except for ARGPARSE_OPT_END).
|
||||
*
|
||||
* `callback`:
|
||||
* function is called when corresponding argument is parsed.
|
||||
*
|
||||
* `data`:
|
||||
* associated data. Callbacks can use it like they want.
|
||||
*
|
||||
* `flags`:
|
||||
* option flags.
|
||||
*/
|
||||
struct argparse_option {
|
||||
enum argparse_option_type type;
|
||||
const char short_name;
|
||||
const char *long_name;
|
||||
void *value;
|
||||
const char *help;
|
||||
argparse_callback *callback;
|
||||
intptr_t data;
|
||||
int flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* argpparse
|
||||
*/
|
||||
struct argparse {
|
||||
// user supplied
|
||||
const struct argparse_option *options;
|
||||
const char *const *usages;
|
||||
int flags;
|
||||
const char *description; // a description after usage
|
||||
const char *epilog; // a description at the end
|
||||
// internal context
|
||||
int argc;
|
||||
const char **argv;
|
||||
const char **out;
|
||||
int cpidx;
|
||||
const char *optvalue; // current option value
|
||||
};
|
||||
|
||||
// built-in callbacks
|
||||
int argparse_help_cb(struct argparse *self,
|
||||
const struct argparse_option *option);
|
||||
|
||||
// built-in option macros
|
||||
#define OPT_END() { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 }
|
||||
#define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ }
|
||||
#define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ }
|
||||
#define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ }
|
||||
#define OPT_FLOAT(...) { ARGPARSE_OPT_FLOAT, __VA_ARGS__ }
|
||||
#define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ }
|
||||
#define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 }
|
||||
#define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, \
|
||||
"show this help message and exit", \
|
||||
argparse_help_cb, 0, OPT_NONEG)
|
||||
|
||||
int argparse_init(struct argparse *self, struct argparse_option *options,
|
||||
const char *const *usages, int flags);
|
||||
void argparse_describe(struct argparse *self, const char *description,
|
||||
const char *epilog);
|
||||
int argparse_parse(struct argparse *self, int argc, const char **argv);
|
||||
void argparse_usage(struct argparse *self);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
16173
vendor/mongoose.c
vendored
16173
vendor/mongoose.c
vendored
File diff suppressed because it is too large
Load diff
6285
vendor/mongoose.h
vendored
6285
vendor/mongoose.h
vendored
File diff suppressed because it is too large
Load diff
6440
vendor/mpack.c
vendored
6440
vendor/mpack.c
vendored
File diff suppressed because it is too large
Load diff
7151
vendor/mpack.h
vendored
7151
vendor/mpack.h
vendored
File diff suppressed because it is too large
Load diff
2274
vendor/toml.c
vendored
2274
vendor/toml.c
vendored
File diff suppressed because it is too large
Load diff
175
vendor/toml.h
vendored
175
vendor/toml.h
vendored
|
@ -1,175 +0,0 @@
|
|||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 - 2019 CK Tan
|
||||
https://github.com/cktan/tomlc99
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
#ifndef TOML_H
|
||||
#define TOML_H
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define TOML_EXTERN extern "C"
|
||||
#else
|
||||
#define TOML_EXTERN extern
|
||||
#endif
|
||||
|
||||
typedef struct toml_timestamp_t toml_timestamp_t;
|
||||
typedef struct toml_table_t toml_table_t;
|
||||
typedef struct toml_array_t toml_array_t;
|
||||
typedef struct toml_datum_t toml_datum_t;
|
||||
|
||||
/* Parse a file. Return a table on success, or 0 otherwise.
|
||||
* Caller must toml_free(the-return-value) after use.
|
||||
*/
|
||||
TOML_EXTERN toml_table_t* toml_parse_file(FILE* fp,
|
||||
char* errbuf,
|
||||
int errbufsz);
|
||||
|
||||
/* Parse a string containing the full config.
|
||||
* Return a table on success, or 0 otherwise.
|
||||
* Caller must toml_free(the-return-value) after use.
|
||||
*/
|
||||
TOML_EXTERN toml_table_t* toml_parse(char* conf, /* NUL terminated, please. */
|
||||
char* errbuf,
|
||||
int errbufsz);
|
||||
|
||||
/* Free the table returned by toml_parse() or toml_parse_file(). Once
|
||||
* this function is called, any handles accessed through this tab
|
||||
* directly or indirectly are no longer valid.
|
||||
*/
|
||||
TOML_EXTERN void toml_free(toml_table_t* tab);
|
||||
|
||||
|
||||
/* Timestamp types. The year, month, day, hour, minute, second, z
|
||||
* fields may be NULL if they are not relevant. e.g. In a DATE
|
||||
* type, the hour, minute, second and z fields will be NULLs.
|
||||
*/
|
||||
struct toml_timestamp_t {
|
||||
struct { /* internal. do not use. */
|
||||
int year, month, day;
|
||||
int hour, minute, second, millisec;
|
||||
char z[10];
|
||||
} __buffer;
|
||||
int *year, *month, *day;
|
||||
int *hour, *minute, *second, *millisec;
|
||||
char* z;
|
||||
};
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
* Enhanced access methods
|
||||
*/
|
||||
struct toml_datum_t {
|
||||
int ok;
|
||||
union {
|
||||
toml_timestamp_t* ts; /* ts must be freed after use */
|
||||
char* s; /* string value. s must be freed after use */
|
||||
int b; /* bool value */
|
||||
int64_t i; /* int value */
|
||||
double d; /* double value */
|
||||
} u;
|
||||
};
|
||||
|
||||
/* on arrays: */
|
||||
/* ... retrieve size of array. */
|
||||
TOML_EXTERN int toml_array_nelem(const toml_array_t* arr);
|
||||
/* ... retrieve values using index. */
|
||||
TOML_EXTERN toml_datum_t toml_string_at(const toml_array_t* arr, int idx);
|
||||
TOML_EXTERN toml_datum_t toml_bool_at(const toml_array_t* arr, int idx);
|
||||
TOML_EXTERN toml_datum_t toml_int_at(const toml_array_t* arr, int idx);
|
||||
TOML_EXTERN toml_datum_t toml_double_at(const toml_array_t* arr, int idx);
|
||||
TOML_EXTERN toml_datum_t toml_timestamp_at(const toml_array_t* arr, int idx);
|
||||
/* ... retrieve array or table using index. */
|
||||
TOML_EXTERN toml_array_t* toml_array_at(const toml_array_t* arr, int idx);
|
||||
TOML_EXTERN toml_table_t* toml_table_at(const toml_array_t* arr, int idx);
|
||||
|
||||
/* on tables: */
|
||||
/* ... retrieve the key in table at keyidx. Return 0 if out of range. */
|
||||
TOML_EXTERN const char* toml_key_in(const toml_table_t* tab, int keyidx);
|
||||
/* ... retrieve values using key. */
|
||||
TOML_EXTERN toml_datum_t toml_string_in(const toml_table_t* arr, const char* key);
|
||||
TOML_EXTERN toml_datum_t toml_bool_in(const toml_table_t* arr, const char* key);
|
||||
TOML_EXTERN toml_datum_t toml_int_in(const toml_table_t* arr, const char* key);
|
||||
TOML_EXTERN toml_datum_t toml_double_in(const toml_table_t* arr, const char* key);
|
||||
TOML_EXTERN toml_datum_t toml_timestamp_in(const toml_table_t* arr, const char* key);
|
||||
/* .. retrieve array or table using key. */
|
||||
TOML_EXTERN toml_array_t* toml_array_in(const toml_table_t* tab,
|
||||
const char* key);
|
||||
TOML_EXTERN toml_table_t* toml_table_in(const toml_table_t* tab,
|
||||
const char* key);
|
||||
|
||||
/*-----------------------------------------------------------------
|
||||
* lesser used
|
||||
*/
|
||||
/* Return the array kind: 't'able, 'a'rray, 'v'alue */
|
||||
TOML_EXTERN char toml_array_kind(const toml_array_t* arr);
|
||||
|
||||
/* For array kind 'v'alue, return the type of values
|
||||
i:int, d:double, b:bool, s:string, t:time, D:date, T:timestamp
|
||||
0 if unknown
|
||||
*/
|
||||
TOML_EXTERN char toml_array_type(const toml_array_t* arr);
|
||||
|
||||
/* Return the key of an array */
|
||||
TOML_EXTERN const char* toml_array_key(const toml_array_t* arr);
|
||||
|
||||
/* Return the number of key-values in a table */
|
||||
TOML_EXTERN int toml_table_nkval(const toml_table_t* tab);
|
||||
|
||||
/* Return the number of arrays in a table */
|
||||
TOML_EXTERN int toml_table_narr(const toml_table_t* tab);
|
||||
|
||||
/* Return the number of sub-tables in a table */
|
||||
TOML_EXTERN int toml_table_ntab(const toml_table_t* tab);
|
||||
|
||||
/* Return the key of a table*/
|
||||
TOML_EXTERN const char* toml_table_key(const toml_table_t* tab);
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
* misc
|
||||
*/
|
||||
TOML_EXTERN int toml_utf8_to_ucs(const char* orig, int len, int64_t* ret);
|
||||
TOML_EXTERN int toml_ucs_to_utf8(int64_t code, char buf[6]);
|
||||
TOML_EXTERN void toml_set_memutil(void* (*xxmalloc)(size_t),
|
||||
void (*xxfree)(void*));
|
||||
|
||||
|
||||
/*--------------------------------------------------------------
|
||||
* deprecated
|
||||
*/
|
||||
/* A raw value, must be processed by toml_rto* before using. */
|
||||
typedef const char* toml_raw_t;
|
||||
TOML_EXTERN toml_raw_t toml_raw_in(const toml_table_t* tab, const char* key);
|
||||
TOML_EXTERN toml_raw_t toml_raw_at(const toml_array_t* arr, int idx);
|
||||
TOML_EXTERN int toml_rtos(toml_raw_t s, char** ret);
|
||||
TOML_EXTERN int toml_rtob(toml_raw_t s, int* ret);
|
||||
TOML_EXTERN int toml_rtoi(toml_raw_t s, int64_t* ret);
|
||||
TOML_EXTERN int toml_rtod(toml_raw_t s, double* ret);
|
||||
TOML_EXTERN int toml_rtod_ex(toml_raw_t s, double* ret, char* buf, int buflen);
|
||||
TOML_EXTERN int toml_rtots(toml_raw_t s, toml_timestamp_t* ret);
|
||||
|
||||
|
||||
#endif /* TOML_H */
|
|
@ -1 +0,0 @@
|
|||
#define EMGAUWA_CONTROLLER_VERSION "@CMAKE_PROJECT_VERSION@"
|
Loading…
Reference in a new issue