Compare commits
5 commits
main
...
go-rewrite
Author | SHA1 | Date | |
---|---|---|---|
c515f55b8b | |||
c49ada88c3 | |||
943dd8e0d1 | |||
5d3c94ef8c | |||
79b1f3b2cf |
60 changed files with 170 additions and 45547 deletions
|
@ -20,9 +20,7 @@ steps:
|
|||
api_key:
|
||||
from_secret: gitea_token
|
||||
base_url: https://git.serguzim.me
|
||||
files:
|
||||
- build/controller
|
||||
- build/controller.ini
|
||||
title: ${DRONE_TAG}
|
||||
when:
|
||||
event: tag
|
||||
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
cmake_minimum_required (VERSION 3.7)
|
||||
project(controller
|
||||
VERSION 0.3.7
|
||||
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 "$ENV{CFLAGS}")
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -std=gnu99 -Wpedantic -Werror -Wall -Wextra -ffile-prefix-map=${CMAKE_SOURCE_DIR}/src/=")
|
||||
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -fprofile-arcs -ftest-coverage")
|
||||
|
||||
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("controller.ini" "controller.ini" COPYONLY)
|
||||
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 ./controller start
|
||||
DEPENDS controller
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_target(debug
|
||||
COMMAND valgrind ./controller start
|
||||
DEPENDS controller
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_target(debug-full
|
||||
COMMAND valgrind --leak-check=full --show-leak-kinds=all ./controller start
|
||||
DEPENDS controller
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_target(docs
|
||||
COMMAND doxygen
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
|
@ -1,69 +0,0 @@
|
|||
[controller]
|
||||
name = new emgauwa device
|
||||
|
||||
: 4422 for testing; 4421 for dev-env; 4420 for testing-env; 4419 for prod-env
|
||||
discovery-port = 4421
|
||||
: 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
|
||||
mqtt-port = 1885
|
||||
mqtt-host = localhost
|
||||
|
||||
relay-count = 10
|
||||
relays-init = 1
|
||||
|
||||
database = controller.sqlite
|
||||
log-level = debug
|
||||
log-file = stdout
|
||||
|
||||
[relay-0]
|
||||
driver = piface
|
||||
pin = 0
|
||||
inverted = 0
|
||||
init = 0
|
||||
|
||||
[relay-1]
|
||||
driver = piface
|
||||
pin = 1
|
||||
inverted = 0
|
||||
init = 1
|
||||
|
||||
[relay-2]
|
||||
driver = gpio
|
||||
pin = 5
|
||||
inverted = 1
|
||||
|
||||
[relay-3]
|
||||
driver = gpio
|
||||
pin = 4
|
||||
inverted = 1
|
||||
|
||||
[relay-4]
|
||||
driver = gpio
|
||||
pin = 3
|
||||
inverted = 1
|
||||
|
||||
[relay-5]
|
||||
driver = gpio
|
||||
pin = 2
|
||||
inverted = 1
|
||||
|
||||
[relay-6]
|
||||
driver = gpio
|
||||
pin = 1
|
||||
inverted = 1
|
||||
pulse-duration = 3
|
||||
|
||||
[relay-7]
|
||||
driver = gpio
|
||||
pin = 0
|
||||
inverted = 1
|
||||
pulse-duration = 3
|
||||
|
||||
[relay-8]
|
||||
driver = gpio
|
||||
pin = 16
|
||||
inverted = 1
|
||||
|
||||
[relay-9]
|
||||
driver = gpio
|
||||
pin = 15
|
||||
inverted = 1
|
66
emgauwa-controller.conf
Normal file
66
emgauwa-controller.conf
Normal file
|
@ -0,0 +1,66 @@
|
|||
[controller]
|
||||
database = "emgauwa-controller.sqlite"
|
||||
|
||||
mqtt-host = "127.0.0.1"
|
||||
|
||||
[ports]
|
||||
# 4422 for testing; 4421 for dev-env; 4420 for testing-env; 4419 for prod-env
|
||||
discovery = 4421
|
||||
# 1886 for testing; 1885 for dev-env; 1884 for testing-env; 1883 for prod-env
|
||||
mqtt = 1885
|
||||
|
||||
[logging]
|
||||
level = "debug"
|
||||
file = "stdout"
|
||||
|
||||
[[relays]]
|
||||
driver = "piface"
|
||||
pin = 0
|
||||
inverted = 0
|
||||
|
||||
[[relays]]
|
||||
driver = "piface"
|
||||
pin = 1
|
||||
inverted = 0
|
||||
|
||||
[[relays]]
|
||||
driver = "gpio"
|
||||
pin = 5
|
||||
inverted = 1
|
||||
|
||||
[[relays]]
|
||||
driver = "gpio"
|
||||
pin = 4
|
||||
inverted = 1
|
||||
|
||||
[[relays]]
|
||||
driver = "gpio"
|
||||
pin = 3
|
||||
inverted = 1
|
||||
|
||||
[[relays]]
|
||||
driver = "gpio"
|
||||
pin = 2
|
||||
inverted = 1
|
||||
|
||||
[[relays]]
|
||||
driver = "gpio"
|
||||
pin = 1
|
||||
inverted = 1
|
||||
pulse-duration = 3
|
||||
|
||||
[[relays]]
|
||||
driver = "gpio"
|
||||
pin = 0
|
||||
inverted = 1
|
||||
pulse-duration = 3
|
||||
|
||||
[[relays]]
|
||||
driver = "gpio"
|
||||
pin = 16
|
||||
inverted = 1
|
||||
|
||||
[[relays]]
|
||||
driver = "gpio"
|
||||
pin = 15
|
||||
inverted = 1
|
BIN
emgauwa-controller.sqlite
Normal file
BIN
emgauwa-controller.sqlite
Normal file
Binary file not shown.
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
|||
module emgauwa.app/controller
|
||||
|
||||
go 1.15
|
|
@ -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,42 +0,0 @@
|
|||
#ifndef CONTROLLER_CONFIG_H
|
||||
#define CONTROLLER_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <confini.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;
|
||||
|
||||
int
|
||||
config_load(IniDispatch *disp, void *config_void);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *file;
|
||||
char database[256];
|
||||
char user[256];
|
||||
char group[256];
|
||||
int log_level;
|
||||
FILE *log_file;
|
||||
run_type_t run_type;
|
||||
char name[MAX_NAME_LENGTH + 1];
|
||||
uint16_t discovery_port;
|
||||
uint16_t mqtt_port;
|
||||
char mqtt_host[256];
|
||||
uint8_t relay_count;
|
||||
int relays_init;
|
||||
config_relay_t *relay_configs;
|
||||
} config_t;
|
||||
|
||||
extern config_t global_config;
|
||||
|
||||
#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,31 +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 Maximum number of dbs for the databases for the MDB_env
|
||||
*
|
||||
* Used when calling mdb_env_set_maxdbs() in database_setup()
|
||||
*/
|
||||
#define MDB_MAXDBS 8
|
||||
|
||||
/**
|
||||
* @brief How many milli seconds to wait until poll timeout in main loop
|
||||
*/
|
||||
#define ACCEPT_TIMEOUT_MSECONDS 1000
|
||||
|
||||
#define PIFACE_GPIO_BASE 200
|
||||
|
||||
#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,31 +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;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RUN_TYPE_START,
|
||||
RUN_TYPE_TEST,
|
||||
} run_type_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,37 +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);
|
||||
|
||||
void
|
||||
helper_parse_cli(int argc, const char **argv, config_t *config);
|
||||
|
||||
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');
|
191
src/config.c
191
src/config.c
|
@ -1,191 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <helpers.h>
|
||||
#include <config.h>
|
||||
#include <logger.h>
|
||||
#include <confini.h>
|
||||
|
||||
#define CONFINI_IS_KEY(SECTION, KEY) \
|
||||
(ini_array_match(SECTION, disp->append_to, '.', disp->format) && \
|
||||
ini_string_match_ii(KEY, disp->data, disp->format))
|
||||
|
||||
static int
|
||||
config_load_log_level(IniDispatch *disp, config_t *config)
|
||||
{
|
||||
if(strcasecmp(disp->value, "debug") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_DEBUG));
|
||||
config->log_level = LOG_DEBUG;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "info") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_INFO));
|
||||
config->log_level = LOG_INFO;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "notice") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_NOTICE));
|
||||
config->log_level = LOG_NOTICE;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "warning") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_WARNING));
|
||||
config->log_level = LOG_WARNING;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "err") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_ERR));
|
||||
config->log_level = LOG_ERR;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "crit") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_CRIT));
|
||||
config->log_level = LOG_CRIT;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "emerg") == 0)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_EMERG));
|
||||
config->log_level = LOG_EMERG;
|
||||
return 0;
|
||||
}
|
||||
LOGGER_WARNING("invalid log-level '%s'\n", disp->value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
config_load_log_file(IniDispatch *disp, config_t *config)
|
||||
{
|
||||
if(strcasecmp(disp->value, "stdout") == 0)
|
||||
{
|
||||
config->log_file = stdout;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "stderr") == 0)
|
||||
{
|
||||
config->log_file = stderr;
|
||||
return 0;
|
||||
}
|
||||
config->log_file = fopen(disp->value, "a+");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
config_load(IniDispatch *disp, void *config_void)
|
||||
{
|
||||
config_t *config = (config_t*)config_void;
|
||||
char relay_section_name[10]; // "relay-255\0" is longest name
|
||||
|
||||
if(disp->type == INI_KEY)
|
||||
{
|
||||
if(CONFINI_IS_KEY("controller", "name"))
|
||||
{
|
||||
strncpy(config->name, disp->value, MAX_NAME_LENGTH);
|
||||
config->name[MAX_NAME_LENGTH] = '\0';
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "database"))
|
||||
{
|
||||
strcpy(config->database, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "user"))
|
||||
{
|
||||
strcpy(config->user, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "group"))
|
||||
{
|
||||
strcpy(config->group, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "log-level"))
|
||||
{
|
||||
return config_load_log_level(disp, config);
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "log-file"))
|
||||
{
|
||||
return config_load_log_file(disp, config);
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "discovery-port"))
|
||||
{
|
||||
config->discovery_port = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "mqtt-port"))
|
||||
{
|
||||
config->mqtt_port = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "mqtt-host"))
|
||||
{
|
||||
strcpy(config->mqtt_host, disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "relays-init"))
|
||||
{
|
||||
config->relays_init = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY("controller", "relay-count"))
|
||||
{
|
||||
config->relay_count = atoi(disp->value);
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
for(uint8_t i = 0; i < config->relay_count; ++i)
|
||||
{
|
||||
sprintf(relay_section_name, "relay-%u", i);
|
||||
if(CONFINI_IS_KEY(relay_section_name, "pin"))
|
||||
{
|
||||
config->relay_configs[i].pin = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY(relay_section_name, "inverted"))
|
||||
{
|
||||
config->relay_configs[i].inverted = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY(relay_section_name, "init"))
|
||||
{
|
||||
config->relay_configs[i].init = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY(relay_section_name, "pulse-duration"))
|
||||
{
|
||||
config->relay_configs[i].pulse_duration = atoi(disp->value);
|
||||
return 0;
|
||||
}
|
||||
if(CONFINI_IS_KEY(relay_section_name, "driver"))
|
||||
{
|
||||
if(strcasecmp(disp->value, "gpio") == 0)
|
||||
{
|
||||
config->relay_configs[i].driver = RELAY_DRIVER_GPIO;
|
||||
return 0;
|
||||
}
|
||||
if(strcasecmp(disp->value, "piface") == 0)
|
||||
{
|
||||
config->relay_configs[i].driver = RELAY_DRIVER_PIFACE;
|
||||
return 0;
|
||||
}
|
||||
LOGGER_WARNING("invalid driver '%s' in section '%s'\n", disp->value, relay_section_name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -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.discovery_port);
|
||||
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.mqtt_port);
|
||||
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;
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <argparse.h>
|
||||
#include <config.h>
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
#include <version.h>
|
||||
|
||||
static const char *const usage[] = {
|
||||
"controller [options] [[--] args]",
|
||||
"controller [options]",
|
||||
NULL,
|
||||
};
|
||||
|
||||
#define PERM_READ (1<<0)
|
||||
#define PERM_WRITE (1<<1)
|
||||
#define PERM_EXEC (1<<2)
|
||||
|
||||
void
|
||||
helper_parse_cli(int argc, const char **argv, config_t *config)
|
||||
{
|
||||
int version = 0;
|
||||
struct argparse_option options[] =
|
||||
{
|
||||
OPT_HELP(),
|
||||
OPT_GROUP("Basic options"),
|
||||
OPT_STRING('c', "config", &config->file, "path to config file", 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."
|
||||
);
|
||||
argc = argparse_parse(&argparse, argc, argv);
|
||||
|
||||
if(version)
|
||||
{
|
||||
printf("%s\n", EMGAUWA_CONTROLLER_VERSION);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if(argc == 1)
|
||||
{
|
||||
if(strcmp(argv[0], "start") == 0)
|
||||
{
|
||||
config->run_type = RUN_TYPE_START;
|
||||
return;
|
||||
}
|
||||
if(strcmp(argv[0], "test") == 0)
|
||||
{
|
||||
config->run_type = RUN_TYPE_TEST;
|
||||
return;
|
||||
}
|
||||
LOGGER_CRIT("bad action '%s' given ('start', 'test')\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER_CRIT("no action given ('start', 'test')\n");
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
}
|
85
src/logger.c
85
src/logger.c
|
@ -1,85 +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.log_level < level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
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));
|
||||
|
||||
char *buffer = malloc(sizeof(char) * (128 + strlen(msg)));
|
||||
sprintf(buffer, "%s[%5s] %s:%d:%s " COLOR_NONE "%s", color, level_str, filename, line, func, msg);
|
||||
|
||||
//fprintf(stream, "%s %s:%d:%s " COLOR_NONE, timestamp_str, filename, line, func);
|
||||
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
vsyslog(level, buffer, args);
|
||||
va_end(args);
|
||||
|
||||
char *buffer_timed = malloc(sizeof(char) * (strlen(timestamp_str) + strlen(buffer) + 2));
|
||||
sprintf(buffer_timed, "%s %s", timestamp_str, buffer);
|
||||
va_start(args, msg);
|
||||
vfprintf(global_config.log_file, buffer_timed, args);
|
||||
fflush(global_config.log_file);
|
||||
va_end(args);
|
||||
|
||||
free(buffer);
|
||||
free(buffer_timed);
|
||||
}
|
208
src/main.c
208
src/main.c
|
@ -1,208 +0,0 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <lmdb.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.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>
|
||||
#include <confini.h>
|
||||
|
||||
config_t global_config;
|
||||
|
||||
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);
|
||||
|
||||
setlogmask(LOG_UPTO(LOG_INFO));
|
||||
|
||||
|
||||
/******************** LOAD CONFIG ********************/
|
||||
|
||||
global_config.file = "controller.ini";
|
||||
global_config.discovery_port = 4421;
|
||||
global_config.mqtt_port = 1885;
|
||||
|
||||
global_config.relay_count = 0;
|
||||
global_config.relays_init = -1;
|
||||
|
||||
global_config.log_level = LOG_INFO;
|
||||
global_config.log_file = stdout;
|
||||
|
||||
strcpy(global_config.user, "");
|
||||
strcpy(global_config.group, "");
|
||||
|
||||
helper_parse_cli(argc, argv, &global_config);
|
||||
|
||||
FILE * const ini_file = fopen(global_config.file, "rb");
|
||||
if(ini_file == NULL)
|
||||
{
|
||||
LOGGER_CRIT("config file '%s' was not found\n", global_config.file);
|
||||
exit(1);
|
||||
}
|
||||
if(load_ini_file( ini_file, INI_DEFAULT_FORMAT, NULL, config_load, &global_config))
|
||||
{
|
||||
LOGGER_CRIT("unable to parse ini file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fclose(ini_file);
|
||||
|
||||
if(global_config.log_file == NULL)
|
||||
{
|
||||
global_config.log_file = stdout;
|
||||
}
|
||||
openlog("emgauwa-controller", 0, LOG_USER);
|
||||
|
||||
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(global_config.run_type == RUN_TYPE_TEST)
|
||||
{
|
||||
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 false;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 false;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while(true)
|
||||
{
|
||||
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
|
5016
vendor/confini.c
vendored
5016
vendor/confini.c
vendored
File diff suppressed because it is too large
Load diff
547
vendor/confini.h
vendored
547
vendor/confini.h
vendored
|
@ -1,547 +0,0 @@
|
|||
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
|
||||
|
||||
/**
|
||||
|
||||
@file confini.h
|
||||
@brief libconfini header
|
||||
@author Stefano Gioffré
|
||||
@copyright GNU General Public License, version 3 or any later version
|
||||
@version 1.14.0
|
||||
@date 2016-2020
|
||||
@see https://madmurphy.github.io/libconfini
|
||||
|
||||
**/
|
||||
|
||||
|
||||
#ifndef _LIBCONFINI_HEADER_
|
||||
#define _LIBCONFINI_HEADER_
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* PRIVATE (HEADER-SCOPED) MACROS */
|
||||
|
||||
|
||||
#define __INIFORMAT_TABLE_CB_FIELDS__(NAME, OFFSET, SIZE, DEFVAL) \
|
||||
unsigned char NAME:SIZE;
|
||||
#define __INIFORMAT_TABLE_CB_DEFAULT__(NAME, OFFSET, SIZE, DEFVAL) DEFVAL,
|
||||
#define __INIFORMAT_TABLE_CB_ZERO__(NAME, OFFSET, SIZE, DEFVAL) 0,
|
||||
#define _LIBCONFINI_INIFORMAT_TYPE_ \
|
||||
struct IniFormat { INIFORMAT_TABLE_AS(__INIFORMAT_TABLE_CB_FIELDS__) }
|
||||
#define _LIBCONFINI_DEFAULT_FORMAT_ \
|
||||
{ INIFORMAT_TABLE_AS(__INIFORMAT_TABLE_CB_DEFAULT__) }
|
||||
#define _LIBCONFINI_UNIXLIKE_FORMAT_ \
|
||||
{ INIFORMAT_TABLE_AS(__INIFORMAT_TABLE_CB_ZERO__) }
|
||||
|
||||
|
||||
|
||||
/* PUBLIC MACROS */
|
||||
|
||||
|
||||
/**
|
||||
@brief Calls a user-given macro (that accepts four arguments) for each row
|
||||
of the table
|
||||
**/
|
||||
/*
|
||||
NOTE: The following table and the order of its rows **define** (and link
|
||||
together) both the #IniFormat and #IniFormatNum data types declared in this
|
||||
header
|
||||
*/
|
||||
#define INIFORMAT_TABLE_AS(_____) /* IniFormat table *\
|
||||
|
||||
NAME BIT SIZE DEFAULT
|
||||
*/\
|
||||
_____( delimiter_symbol, 0, 7, INI_EQUALS ) \
|
||||
_____( case_sensitive, 7, 1, false )/*
|
||||
*/\
|
||||
_____( semicolon_marker, 8, 2, INI_DISABLED_OR_COMMENT ) \
|
||||
_____( hash_marker, 10, 2, INI_DISABLED_OR_COMMENT ) \
|
||||
_____( section_paths, 12, 2, INI_ABSOLUTE_AND_RELATIVE ) \
|
||||
_____( multiline_nodes, 14, 2, INI_MULTILINE_EVERYWHERE )/*
|
||||
*/\
|
||||
_____( no_single_quotes, 16, 1, false ) \
|
||||
_____( no_double_quotes, 17, 1, false ) \
|
||||
_____( no_spaces_in_names, 18, 1, false ) \
|
||||
_____( implicit_is_not_empty, 19, 1, false ) \
|
||||
_____( do_not_collapse_values, 20, 1, false ) \
|
||||
_____( preserve_empty_quotes, 21, 1, false ) \
|
||||
_____( disabled_after_space, 22, 1, false ) \
|
||||
_____( disabled_can_be_implicit, 23, 1, false )
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@brief Checks whether a format does **not** support escape sequences
|
||||
**/
|
||||
#define INIFORMAT_HAS_NO_ESC(FORMAT) \
|
||||
(FORMAT.multiline_nodes == INI_NO_MULTILINE && \
|
||||
FORMAT.no_double_quotes && FORMAT.no_single_quotes)
|
||||
|
||||
|
||||
|
||||
/* PUBLIC TYPEDEFS */
|
||||
|
||||
|
||||
/**
|
||||
@brief 24-bit bitfield representing the format of an INI file (INI
|
||||
dialect)
|
||||
**/
|
||||
typedef _LIBCONFINI_INIFORMAT_TYPE_ IniFormat;
|
||||
|
||||
|
||||
/**
|
||||
@brief Global statistics about an INI file
|
||||
**/
|
||||
typedef struct IniStatistics {
|
||||
const IniFormat format;
|
||||
const size_t bytes;
|
||||
const size_t members;
|
||||
} IniStatistics;
|
||||
|
||||
|
||||
/**
|
||||
@brief Dispatch of a single INI node
|
||||
**/
|
||||
typedef struct IniDispatch {
|
||||
const IniFormat format;
|
||||
uint8_t type;
|
||||
char * data;
|
||||
char * value;
|
||||
const char * append_to;
|
||||
size_t d_len;
|
||||
size_t v_len;
|
||||
size_t at_len;
|
||||
size_t dispatch_id;
|
||||
} IniDispatch;
|
||||
|
||||
|
||||
/**
|
||||
@brief The unique ID of an INI format (24-bit maximum)
|
||||
**/
|
||||
typedef uint32_t IniFormatNum;
|
||||
|
||||
|
||||
/**
|
||||
@brief Callback function for handling an #IniStatistics structure
|
||||
**/
|
||||
typedef int (* IniStatsHandler) (
|
||||
IniStatistics * statistics,
|
||||
void * user_data
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@brief Callback function for handling an #IniDispatch structure
|
||||
**/
|
||||
typedef int (* IniDispHandler) (
|
||||
IniDispatch * dispatch,
|
||||
void * user_data
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@brief Callback function for handling an INI string belonging to a
|
||||
sequence of INI strings
|
||||
**/
|
||||
typedef int (* IniStrHandler) (
|
||||
char * ini_string,
|
||||
size_t string_length,
|
||||
size_t string_num,
|
||||
IniFormat format,
|
||||
void * user_data
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@brief Callback function for handling a selected fragment of an INI string
|
||||
**/
|
||||
typedef int (* IniSubstrHandler) (
|
||||
const char * ini_string,
|
||||
size_t fragm_offset,
|
||||
size_t fragm_length,
|
||||
size_t fragm_num,
|
||||
IniFormat format,
|
||||
void * user_data
|
||||
);
|
||||
|
||||
|
||||
|
||||
/* PUBLIC FUNCTIONS */
|
||||
|
||||
|
||||
extern int strip_ini_cache (
|
||||
register char * const ini_source,
|
||||
const size_t ini_length,
|
||||
const IniFormat format,
|
||||
const IniStatsHandler f_init,
|
||||
const IniDispHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern int load_ini_file (
|
||||
FILE * const ini_file,
|
||||
const IniFormat format,
|
||||
const IniStatsHandler f_init,
|
||||
const IniDispHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern int load_ini_path (
|
||||
const char * const path,
|
||||
const IniFormat format,
|
||||
const IniStatsHandler f_init,
|
||||
const IniDispHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern bool ini_string_match_ss (
|
||||
const char * const simple_string_a,
|
||||
const char * const simple_string_b,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern bool ini_string_match_si (
|
||||
const char * const simple_string,
|
||||
const char * const ini_string,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern bool ini_string_match_ii (
|
||||
const char * const ini_string_a,
|
||||
const char * const ini_string_b,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern bool ini_array_match (
|
||||
const char * const ini_string_a,
|
||||
const char * const ini_string_b,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_unquote (
|
||||
char * const ini_string,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_string_parse (
|
||||
char * const ini_string,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_array_get_length (
|
||||
const char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern int ini_array_foreach (
|
||||
const char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format,
|
||||
const IniSubstrHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_array_shift (
|
||||
const char ** const ini_strptr,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern size_t ini_array_collapse (
|
||||
char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern char * ini_array_break (
|
||||
char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern char * ini_array_release (
|
||||
char ** const ini_strptr,
|
||||
const char delimiter,
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern int ini_array_split (
|
||||
char * const ini_string,
|
||||
const char delimiter,
|
||||
const IniFormat format,
|
||||
const IniStrHandler f_foreach,
|
||||
void * const user_data
|
||||
);
|
||||
|
||||
|
||||
extern void ini_global_set_lowercase_mode (
|
||||
const bool lowercase
|
||||
);
|
||||
|
||||
|
||||
extern void ini_global_set_implicit_value (
|
||||
char * const implicit_value,
|
||||
const size_t implicit_v_len
|
||||
);
|
||||
|
||||
|
||||
extern IniFormatNum ini_fton (
|
||||
const IniFormat format
|
||||
);
|
||||
|
||||
|
||||
extern IniFormat ini_ntof (
|
||||
const IniFormatNum format_id
|
||||
);
|
||||
|
||||
|
||||
extern int ini_get_bool (
|
||||
const char * const ini_string,
|
||||
const int when_fail
|
||||
);
|
||||
|
||||
|
||||
|
||||
/* PUBLIC LINKS */
|
||||
|
||||
|
||||
extern int (* const ini_get_int) (
|
||||
const char * ini_string
|
||||
);
|
||||
|
||||
|
||||
extern long int (* const ini_get_lint) (
|
||||
const char * ini_string
|
||||
);
|
||||
|
||||
|
||||
extern long long int (* const ini_get_llint) (
|
||||
const char * ini_string
|
||||
);
|
||||
|
||||
|
||||
extern double (* const ini_get_double) (
|
||||
const char * ini_string
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
@brief Legacy support, soon to be replaced with a `float` data type --
|
||||
please **do not use `ini_get_float()`!**
|
||||
**/
|
||||
#define ini_get_float \
|
||||
_Pragma("GCC warning \"function `ini_get_float()` is deprecated for parsing a `double` data type; use `ini_get_double()` instead\"") \
|
||||
ini_get_double
|
||||
|
||||
|
||||
|
||||
/* PUBLIC CONSTANTS AND VARIABLES */
|
||||
|
||||
|
||||
/**
|
||||
@brief Error mask (flags not present in user-generated interruptions)
|
||||
**/
|
||||
#define CONFINI_ERROR 252
|
||||
|
||||
|
||||
/**
|
||||
@brief Error codes
|
||||
**/
|
||||
enum ConfiniInterruptNo {
|
||||
CONFINI_SUCCESS = 0, /**< There have been no interruptions, everything
|
||||
went well [value=0] **/
|
||||
CONFINI_IINTR = 1, /**< Interrupted by the user during `f_init()`
|
||||
[value=1] **/
|
||||
CONFINI_FEINTR = 2, /**< Interrupted by the user during `f_foreach()`
|
||||
[value=2] **/
|
||||
CONFINI_ENOENT = 4, /**< File inaccessible [value=4] **/
|
||||
CONFINI_ENOMEM = 5, /**< Error allocating virtual memory [value=5] **/
|
||||
CONFINI_EIO = 6, /**< Error reading the file [value=6] **/
|
||||
CONFINI_EOOR = 7, /**< Out-of-range error: callbacks are more than
|
||||
expected [value=7] **/
|
||||
CONFINI_EBADF = 8, /**< The stream specified is not a seekable stream
|
||||
[value=8] **/
|
||||
CONFINI_EFBIG = 9, /**< File too large [value=9] **/
|
||||
CONFINI_EROADDR = 10 /**< Address is read-only [value=10] **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief INI node types
|
||||
**/
|
||||
enum IniNodeType {
|
||||
INI_UNKNOWN = 0, /**< This is a node impossible to categorize
|
||||
[value=0] **/
|
||||
INI_VALUE = 1, /**< Not used by **libconfini** (values are
|
||||
dispatched together with keys) -- but
|
||||
available for user's implementations
|
||||
[value=1] **/
|
||||
INI_KEY = 2, /**< This is a key [value=2] **/
|
||||
INI_SECTION = 3, /**< This is a section or a section path
|
||||
[value=3] **/
|
||||
INI_COMMENT = 4, /**< This is a comment [value=4] **/
|
||||
INI_INLINE_COMMENT = 5, /**< This is an inline comment [value=5] **/
|
||||
INI_DISABLED_KEY = 6, /**< This is a disabled key [value=6] **/
|
||||
INI_DISABLED_SECTION = 7 /**< This is a disabled section path
|
||||
[value=7] **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Common array and key-value delimiters (but a delimiter may also be
|
||||
any other ASCII character not present in this list)
|
||||
**/
|
||||
enum IniDelimiters {
|
||||
INI_ANY_SPACE = 0, /**< In multi-line INIs:
|
||||
`/(?:\\(?:\n\r?|\r\n?)|[\t \v\f])+/`, in
|
||||
non-multi-line INIs: `/[\t \v\f])+/` **/
|
||||
INI_EQUALS = '=', /**< Equals character (`=`) **/
|
||||
INI_COLON = ':', /**< Colon character (`:`) **/
|
||||
INI_DOT = '.', /**< Dot character (`.`) **/
|
||||
INI_COMMA = ',' /**< Comma character (`,`) **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Possible values of #IniFormat::semicolon_marker and
|
||||
#IniFormat::hash_marker (i.e., meaning of `/\s+;/` and `/\s+#/` in
|
||||
respect to a format)
|
||||
**/
|
||||
enum IniCommentMarker {
|
||||
INI_DISABLED_OR_COMMENT = 0, /**< This marker opens a comment or a
|
||||
disabled entry **/
|
||||
INI_ONLY_COMMENT = 1, /**< This marker opens a comment **/
|
||||
INI_IGNORE = 2, /**< This marker opens a comment that has
|
||||
been marked for deletion and must not
|
||||
be dispatched or counted **/
|
||||
INI_IS_NOT_A_MARKER = 3 /**< This is not a marker at all, but a
|
||||
normal character instead **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Possible values of #IniFormat::section_paths
|
||||
**/
|
||||
enum IniSectionPaths {
|
||||
INI_ABSOLUTE_AND_RELATIVE = 0, /**< Section paths starting with a dot
|
||||
express nesting to the current parent,
|
||||
to root otherwise **/
|
||||
INI_ABSOLUTE_ONLY = 1, /**< Section paths starting with a dot will
|
||||
be cleaned of their leading dot and
|
||||
appended to root **/
|
||||
INI_ONE_LEVEL_ONLY = 2, /**< Format supports sections, but the dot
|
||||
does not express nesting and is not a
|
||||
meta-character **/
|
||||
INI_NO_SECTIONS = 3 /**< Format does *not* support sections --
|
||||
`/\[[^\]]*\]/g`, if any, will be
|
||||
treated as keys! **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Possible values of #IniFormat::multiline_nodes
|
||||
**/
|
||||
enum IniMultiline {
|
||||
INI_MULTILINE_EVERYWHERE = 0, /**< Comments, section paths and keys
|
||||
-- disabled or not -- are allowed
|
||||
to be multi-line **/
|
||||
INI_BUT_COMMENTS = 1, /**< Only section paths and keys --
|
||||
disabled or not -- are allowed to
|
||||
be multi-line **/
|
||||
INI_BUT_DISABLED_AND_COMMENTS = 2, /**< Only active section paths and
|
||||
active keys are allowed to be
|
||||
multi-line **/
|
||||
INI_NO_MULTILINE = 3 /**< Multi-line escape sequences are
|
||||
disabled **/
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief A model format for standard INI files
|
||||
**/
|
||||
static const IniFormat INI_DEFAULT_FORMAT = _LIBCONFINI_DEFAULT_FORMAT_;
|
||||
|
||||
|
||||
/**
|
||||
@brief A model format for Unix-like .conf files (where space characters
|
||||
are delimiters between keys and values)
|
||||
**/
|
||||
/* All fields are set to `0` here. */
|
||||
static const IniFormat INI_UNIXLIKE_FORMAT = _LIBCONFINI_UNIXLIKE_FORMAT_;
|
||||
|
||||
|
||||
/**
|
||||
@brief If set to `true`, key and section names in case-insensitive INI
|
||||
formats will be dispatched lowercase, verbatim otherwise (default
|
||||
value: `false`)
|
||||
**/
|
||||
extern bool INI_GLOBAL_LOWERCASE_MODE;
|
||||
|
||||
|
||||
/**
|
||||
@brief Value to be assigned to implicit keys (default value: `NULL`)
|
||||
**/
|
||||
extern char * INI_GLOBAL_IMPLICIT_VALUE;
|
||||
|
||||
|
||||
/**
|
||||
@brief Length of the value assigned to implicit keys (default value: `0`)
|
||||
**/
|
||||
extern size_t INI_GLOBAL_IMPLICIT_V_LEN;
|
||||
|
||||
|
||||
|
||||
/* CLEAN THE PRIVATE ENVIRONMENT */
|
||||
|
||||
|
||||
#undef _LIBCONFINI_UNIXLIKE_FORMAT_
|
||||
#undef _LIBCONFINI_DEFAULT_FORMAT_
|
||||
#undef _LIBCONFINI_INIFORMAT_TYPE_
|
||||
#undef __INIFORMAT_TABLE_CB_ZERO__
|
||||
#undef __INIFORMAT_TABLE_CB_DEFAULT__
|
||||
#undef __INIFORMAT_TABLE_CB_FIELDS__
|
||||
|
||||
|
||||
|
||||
/* END OF `_LIBCONFINI_HEADER_` */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* EOF */
|
||||
|
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
|
@ -1 +0,0 @@
|
|||
#define EMGAUWA_CONTROLLER_VERSION "@CMAKE_PROJECT_VERSION@"
|
Loading…
Reference in a new issue