init rewrite
This commit is contained in:
parent
9a44bc494e
commit
6d828fcffc
100 changed files with 50541 additions and 2707 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,5 +1,4 @@
|
||||||
build
|
build/
|
||||||
cmake-build-debug
|
docs/
|
||||||
.idea
|
|
||||||
|
|
||||||
sql/*.h
|
include/migrations/*.sql.h
|
||||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
||||||
[submodule "drogon"]
|
|
||||||
path = drogon
|
|
||||||
url = https://github.com/an-tao/drogon.git
|
|
103
CMakeLists.txt
103
CMakeLists.txt
|
@ -1,70 +1,57 @@
|
||||||
cmake_minimum_required (VERSION 3.2)
|
cmake_minimum_required (VERSION 3.7)
|
||||||
project(core)
|
project(core)
|
||||||
|
|
||||||
include(CheckIncludeFileCXX)
|
add_executable(core main.c)
|
||||||
|
|
||||||
check_include_file_cxx(any HAS_ANY)
|
option(WIRING_PI_DEBUG "Use WiringPi Debugging Tool (OFF)" OFF)
|
||||||
check_include_file_cxx(string_view HAS_STRING_VIEW)
|
|
||||||
if(HAS_ANY AND HAS_STRING_VIEW)
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
else()
|
|
||||||
set(CMAKE_CXX_STANDARD 14)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
#SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -Werror -Wpedantic -lwiringPi -lwiringPiDev -luuid -llmdb -g")
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wpedantic -Werror -Wall -Wextra -luuid -lsqlite3 -g")
|
||||||
|
|
||||||
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -latomic")
|
string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE)
|
||||||
|
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")
|
||||||
|
|
||||||
##########
|
if(WIRING_PI_DEBUG)
|
||||||
# If you include the drogon source code locally in your project, use this method to add drogon
|
message("Showing wiringPi calls as debug")
|
||||||
# add_subdirectory(drogon)
|
add_definitions("-DWIRING_PI_DEBUG")
|
||||||
# set(Drogon_DIR ${PROJECT_BINARY_DIR}/drogon)
|
endif(WIRING_PI_DEBUG)
|
||||||
# find_package(Drogon CONFIG REQUIRED NO_DEFAULT_PATH)
|
|
||||||
# include_directories(${DROGON_INCLUDE_DIRS})
|
|
||||||
# link_libraries(${DROGON_LIBRARIES})
|
|
||||||
##########
|
|
||||||
|
|
||||||
# find_package(Drogon CONFIG REQUIRED)
|
add_definitions("-DMG_ENABLE_EXTRA_ERRORS_DESC")
|
||||||
# include_directories(${DROGON_INCLUDE_DIRS})
|
|
||||||
# link_libraries(${DROGON_LIBRARIES})
|
|
||||||
|
|
||||||
if(CMAKE_CXX_STANDARD LESS 17)
|
aux_source_directory(vendor VENDOR_SRC) # vendor first to put their warnings on top
|
||||||
#With C++14, use boost to support any and string_view
|
aux_source_directory(. SRC_DIR)
|
||||||
message(STATUS "use c++14")
|
aux_source_directory(models MODELS_SRC)
|
||||||
find_package(Boost 1.61.0 REQUIRED)
|
aux_source_directory(helpers HELPERS_SRC)
|
||||||
include_directories(${Boost_INCLUDE_DIRS})
|
aux_source_directory(handlers HANDLERS_SRC)
|
||||||
else()
|
aux_source_directory(endpoints ENDPOINTSS_SRC)
|
||||||
message(STATUS "use c++17")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
aux_source_directory(./ SRC_DIR)
|
configure_file("core.ini" "core.ini" COPYONLY)
|
||||||
aux_source_directory(controllers CTL_SRC)
|
|
||||||
aux_source_directory(filters FILTER_SRC)
|
|
||||||
aux_source_directory(plugins PLUGIN_SRC)
|
|
||||||
aux_source_directory(models MODEL_SRC)
|
|
||||||
aux_source_directory(helpers HELPER_SRC)
|
|
||||||
|
|
||||||
file(GLOB SCP_LIST ${CMAKE_CURRENT_SOURCE_DIR}/views/*.csp)
|
target_sources(core PRIVATE ${VENDOR_SRC} ${SRC_DIR} ${HANDLERS_SRC} ${HELPERS_SRC} ${MODELS_SRC} ${ENDPOINTSS_SRC})
|
||||||
foreach(cspFile ${SCP_LIST})
|
target_include_directories(core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
message(STATUS "cspFile:" ${cspFile})
|
target_include_directories(core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor)
|
||||||
EXEC_PROGRAM(basename ARGS "${cspFile} .csp" OUTPUT_VARIABLE classname)
|
|
||||||
message(STATUS "view classname:" ${classname})
|
|
||||||
ADD_CUSTOM_COMMAND(OUTPUT ${classname}.h ${classname}.cc
|
|
||||||
COMMAND drogon_ctl
|
|
||||||
ARGS create view ${cspFile}
|
|
||||||
DEPENDS ${cspFile}
|
|
||||||
VERBATIM )
|
|
||||||
set(VIEWSRC ${VIEWSRC} ${classname}.cc)
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
configure_file("config.json" "config.json" COPYONLY)
|
add_custom_target(migrations
|
||||||
|
COMMAND ./compile_migrations.sh
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
add_custom_target(run
|
||||||
add_executable(core ${SRC_DIR} ${CTL_SRC} ${FILTER_SRC} ${VIEWSRC} ${PLUGIN_SRC} ${MODEL_SRC} ${HELPER_SRC})
|
COMMAND ./core start
|
||||||
|
DEPENDS core
|
||||||
add_custom_target(compiled_migrations ${CMAKE_CURRENT_SOURCE_DIR}/compile_migrations.sh)
|
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
|
||||||
add_dependencies(core compiled_migrations)
|
)
|
||||||
|
add_custom_target(debug
|
||||||
add_subdirectory(drogon)
|
COMMAND valgrind ./core start
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
|
DEPENDS core
|
||||||
|
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
|
||||||
|
)
|
||||||
|
add_custom_target(debug-full
|
||||||
|
COMMAND valgrind --leak-check=full --show-leak-kinds=all ./core start
|
||||||
|
DEPENDS core
|
||||||
|
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
|
||||||
|
)
|
||||||
|
add_custom_target(docs
|
||||||
|
COMMAND doxygen
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
|
@ -6,7 +6,7 @@ migration_num=0;
|
||||||
|
|
||||||
while [ -f sql/migration_$migration_num.sql ]
|
while [ -f sql/migration_$migration_num.sql ]
|
||||||
do
|
do
|
||||||
xxd -i sql/migration_$migration_num.sql | sed 's/\([0-9a-f]\)$/\0, 0x00/' > sql/migration_$migration_num.h
|
xxd -i sql/migration_$migration_num.sql | sed 's/\([0-9a-f]\)$/\0, 0x00/' > include/migrations/$migration_num.sql.h
|
||||||
((migration_num++));
|
((migration_num++));
|
||||||
done;
|
done;
|
||||||
|
|
||||||
|
|
74
config.c
Normal file
74
config.c
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <logger.h>
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
config_t global_config;
|
||||||
|
|
||||||
|
#define CONFINI_IS_KEY(SECTION, KEY) \
|
||||||
|
(ini_array_match(SECTION, disp->append_to, '.', disp->format) && \
|
||||||
|
ini_string_match_ii(KEY, disp->data, disp->format))
|
||||||
|
|
||||||
|
int
|
||||||
|
config_load(IniDispatch *disp, void *config_void)
|
||||||
|
{
|
||||||
|
config_t *config = (config_t*)config_void;
|
||||||
|
|
||||||
|
if(disp->type == INI_KEY)
|
||||||
|
{
|
||||||
|
if(CONFINI_IS_KEY("core", "database"))
|
||||||
|
{
|
||||||
|
config->database = malloc(sizeof(char) * (strlen(disp->value) + 1));
|
||||||
|
strcpy(config->database, disp->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(CONFINI_IS_KEY("core", "log-level"))
|
||||||
|
{
|
||||||
|
if(strcasecmp(disp->value, "trace") == 0)
|
||||||
|
{
|
||||||
|
config->log_level = LOG_LEVEL_TRACE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(strcasecmp(disp->value, "debug") == 0)
|
||||||
|
{
|
||||||
|
config->log_level = LOG_LEVEL_DEBUG;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(strcasecmp(disp->value, "info") == 0)
|
||||||
|
{
|
||||||
|
config->log_level = LOG_LEVEL_INFO;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(strcasecmp(disp->value, "warn") == 0)
|
||||||
|
{
|
||||||
|
config->log_level = LOG_LEVEL_WARN;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(strcasecmp(disp->value, "error") == 0)
|
||||||
|
{
|
||||||
|
config->log_level = LOG_LEVEL_ERROR;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(strcasecmp(disp->value, "fatal") == 0)
|
||||||
|
{
|
||||||
|
config->log_level = LOG_LEVEL_FATAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
LOG_WARN("invalid log-level '%s'\n", disp->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(CONFINI_IS_KEY("core", "discovery-port"))
|
||||||
|
{
|
||||||
|
config->discovery_port = atoi(disp->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(CONFINI_IS_KEY("core", "server-port"))
|
||||||
|
{
|
||||||
|
strcpy(config->server_port, disp->value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
22
config.cc
22
config.cc
|
@ -1,22 +0,0 @@
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
namespace config
|
|
||||||
{
|
|
||||||
char version[] = "0.0.1";
|
|
||||||
|
|
||||||
uint16_t discover_max_client_backlog = 20;
|
|
||||||
uint16_t discover_port_dev = 4420;
|
|
||||||
uint16_t discover_port = 4419;
|
|
||||||
uint16_t discover_timeout_ms = 2000;
|
|
||||||
uint8_t discover_code_accept = 0;
|
|
||||||
uint8_t discover_code_reject = 100;
|
|
||||||
|
|
||||||
uint8_t command_code_get_time = 1;
|
|
||||||
uint8_t command_code_get_id = 2;
|
|
||||||
uint8_t command_code_set_name = 100;
|
|
||||||
uint8_t command_code_get_name = 101;
|
|
||||||
uint8_t command_code_set_schedule = 102;
|
|
||||||
uint8_t command_code_get_schedule = 103;
|
|
||||||
uint8_t command_code_set_relay_name = 104;
|
|
||||||
uint8_t command_code_get_relay_name = 105;
|
|
||||||
}
|
|
27
config.h
27
config.h
|
@ -1,27 +0,0 @@
|
||||||
#ifndef EMGAUWA_CORE_CONFIG_H
|
|
||||||
#define EMGAUWA_CORE_CONFIG_H
|
|
||||||
|
|
||||||
#include <stdint-gcc.h>
|
|
||||||
|
|
||||||
namespace config
|
|
||||||
{
|
|
||||||
extern char version[];
|
|
||||||
|
|
||||||
extern uint16_t discover_max_client_backlog;
|
|
||||||
extern uint16_t discover_port_dev;
|
|
||||||
extern uint16_t discover_port;
|
|
||||||
extern uint16_t discover_timeout_ms;
|
|
||||||
extern uint8_t discover_code_accept;
|
|
||||||
extern uint8_t discover_code_reject;
|
|
||||||
|
|
||||||
extern uint8_t command_code_get_time;
|
|
||||||
extern uint8_t command_code_get_id;
|
|
||||||
extern uint8_t command_code_set_name;
|
|
||||||
extern uint8_t command_code_get_name;
|
|
||||||
extern uint8_t command_code_set_schedule;
|
|
||||||
extern uint8_t command_code_get_schedule;
|
|
||||||
extern uint8_t command_code_set_relay_name;
|
|
||||||
extern uint8_t command_code_get_relay_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //EMGAUWA_CORE_CONFIG_H
|
|
63
config.json
63
config.json
|
@ -1,63 +0,0 @@
|
||||||
{
|
|
||||||
"listeners": [
|
|
||||||
{
|
|
||||||
"address": "0.0.0.0",
|
|
||||||
"port": 5000,
|
|
||||||
"https": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"app": {
|
|
||||||
"threads_num": 1,
|
|
||||||
"enable_session": false,
|
|
||||||
"session_timeout": 0,
|
|
||||||
"document_root": "./static/",
|
|
||||||
"home_page": "index.html",
|
|
||||||
"upload_path": "uploads",
|
|
||||||
"file_types": [
|
|
||||||
"gif",
|
|
||||||
"png",
|
|
||||||
"jpg",
|
|
||||||
"js",
|
|
||||||
"css",
|
|
||||||
"html",
|
|
||||||
"ico",
|
|
||||||
"swf",
|
|
||||||
"xap",
|
|
||||||
"apk",
|
|
||||||
"cur",
|
|
||||||
"xml"
|
|
||||||
],
|
|
||||||
"max_connections": 100000,
|
|
||||||
"max_connections_per_ip": 0,
|
|
||||||
"load_dynamic_views": false,
|
|
||||||
"dynamic_views_path": [
|
|
||||||
"./views"
|
|
||||||
],
|
|
||||||
"log": {
|
|
||||||
"logfile_base_name": "",
|
|
||||||
"log_size_limit": 100000000,
|
|
||||||
"log_level": "DEBUG"
|
|
||||||
},
|
|
||||||
"run_as_daemon": false,
|
|
||||||
"relaunch_on_error": false,
|
|
||||||
"use_sendfile": true,
|
|
||||||
"use_gzip": true,
|
|
||||||
"static_files_cache_time": 5,
|
|
||||||
"idle_connection_timeout": 60,
|
|
||||||
"server_header_field": "",
|
|
||||||
"keepalive_requests": 0,
|
|
||||||
"pipelining_requests": 0,
|
|
||||||
"gzip_static": true,
|
|
||||||
"client_max_body_size": "1M",
|
|
||||||
"client_max_memory_body_size": "64K",
|
|
||||||
"client_max_websocket_message_size": "128K"
|
|
||||||
},
|
|
||||||
"plugins": [{
|
|
||||||
"dependencies": [],
|
|
||||||
"config": {
|
|
||||||
"heartbeat_interval": 2
|
|
||||||
}
|
|
||||||
|
|
||||||
}],
|
|
||||||
"custom_config": {}
|
|
||||||
}
|
|
185
config.template
185
config.template
|
@ -1,185 +0,0 @@
|
||||||
/* This is a JSON format configuration file
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
//ssl:The global ssl files setting
|
|
||||||
"ssl": {
|
|
||||||
"cert": "../../trantor/trantor/tests/server.pem",
|
|
||||||
"key": "../../trantor/trantor/tests/server.pem"
|
|
||||||
},
|
|
||||||
"listeners": [
|
|
||||||
{
|
|
||||||
//address: Ip address,0.0.0.0 by default
|
|
||||||
"address": "0.0.0.0",
|
|
||||||
//port: Port number
|
|
||||||
"port": 80,
|
|
||||||
//https: If true, use https for security,false by default
|
|
||||||
"https": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"address": "0.0.0.0",
|
|
||||||
"port": 443,
|
|
||||||
"https": true,
|
|
||||||
//cert,key: Cert file path and key file path, empty by default,
|
|
||||||
//if empty, use the global setting
|
|
||||||
"cert": "",
|
|
||||||
"key": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"db_clients": [
|
|
||||||
{
|
|
||||||
//name: Name of the client,'default' by default
|
|
||||||
//"name":"",
|
|
||||||
//rdbms: Server type, postgresql,mysql or sqlite3, "postgresql" by default
|
|
||||||
"rdbms": "postgresql",
|
|
||||||
//filename: Sqlite3 db file name
|
|
||||||
//"filename":"",
|
|
||||||
//host: Server address,localhost by default
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
//port: Server port, 5432 by default
|
|
||||||
"port": 5432,
|
|
||||||
//dbname: Database name
|
|
||||||
"dbname": "test",
|
|
||||||
//user: 'postgres' by default
|
|
||||||
"user": "",
|
|
||||||
//passwd: '' by default
|
|
||||||
"passwd": "",
|
|
||||||
//is_fast: false by default, if it is true, the client is faster but user can't call
|
|
||||||
//any synchronous interface of it.
|
|
||||||
"is_fast": false,
|
|
||||||
//connection_number: 1 by default, valid only if is_fast is false.
|
|
||||||
"connection_number": 1
|
|
||||||
}
|
|
||||||
],*/
|
|
||||||
"app": {
|
|
||||||
//threads_num: The number of IO threads, 1 by default, if the value is set to 0, the number of threads
|
|
||||||
//is the number of CPU cores
|
|
||||||
"threads_num": 1,
|
|
||||||
//enable_session: False by default
|
|
||||||
"enable_session": false,
|
|
||||||
"session_timeout": 0,
|
|
||||||
//document_root: Root path of HTTP document, defaut path is ./
|
|
||||||
"document_root": "./",
|
|
||||||
//home_page: Set the HTML file of the home page, the default value is "index.html"
|
|
||||||
//If there isn't any handler registered to the path "/", the home page file in the "document_root" is send to clients as a response
|
|
||||||
//to the request for "/".
|
|
||||||
"home_page": "index.html",
|
|
||||||
//upload_path: The path to save the uploaded file. "uploads" by default.
|
|
||||||
//If the path isn't prefixed with /, ./ or ../,
|
|
||||||
//it is relative path of document_root path
|
|
||||||
"upload_path": "uploads",
|
|
||||||
/* file_types:
|
|
||||||
* HTTP download file types,The file types supported by drogon
|
|
||||||
* by default are "html", "js", "css", "xml", "xsl", "txt", "svg",
|
|
||||||
* "ttf", "otf", "woff2", "woff" , "eot", "png", "jpg", "jpeg",
|
|
||||||
* "gif", "bmp", "ico", "icns", etc. */
|
|
||||||
"file_types": [
|
|
||||||
"gif",
|
|
||||||
"png",
|
|
||||||
"jpg",
|
|
||||||
"js",
|
|
||||||
"css",
|
|
||||||
"html",
|
|
||||||
"ico",
|
|
||||||
"swf",
|
|
||||||
"xap",
|
|
||||||
"apk",
|
|
||||||
"cur",
|
|
||||||
"xml"
|
|
||||||
],
|
|
||||||
//max_connections: maximum connections number,100000 by default
|
|
||||||
"max_connections": 100000,
|
|
||||||
//max_connections_per_ip: maximum connections number per clinet,0 by default which means no limit
|
|
||||||
"max_connections_per_ip": 0,
|
|
||||||
//Load_dynamic_views: False by default, when set to true, drogon
|
|
||||||
//compiles and loads dynamically "CSP View Files" in directories defined
|
|
||||||
//by "dynamic_views_path"
|
|
||||||
"load_dynamic_views": false,
|
|
||||||
//dynamic_views_path: If the path isn't prefixed with /, ./ or ../,
|
|
||||||
//it is relative path of document_root path
|
|
||||||
"dynamic_views_path": [
|
|
||||||
"./views"
|
|
||||||
],
|
|
||||||
//log: Set log output, drogon output logs to stdout by default
|
|
||||||
"log": {
|
|
||||||
//log_path: Log file path,empty by default,in which case,logs are output to the stdout
|
|
||||||
//"log_path": "./",
|
|
||||||
//logfile_base_name: Log file base name,empty by default which means drogon names logfile as
|
|
||||||
//drogon.log ...
|
|
||||||
"logfile_base_name": "",
|
|
||||||
//log_size_limit: 100000000 bytes by default,
|
|
||||||
//When the log file size reaches "log_size_limit", the log file is switched.
|
|
||||||
"log_size_limit": 100000000,
|
|
||||||
//log_level: "DEBUG" by default,options:"TRACE","DEBUG","INFO","WARN"
|
|
||||||
//The TRACE level is only valid when built in DEBUG mode.
|
|
||||||
"log_level": "DEBUG"
|
|
||||||
},
|
|
||||||
//run_as_daemon: False by default
|
|
||||||
"run_as_daemon": false,
|
|
||||||
//relaunch_on_error: False by default, if true, the program will be restart by the parent after exiting;
|
|
||||||
"relaunch_on_error": false,
|
|
||||||
//use_sendfile: True by default, if ture, the program
|
|
||||||
//uses sendfile() system-call to send static files to clients;
|
|
||||||
"use_sendfile": true,
|
|
||||||
//use_gzip: True by default, use gzip to compress the response body's content;
|
|
||||||
"use_gzip": true,
|
|
||||||
//static_files_cache_time: 5 (seconds) by default, the time in which the static file response is cached,
|
|
||||||
//0 means cache forever, the negative value means no cache
|
|
||||||
"static_files_cache_time": 5,
|
|
||||||
//simple_controllers_map: Used to configure mapping from path to simple controller
|
|
||||||
"simple_controllers_map": [{
|
|
||||||
"path": "/path/name",
|
|
||||||
"controller": "controllerClassName",
|
|
||||||
"http_methods": [
|
|
||||||
"get",
|
|
||||||
"post"
|
|
||||||
],
|
|
||||||
"filters": [
|
|
||||||
"FilterClassName"
|
|
||||||
]
|
|
||||||
}],
|
|
||||||
//idle_connection_timeout: Defaults to 60 seconds, the lifetime
|
|
||||||
//of the connection without read or write
|
|
||||||
"idle_connection_timeout": 60,
|
|
||||||
//server_header_field: Set the 'server' header field in each response sent by drogon,
|
|
||||||
//empty string by default with which the 'server' header field is set to "Server: drogon/version string\r\n"
|
|
||||||
"server_header_field": "",
|
|
||||||
//keepalive_requests: Set the maximum number of requests that can be served through one keep-alive connection.
|
|
||||||
//After the maximum number of requests are made, the connection is closed.
|
|
||||||
//The default value of 0 means no limit.
|
|
||||||
"keepalive_requests": 0,
|
|
||||||
//pipelining_requests: Set the maximum number of unhandled requests that can be cached in pipelining buffer.
|
|
||||||
//After the maximum number of requests are made, the connection is closed.
|
|
||||||
//The default value of 0 means no limit.
|
|
||||||
"pipelining_requests": 0,
|
|
||||||
//gzip_static: If it is set to true, when the client requests a static file, drogon first finds the compressed
|
|
||||||
//file with the extension ".gz" in the same path and send the compressed file to the client.
|
|
||||||
//The default value of gzip_static is true.
|
|
||||||
"gzip_static": true,
|
|
||||||
//client_max_body_size: Set the maximum body size of HTTP requests received by drogon. The default value is "1M".
|
|
||||||
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
|
||||||
"client_max_body_size": "1M",
|
|
||||||
//max_memory_body_size: Set the maximum body size in memory of HTTP requests received by drogon. The default value is "64K" bytes.
|
|
||||||
//If the body size of a HTTP request exceeds this limit, the body is stored to a temporary file for processing.
|
|
||||||
//Setting it to "" means no limit.
|
|
||||||
"client_max_memory_body_size": "64K",
|
|
||||||
//client_max_websocket_message_size: Set the maximum size of messages sent by WebSocket client. The default value is "128K".
|
|
||||||
//One can set it to "1024", "1k", "10M", "1G", etc. Setting it to "" means no limit.
|
|
||||||
"client_max_websocket_message_size": "128K"
|
|
||||||
},
|
|
||||||
//plugins: Define all plugins running in the application
|
|
||||||
"plugins": [{
|
|
||||||
//name: The class name of the plugin
|
|
||||||
//"name": "TestPlugin",
|
|
||||||
//dependencies: Plugins that the plugin depends on. It can be commented out
|
|
||||||
"dependencies": [],
|
|
||||||
//config: The configuration of the plugin. This json object is the parameter to initialize the plugin.
|
|
||||||
//It can be commented out
|
|
||||||
"config": {
|
|
||||||
"heartbeat_interval": 2
|
|
||||||
}
|
|
||||||
|
|
||||||
}],
|
|
||||||
//custom_config: custom configuration for users. This object can be get by the app().getCustomConfig() method.
|
|
||||||
"custom_config": {}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
#include <models/controller_dbo.h>
|
|
||||||
#include <config.h>
|
|
||||||
#include "api_v1_controllers.h"
|
|
||||||
using namespace api::v1;
|
|
||||||
|
|
||||||
void
|
|
||||||
controllers::get_all(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback)
|
|
||||||
{
|
|
||||||
controller_dbo **all_controllers = controller_dbo::get_all();
|
|
||||||
Json::Value all_controllers_json(Json::arrayValue);
|
|
||||||
|
|
||||||
for(int i = 0; all_controllers[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
all_controllers_json.append(all_controllers[i]->to_json());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(all_controllers_json);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
|
|
||||||
controller_dbo::free_list(all_controllers);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
controllers::get_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string& controller_id)
|
|
||||||
{
|
|
||||||
controller_dbo **controllers = controller_dbo::get_by_simple("id", controller_id.c_str(), (intptr_t) &sqlite3_bind_text);
|
|
||||||
|
|
||||||
if(controllers[0])
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(controllers[0]->to_json());
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k404NotFound);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
controller_dbo::free_list(controllers);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
controllers::delete_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string& controller_id)
|
|
||||||
{
|
|
||||||
controller_dbo **controllers = controller_dbo::get_by_simple("id", controller_id.c_str(), (intptr_t) &sqlite3_bind_text);
|
|
||||||
|
|
||||||
if(controllers[0])
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
if(!controllers[0]->remove())
|
|
||||||
{
|
|
||||||
resp->setStatusCode(k500InternalServerError);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k404NotFound);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
controller_dbo::free_list(controllers);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
controllers::put_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback,
|
|
||||||
const std::string &controller_id)
|
|
||||||
{
|
|
||||||
Json::Value body = *req->getJsonObject();
|
|
||||||
|
|
||||||
controller_dbo **controllers = controller_dbo::get_by_simple("id", controller_id.c_str(), (intptr_t) &sqlite3_bind_text);
|
|
||||||
|
|
||||||
if(controllers[0])
|
|
||||||
{
|
|
||||||
strncpy(controllers[0]->name, body["name"].asCString(), 127);
|
|
||||||
strncpy(controllers[0]->ip, body["ip"].asCString(), 16);
|
|
||||||
|
|
||||||
controllers[0]->name[127] = '\0';
|
|
||||||
controllers[0]->ip[16] = '\0';
|
|
||||||
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
if(controllers[0]->update())
|
|
||||||
{
|
|
||||||
controllers[0]->command(config::command_code_set_name, controllers[0]->name);
|
|
||||||
resp = HttpResponse::newHttpJsonResponse(controllers[0]->to_json());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resp->setStatusCode(k500InternalServerError);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k404NotFound);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
controller_dbo::free_list(controllers);
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <drogon/HttpController.h>
|
|
||||||
using namespace drogon;
|
|
||||||
namespace api::v1
|
|
||||||
{
|
|
||||||
class controllers:public drogon::HttpController<controllers>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
METHOD_LIST_BEGIN
|
|
||||||
METHOD_ADD(controllers::post_discover, "/discover", Post, Options);
|
|
||||||
METHOD_ADD(controllers::get_all, "/", Get, Options);
|
|
||||||
METHOD_ADD(controllers::get_one_by_id, "/{1}", Get, Options);
|
|
||||||
METHOD_ADD(controllers::delete_one_by_id, "/{1}", Delete, Options);
|
|
||||||
METHOD_ADD(controllers::put_one_by_id, "/{1}", Put, Options, "filters::json_required", "filters::controllers::valid_json");
|
|
||||||
|
|
||||||
METHOD_ADD(controllers::get_relays_all, "/{1}/relays/", Get, Options);
|
|
||||||
METHOD_ADD(controllers::get_relays_one_by_id_and_num, "/{1}/relays/{2}", Get, Options);
|
|
||||||
METHOD_ADD(controllers::put_relays_one_by_id_and_num, "/{1}/relays/{2}", Put, Options, "filters::json_required", "filters::relays::valid_json");
|
|
||||||
METHOD_LIST_END
|
|
||||||
|
|
||||||
static void post_discover(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback);
|
|
||||||
static void get_all(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback);
|
|
||||||
static void get_one_by_id(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& controller_id);
|
|
||||||
static void delete_one_by_id(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& controller_id);
|
|
||||||
static void put_one_by_id(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& controller_id);
|
|
||||||
|
|
||||||
static void get_relays_all(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& controller_id);
|
|
||||||
static void get_relays_one_by_id_and_num(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& controller_id, int relay_num);
|
|
||||||
static void put_relays_one_by_id_and_num(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& controller_id, int relay_num);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
#include <netdb.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <config.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include <cmath>
|
|
||||||
#include <models/controller_dbo.h>
|
|
||||||
#include "api_v1_controllers.h"
|
|
||||||
|
|
||||||
using namespace api::v1;
|
|
||||||
|
|
||||||
void controllers::post_discover(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback)
|
|
||||||
{
|
|
||||||
|
|
||||||
int discover_server_socket = helpers::bind_tcp_server("0.0.0.0", "0", config::discover_max_client_backlog);
|
|
||||||
int discover_server_port = helpers::get_server_port(discover_server_socket);
|
|
||||||
|
|
||||||
if(discover_server_port == -1)
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k500InternalServerError);
|
|
||||||
callback(resp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value payload;
|
|
||||||
payload["port"] = discover_server_port;
|
|
||||||
|
|
||||||
Json::StreamWriterBuilder json_writer;
|
|
||||||
|
|
||||||
if(helpers::send_udp_broadcast("255.255.255.255", config::discover_port_dev, Json::writeString(json_writer, payload).c_str()) < 0)
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k500InternalServerError);
|
|
||||||
callback(resp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_storage their_addr{};
|
|
||||||
socklen_t addr_size;
|
|
||||||
int client_fd, s_ret;
|
|
||||||
fd_set accept_fds;
|
|
||||||
struct timeval timeout{};
|
|
||||||
|
|
||||||
uint8_t discover_answer_buf[1];
|
|
||||||
uint8_t discover_header_buf[1];
|
|
||||||
|
|
||||||
controller_dbo **known_controllers = controller_dbo::get_all();
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
addr_size = sizeof(their_addr);
|
|
||||||
|
|
||||||
FD_ZERO(&accept_fds);
|
|
||||||
FD_SET(discover_server_socket, &accept_fds); // NOLINT(hicpp-signed-bitwise)
|
|
||||||
|
|
||||||
timeout.tv_sec = config::discover_timeout_ms / 1000;
|
|
||||||
timeout.tv_usec = (config::discover_timeout_ms % 1000) * 1000;
|
|
||||||
|
|
||||||
s_ret = select(discover_server_socket + 1, &accept_fds, nullptr, nullptr, &timeout);
|
|
||||||
if(s_ret == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if((client_fd = accept(discover_server_socket, (struct sockaddr *) &their_addr, &addr_size)) < 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error Accepting client " << strerror(errno);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(recv(client_fd, discover_header_buf, 1, 0) < 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error Receiving header from client";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t payload_length = discover_header_buf[0];
|
|
||||||
char *answer_payload = (char*)malloc((payload_length + 1) * sizeof(*answer_payload));
|
|
||||||
|
|
||||||
if(recv(client_fd, answer_payload, payload_length, 0) < 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error Receiving payload from client";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sockaddr_in addr{};
|
|
||||||
socklen_t client_addr_size = sizeof(struct sockaddr_in);
|
|
||||||
if(getpeername(client_fd, (struct sockaddr *)&addr, &client_addr_size) != 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
LOG_ERROR << "Error Receiving payload from client";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
answer_payload[payload_length] = '\0';
|
|
||||||
std::istringstream answer_payload_stream(answer_payload);
|
|
||||||
|
|
||||||
Json::CharReaderBuilder json_reader;
|
|
||||||
json_reader["strictRoot"] = true;
|
|
||||||
|
|
||||||
std::string errors;
|
|
||||||
Json::Value client_info;
|
|
||||||
|
|
||||||
if (!Json::parseFromStream(json_reader, answer_payload_stream, &client_info, &errors))
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Failed to parse response: " << errors;
|
|
||||||
discover_answer_buf[0] = config::discover_code_reject;
|
|
||||||
send(client_fd, discover_answer_buf, sizeof(uint8_t), 0);
|
|
||||||
close(client_fd);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(answer_payload);
|
|
||||||
|
|
||||||
const char *discovered_id = client_info["id"].asCString();
|
|
||||||
|
|
||||||
bool found_discovered_in_list = false;
|
|
||||||
|
|
||||||
for(int i = 0; known_controllers[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
if(!found_discovered_in_list)
|
|
||||||
{
|
|
||||||
if(strcmp(known_controllers[i]->id, discovered_id) == 0)
|
|
||||||
{
|
|
||||||
known_controllers[i]->active = true;
|
|
||||||
known_controllers[i]->update();
|
|
||||||
delete known_controllers[i];
|
|
||||||
found_discovered_in_list = true;
|
|
||||||
known_controllers[i] = known_controllers[i + 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
known_controllers[i] = known_controllers[i + 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!found_discovered_in_list)
|
|
||||||
{
|
|
||||||
controller_dbo discovered_controller{};
|
|
||||||
strcpy(discovered_controller.ip, inet_ntoa(addr.sin_addr));
|
|
||||||
strcpy(discovered_controller.id, discovered_id);
|
|
||||||
strcpy(discovered_controller.name, client_info["name"].asCString());
|
|
||||||
discovered_controller.relay_count = client_info["relay_count"].asInt();
|
|
||||||
discovered_controller.port = client_info["port"].asInt();
|
|
||||||
discovered_controller.active = true;
|
|
||||||
|
|
||||||
discovered_controller.insert();
|
|
||||||
}
|
|
||||||
|
|
||||||
discover_answer_buf[0] = config::discover_code_accept;
|
|
||||||
send(client_fd, discover_answer_buf, sizeof(uint8_t), 0);
|
|
||||||
close(client_fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(int i = 0; known_controllers[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
known_controllers[i]->active = false;
|
|
||||||
known_controllers[i]->update();
|
|
||||||
LOG_DEBUG << "Lost: " << known_controllers[i]->name;
|
|
||||||
}
|
|
||||||
controller_dbo::free_list(known_controllers);
|
|
||||||
|
|
||||||
controller_dbo **all_controllers = controller_dbo::get_all();
|
|
||||||
Json::Value all_controllers_json(Json::arrayValue);
|
|
||||||
|
|
||||||
for(int i = 0; all_controllers[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
all_controllers_json.append(all_controllers[i]->to_json());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(all_controllers_json);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
|
|
||||||
controller_dbo::free_list(all_controllers);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
#include <netdb.h>
|
|
||||||
#include <models/relay_dbo.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include <models/controller_dbo.h>
|
|
||||||
#include <models/schedule_dbo.h>
|
|
||||||
#include <config.h>
|
|
||||||
#include "api_v1_controllers.h"
|
|
||||||
|
|
||||||
using namespace api::v1;
|
|
||||||
|
|
||||||
void
|
|
||||||
controllers::get_relays_all(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback,
|
|
||||||
const std::string& controller_id)
|
|
||||||
{
|
|
||||||
relay_dbo **all_controller_relays = relay_dbo::get_by_simple("controller_id", (void *) controller_id.c_str(), (intptr_t) sqlite3_bind_text);
|
|
||||||
Json::Value all_relays_json(Json::arrayValue);
|
|
||||||
|
|
||||||
for(int i = 0; all_controller_relays[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
all_relays_json.append(all_controller_relays[i]->to_json());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(all_relays_json);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
|
|
||||||
relay_dbo::free_list(all_controller_relays);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
controllers::get_relays_one_by_id_and_num(const HttpRequestPtr &req,
|
|
||||||
std::function<void(const HttpResponsePtr &)> &&callback, const std::string& controller_id,
|
|
||||||
int relay_num)
|
|
||||||
{
|
|
||||||
relay_dbo *relay = relay_dbo::get_relay_for_controller(controller_id.c_str(), relay_num);
|
|
||||||
|
|
||||||
if(relay)
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(relay->to_json());
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
|
|
||||||
delete relay;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k404NotFound);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
controllers::put_relays_one_by_id_and_num(const HttpRequestPtr &req,
|
|
||||||
std::function<void(const HttpResponsePtr &)> &&callback, const std::string& controller_id,
|
|
||||||
int relay_num)
|
|
||||||
{
|
|
||||||
if(!relay_dbo::valid_num_for_controller(controller_id.c_str(), relay_num))
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k400BadRequest);
|
|
||||||
callback(resp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
relay_dbo *relay = relay_dbo::get_relay_for_controller(controller_id.c_str(), relay_num);
|
|
||||||
Json::Value body = *req->getJsonObject();
|
|
||||||
|
|
||||||
bool db_action_result;
|
|
||||||
|
|
||||||
if(relay)
|
|
||||||
{
|
|
||||||
strncpy(relay->name, body["name"].asCString(), 127);
|
|
||||||
strncpy(relay->active_schedule_id, body["active_schedule"].asCString(), 32);
|
|
||||||
|
|
||||||
db_action_result = relay->update();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
relay = new relay_dbo();
|
|
||||||
relay->number = relay_num;
|
|
||||||
strncpy(relay->name, body["name"].asCString(), 127);
|
|
||||||
strncpy(relay->active_schedule_id, body["active_schedule"].asCString(), 32);
|
|
||||||
strncpy(relay->controller_id, controller_id.c_str(), 32);
|
|
||||||
|
|
||||||
relay->reload_active_schedule();
|
|
||||||
|
|
||||||
db_action_result = relay->insert();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!db_action_result)
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k500InternalServerError);
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto schedules = schedule_dbo::get_by_simple("id", body["active_schedule"].asCString(), (intptr_t)&sqlite3_bind_text);
|
|
||||||
auto controllers = controller_dbo::get_by_simple("id", controller_id.c_str(), (intptr_t)&sqlite3_bind_text);
|
|
||||||
|
|
||||||
Json::Value payload;
|
|
||||||
payload["target"] = relay_num;
|
|
||||||
payload["schedule"] = schedules[0]->to_json();
|
|
||||||
|
|
||||||
Json::StreamWriterBuilder json_writer;
|
|
||||||
|
|
||||||
controllers[0]->command(config::command_code_set_schedule, Json::writeString(json_writer, payload).c_str());
|
|
||||||
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(relay->to_json());
|
|
||||||
callback(resp);
|
|
||||||
|
|
||||||
schedule_dbo::free_list(schedules);
|
|
||||||
controller_dbo::free_list(controllers);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete relay;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
#include <netdb.h>
|
|
||||||
#include <models/relay_dbo.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include "api_v1_relays.h"
|
|
||||||
using namespace api::v1;
|
|
||||||
|
|
||||||
void
|
|
||||||
relays::get_all(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback)
|
|
||||||
{
|
|
||||||
relay_dbo **all_relays = relay_dbo::get_all();
|
|
||||||
Json::Value all_relays_json(Json::arrayValue);
|
|
||||||
|
|
||||||
for(int i = 0; all_relays[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
all_relays_json.append(all_relays[i]->to_json());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(all_relays_json);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
|
|
||||||
relay_dbo::free_list(all_relays);
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <drogon/HttpController.h>
|
|
||||||
using namespace drogon;
|
|
||||||
namespace api::v1
|
|
||||||
{
|
|
||||||
class relays:public drogon::HttpController<relays>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
METHOD_LIST_BEGIN
|
|
||||||
METHOD_ADD(relays::get_all, "/", Get, Options);
|
|
||||||
METHOD_LIST_END
|
|
||||||
|
|
||||||
static void get_all(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,152 +0,0 @@
|
||||||
#include <netdb.h>
|
|
||||||
#include <models/schedule_dbo.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include "api_v1_schedules.h"
|
|
||||||
using namespace api::v1;
|
|
||||||
|
|
||||||
void
|
|
||||||
schedules::get_all(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback)
|
|
||||||
{
|
|
||||||
schedule_dbo **all_schedules = schedule_dbo::get_all();
|
|
||||||
Json::Value all_schedules_json(Json::arrayValue);
|
|
||||||
|
|
||||||
for(int i = 0; all_schedules[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
all_schedules_json.append(all_schedules[i]->to_json());
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::StreamWriterBuilder jw;
|
|
||||||
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(all_schedules_json);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
|
|
||||||
schedule_dbo::free_list(all_schedules);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
schedules::get_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string& schedule_id)
|
|
||||||
{
|
|
||||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("id", schedule_id.c_str(), (intptr_t) &sqlite3_bind_text);
|
|
||||||
|
|
||||||
if(schedules[0])
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(schedules[0]->to_json());
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k404NotFound);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
schedule_dbo::free_list(schedules);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
schedules::delete_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback, const std::string& schedule_id)
|
|
||||||
{
|
|
||||||
if(strcmp(schedule_id.c_str(), "off") == 0)
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k403Forbidden);
|
|
||||||
callback(resp);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("id", schedule_id.c_str(), (intptr_t) &sqlite3_bind_text);
|
|
||||||
|
|
||||||
if(schedules[0])
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
if(!schedules[0]->remove())
|
|
||||||
{
|
|
||||||
resp->setStatusCode(k500InternalServerError);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k404NotFound);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
schedule_dbo::free_list(schedules);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
schedules::post_new(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback)
|
|
||||||
{
|
|
||||||
Json::Value body = *req->jsonObject();
|
|
||||||
|
|
||||||
schedule_dbo new_schedule{};
|
|
||||||
|
|
||||||
strncpy(new_schedule.name, body["name"].asCString(), 127);
|
|
||||||
new_schedule.name[127] = '\0';
|
|
||||||
strncpy(new_schedule.id, drogon::utils::getUuid().c_str(), 32);
|
|
||||||
new_schedule.id[32] = '\0';
|
|
||||||
new_schedule.periods = helpers::parse_periods(body["periods"]);
|
|
||||||
|
|
||||||
if(!new_schedule.insert())
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k500InternalServerError);
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(new_schedule.to_json());
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
schedules::put_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback,
|
|
||||||
const std::string &schedule_id)
|
|
||||||
{
|
|
||||||
if(strcmp(schedule_id.c_str(), "off") == 0)
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k403Forbidden);
|
|
||||||
callback(resp);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value body = *req->jsonObject();
|
|
||||||
|
|
||||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("id", schedule_id.c_str(), (intptr_t) &sqlite3_bind_text);
|
|
||||||
|
|
||||||
if(schedules[0])
|
|
||||||
{
|
|
||||||
strncpy(schedules[0]->name, body["name"].asCString(), 127);
|
|
||||||
schedules[0]->name[127] = '\0';
|
|
||||||
delete schedules[0]->periods;
|
|
||||||
schedules[0]->periods = helpers::parse_periods(body["periods"]);
|
|
||||||
|
|
||||||
if(!schedules[0]->update())
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k500InternalServerError);
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpJsonResponse(schedules[0]->to_json());
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resp = HttpResponse::newHttpResponse();
|
|
||||||
resp->setStatusCode(k404NotFound);
|
|
||||||
|
|
||||||
callback(resp);
|
|
||||||
}
|
|
||||||
schedule_dbo::free_list(schedules);
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <drogon/HttpController.h>
|
|
||||||
using namespace drogon;
|
|
||||||
namespace api::v1
|
|
||||||
{
|
|
||||||
class schedules:public drogon::HttpController<schedules>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
METHOD_LIST_BEGIN
|
|
||||||
METHOD_ADD(schedules::post_new, "/", Post, Options, "filters::json_required", "filters::schedules::valid_json");
|
|
||||||
METHOD_ADD(schedules::get_all, "/", Get, Options);
|
|
||||||
METHOD_ADD(schedules::get_one_by_id, "/{1}", Get, Options);
|
|
||||||
METHOD_ADD(schedules::delete_one_by_id, "/{1}", Delete, Options);
|
|
||||||
METHOD_ADD(schedules::put_one_by_id, "/{1}", Put, Options, "filters::json_required", "filters::schedules::valid_json");
|
|
||||||
//METHOD_ADD(controllers::get_relays_all,"/{1}/relays",Get);
|
|
||||||
//METHOD_ADD(controllers::get_relays_one,"/{1}/relays/{2}",Get);
|
|
||||||
METHOD_LIST_END
|
|
||||||
|
|
||||||
static void post_new(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback);
|
|
||||||
static void get_all(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback);
|
|
||||||
static void get_one_by_id(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& schedule_id);
|
|
||||||
static void delete_one_by_id(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& schedule_id);
|
|
||||||
static void put_one_by_id(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& schedule_id);
|
|
||||||
//void get_relays_all(const HttpRequestPtr& req,std::function<void (const HttpResponsePtr &)> &&callback,std::string schedule_id);
|
|
||||||
//void get_relays_one(const HttpRequestPtr& req,std::function<void (const HttpResponsePtr &)> &&callback,std::string schedule_id,std::string relay_id);
|
|
||||||
};
|
|
||||||
}
|
|
5
core.ini
Normal file
5
core.ini
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[core]
|
||||||
|
server-port = 5000
|
||||||
|
discovery-port = 4421
|
||||||
|
database = core.sqlite
|
||||||
|
log-level = debug
|
67
database.c
Normal file
67
database.c
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <logger.h>
|
||||||
|
#include <database.h>
|
||||||
|
|
||||||
|
#include <migrations/0.sql.h>
|
||||||
|
|
||||||
|
sqlite3 *global_database;
|
||||||
|
|
||||||
|
int
|
||||||
|
database_migrate()
|
||||||
|
{
|
||||||
|
uint16_t version_num = 0;
|
||||||
|
int s, rc;
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT version_num FROM meta LIMIT 1;", -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:
|
||||||
|
LOG_INFO("migrating LEVEL 0\n");
|
||||||
|
rc = sqlite3_exec(global_database, (const char *)sql_migration_0_sql, NULL, NULL, &err_msg);
|
||||||
|
if(rc != 0)
|
||||||
|
{
|
||||||
|
LOG_FATAL("couldn't migrate LEVEL 0 (%s)\n", err_msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
new_version_num = 1;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(version_num == 0)
|
||||||
|
{
|
||||||
|
sqlite3_prepare_v2(global_database, "INSERT INTO meta (version_num) VALUES (?1);", -1, &stmt, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sqlite3_prepare_v2(global_database, "UPDATE meta SET version_num=?1;", -1, &stmt, NULL);
|
||||||
|
}
|
||||||
|
sqlite3_bind_int(stmt, 1, new_version_num);
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (rc != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
LOG_FATAL("couldn't write new schema version");
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return rc != SQLITE_DONE;
|
||||||
|
}
|
14
drivers/gpio.c
Normal file
14
drivers/gpio.c
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <wiringPi.h>
|
||||||
|
#include <piFace.h>
|
||||||
|
#include <wiring_debug.h>
|
||||||
|
|
||||||
|
#include <drivers.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
driver_gpio_set(int pin, int value)
|
||||||
|
{
|
||||||
|
// disable "unused parameter" warning (happens when using wiring_debug)
|
||||||
|
(void)pin;
|
||||||
|
(void)value;
|
||||||
|
digitalWrite(pin, value);
|
||||||
|
}
|
14
drivers/piface.c
Normal file
14
drivers/piface.c
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <wiringPi.h>
|
||||||
|
#include <piFace.h>
|
||||||
|
#include <wiring_debug.h>
|
||||||
|
|
||||||
|
#include <drivers.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
driver_piface_set(int pin, int value)
|
||||||
|
{
|
||||||
|
// disable "unused parameter" warning (happens when using wiring_debug)
|
||||||
|
(void)pin;
|
||||||
|
(void)value;
|
||||||
|
digitalWrite(PIFACE_GPIO_BASE + pin, value);
|
||||||
|
}
|
1
drogon
1
drogon
|
@ -1 +0,0 @@
|
||||||
Subproject commit 543d1a8c8062b3873ef89c64ffd7394c6dd7c7e8
|
|
159
endpoints/api_v1_schedules.c
Normal file
159
endpoints/api_v1_schedules.c
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
#include <cJSON.h>
|
||||||
|
#include <endpoints/api_v1_schedules.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <models/junction_tag.h>
|
||||||
|
#include <models/schedule.h>
|
||||||
|
#include <models/tag.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_schedules_POST(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm)
|
||||||
|
{
|
||||||
|
(void)args;
|
||||||
|
cJSON *json = cJSON_ParseWithLength(hm->body.p, hm->body.len);
|
||||||
|
|
||||||
|
if(json == NULL)
|
||||||
|
{
|
||||||
|
const char *error_ptr = cJSON_GetErrorPtr();
|
||||||
|
if (error_ptr != NULL)
|
||||||
|
{
|
||||||
|
LOG_ERROR("error before: %s\n", error_ptr);
|
||||||
|
}
|
||||||
|
cJSON_Delete(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||||
|
if(!cJSON_IsString(json_name) || (json_name->valuestring == NULL))
|
||||||
|
{
|
||||||
|
char *error_msg = "ERROR: no name for schedule provided";
|
||||||
|
mg_send_head(c, 400, strlen(error_msg), "Content-Type: text/plain");
|
||||||
|
mg_printf(c, "%s", error_msg);
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cJSON *json_period;
|
||||||
|
cJSON *json_periods = cJSON_GetObjectItemCaseSensitive(json, "periods");
|
||||||
|
|
||||||
|
schedule_t *new_schedule = malloc(sizeof(schedule_t));
|
||||||
|
|
||||||
|
new_schedule->id = 0;
|
||||||
|
uuid_generate(new_schedule->uid);
|
||||||
|
|
||||||
|
strncpy(new_schedule->name, json_name->valuestring, MAX_NAME_LENGTH);
|
||||||
|
new_schedule->name[MAX_NAME_LENGTH] = '\0';
|
||||||
|
|
||||||
|
int periods_count = cJSON_GetArraySize(json_periods);
|
||||||
|
new_schedule->periods = malloc(sizeof(period_t) * periods_count);
|
||||||
|
|
||||||
|
int periods_valid = 0;
|
||||||
|
|
||||||
|
cJSON_ArrayForEach(json_period, json_periods)
|
||||||
|
{
|
||||||
|
cJSON *json_period_start = cJSON_GetObjectItemCaseSensitive(json_period, "start");
|
||||||
|
cJSON *json_period_end = cJSON_GetObjectItemCaseSensitive(json_period, "end");
|
||||||
|
|
||||||
|
if(!cJSON_IsString(json_period_start) || (json_period_start->valuestring == NULL))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("period is missing start\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!cJSON_IsString(json_period_end) || (json_period_end->valuestring == NULL))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("period is missing end\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t start;
|
||||||
|
uint16_t end;
|
||||||
|
if(period_helper_parse_hhmm(json_period_start->valuestring, &start))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("couldn't parse start '%s'\n", json_period_start->valuestring);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(period_helper_parse_hhmm(json_period_end->valuestring, &end))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("couldn't parse end '%s'\n", json_period_end->valuestring);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_schedule->periods[periods_valid].start = start;
|
||||||
|
new_schedule->periods[periods_valid].end = end;
|
||||||
|
++periods_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_schedule->periods_count = periods_valid;
|
||||||
|
|
||||||
|
schedule_save(new_schedule);
|
||||||
|
|
||||||
|
junction_tag_remove_for_schedule(new_schedule->id);
|
||||||
|
cJSON *json_tag;
|
||||||
|
cJSON *json_tags = cJSON_GetObjectItemCaseSensitive(json, "tags");
|
||||||
|
cJSON_ArrayForEach(json_tag, json_tags)
|
||||||
|
{
|
||||||
|
if(!cJSON_IsString(json_tag) || (json_tag->valuestring == NULL))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("invalid tag in tags\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const char *tag = json_tag->valuestring;
|
||||||
|
int tag_id = tag_get_id(tag);
|
||||||
|
if(tag_id == 0)
|
||||||
|
{
|
||||||
|
tag_save(tag_id, tag);
|
||||||
|
tag_id = tag_get_id(tag);
|
||||||
|
}
|
||||||
|
junction_tag_insert(tag_id, 0, new_schedule->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_Delete(json);
|
||||||
|
json = schedule_to_json(new_schedule);
|
||||||
|
|
||||||
|
char *json_str = cJSON_Print(json);
|
||||||
|
if (json_str == NULL)
|
||||||
|
{
|
||||||
|
LOG_ERROR("failed to print schedule json\n");
|
||||||
|
mg_send_head(c, 201, 2, "Content-Type: application/json");
|
||||||
|
mg_printf(c, "{}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mg_send_head(c, 201, strlen(json_str), "Content-Type: application/json");
|
||||||
|
mg_printf(c, "%s", json_str);
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
cJSON_Delete(json);
|
||||||
|
schedule_free(new_schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_schedules_GET(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm)
|
||||||
|
{
|
||||||
|
(void)args;
|
||||||
|
(void)hm;
|
||||||
|
schedule_t** all_schedules = schedule_get_all();
|
||||||
|
|
||||||
|
cJSON *json = cJSON_CreateArray();
|
||||||
|
|
||||||
|
for(int i = 0; all_schedules[i] != NULL; ++i)
|
||||||
|
{
|
||||||
|
cJSON *json_schedule = schedule_to_json(all_schedules[i]);
|
||||||
|
|
||||||
|
cJSON_AddItemToArray(json, json_schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *json_str = cJSON_Print(json);
|
||||||
|
if (json_str == NULL)
|
||||||
|
{
|
||||||
|
LOG_ERROR("failed to print schedules json\n");
|
||||||
|
mg_send_head(c, 500, 2, "Content-Type: application/json");
|
||||||
|
mg_printf(c, "[]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mg_send_head(c, 200, strlen(json_str), "Content-Type: application/json");
|
||||||
|
mg_printf(c, "%s", json_str);
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
cJSON_Delete(json);
|
||||||
|
schedule_free_list(all_schedules);
|
||||||
|
}
|
54
endpoints/api_v1_schedules_STR.c
Normal file
54
endpoints/api_v1_schedules_STR.c
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include <cJSON.h>
|
||||||
|
#include <endpoints/api_v1_schedules.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <models/junction_tag.h>
|
||||||
|
#include <models/schedule.h>
|
||||||
|
#include <models/tag.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_schedules_STR_GET(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm)
|
||||||
|
{
|
||||||
|
(void)args;
|
||||||
|
(void)hm;
|
||||||
|
|
||||||
|
uuid_t target_uid;
|
||||||
|
if(schedule_uid_parse(args[0].value.v_str, target_uid))
|
||||||
|
{
|
||||||
|
LOG_ERROR("failed to unparse uid\n");
|
||||||
|
mg_send_head(c, 400, 2, "Content-Type: application/json");
|
||||||
|
mg_printf(c, "{}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char debug_str[40];
|
||||||
|
uuid_unparse(target_uid, debug_str);
|
||||||
|
LOG_DEBUG("uid: %s\n", debug_str);
|
||||||
|
|
||||||
|
schedule_t** all_schedules = schedule_get_all();
|
||||||
|
|
||||||
|
cJSON *json = cJSON_CreateArray();
|
||||||
|
|
||||||
|
for(int i = 0; all_schedules[i] != NULL; ++i)
|
||||||
|
{
|
||||||
|
cJSON *json_schedule = schedule_to_json(all_schedules[i]);
|
||||||
|
|
||||||
|
cJSON_AddItemToArray(json, json_schedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *json_str = cJSON_Print(json);
|
||||||
|
if (json_str == NULL)
|
||||||
|
{
|
||||||
|
LOG_ERROR("failed to print schedules json\n");
|
||||||
|
mg_send_head(c, 500, 2, "Content-Type: application/json");
|
||||||
|
mg_printf(c, "[]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mg_send_head(c, 200, strlen(json_str), "Content-Type: application/json");
|
||||||
|
mg_printf(c, "%s", json_str);
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
cJSON_Delete(json);
|
||||||
|
schedule_free_list(all_schedules);
|
||||||
|
}
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
#include <models/relay_dbo.h>
|
|
||||||
#include "controllers_valid_json.h"
|
|
||||||
|
|
||||||
using namespace drogon;
|
|
||||||
using namespace filters::controllers;
|
|
||||||
|
|
||||||
void valid_json::doFilter(const HttpRequestPtr &req,
|
|
||||||
FilterCallback &&fcb,
|
|
||||||
FilterChainCallback &&fccb)
|
|
||||||
{
|
|
||||||
if(req->getMethod() == Options)
|
|
||||||
{
|
|
||||||
fccb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value body = *req->jsonObject();
|
|
||||||
|
|
||||||
bool is_valid = true;
|
|
||||||
|
|
||||||
is_valid &= body["name"].type() == Json::ValueType::stringValue;
|
|
||||||
is_valid &= body["ip"].type() == Json::ValueType::stringValue;
|
|
||||||
|
|
||||||
if(is_valid)
|
|
||||||
{
|
|
||||||
//Passed
|
|
||||||
fccb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//Check failed
|
|
||||||
auto res = drogon::HttpResponse::newHttpResponse();
|
|
||||||
res->setStatusCode(k400BadRequest);
|
|
||||||
fcb(res);
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <drogon/HttpFilter.h>
|
|
||||||
using namespace drogon;
|
|
||||||
namespace filters::controllers
|
|
||||||
{
|
|
||||||
|
|
||||||
class valid_json : public HttpFilter<valid_json>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
valid_json() = default;
|
|
||||||
void doFilter(const HttpRequestPtr &req,
|
|
||||||
FilterCallback &&fcb,
|
|
||||||
FilterChainCallback &&fccb) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
#include "json_required.h"
|
|
||||||
|
|
||||||
using namespace drogon;
|
|
||||||
using namespace filters;
|
|
||||||
|
|
||||||
void json_required::doFilter(const HttpRequestPtr &req,
|
|
||||||
FilterCallback &&fcb,
|
|
||||||
FilterChainCallback &&fccb)
|
|
||||||
{
|
|
||||||
if(req->getMethod() == Options)
|
|
||||||
{
|
|
||||||
fccb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO remove this workaround
|
|
||||||
|
|
||||||
HttpMethod original_method = req->getMethod();
|
|
||||||
req->setMethod(Post);
|
|
||||||
|
|
||||||
if(req->jsonObject())
|
|
||||||
{
|
|
||||||
req->setMethod(original_method);
|
|
||||||
fccb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto res = drogon::HttpResponse::newHttpResponse();
|
|
||||||
res->setStatusCode(k400BadRequest);
|
|
||||||
fcb(res);
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <drogon/HttpFilter.h>
|
|
||||||
using namespace drogon;
|
|
||||||
namespace filters
|
|
||||||
{
|
|
||||||
class json_required : public HttpFilter<json_required>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
json_required() = default;
|
|
||||||
void
|
|
||||||
doFilter(const HttpRequestPtr &req, FilterCallback &&fcb, FilterChainCallback &&fccb) override;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
#include <models/schedule_dbo.h>
|
|
||||||
#include "relays_valid_json.h"
|
|
||||||
|
|
||||||
using namespace drogon;
|
|
||||||
using namespace filters::relays;
|
|
||||||
|
|
||||||
void valid_json::doFilter(const HttpRequestPtr &req,
|
|
||||||
FilterCallback &&fcb,
|
|
||||||
FilterChainCallback &&fccb)
|
|
||||||
{
|
|
||||||
if(req->getMethod() == Options)
|
|
||||||
{
|
|
||||||
fccb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value body = *req->jsonObject();
|
|
||||||
|
|
||||||
bool is_valid = true;
|
|
||||||
|
|
||||||
is_valid &= body["name"].type() == Json::ValueType::stringValue;
|
|
||||||
is_valid &= body["active_schedule"].type() == Json::ValueType::stringValue;
|
|
||||||
|
|
||||||
if(is_valid)
|
|
||||||
{
|
|
||||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("id", body["active_schedule"].asCString(), (intptr_t)&sqlite3_bind_text);
|
|
||||||
bool schedule_found = schedules[0] != nullptr;
|
|
||||||
schedule_dbo::free_list(schedules);
|
|
||||||
if(schedule_found)
|
|
||||||
{
|
|
||||||
//Passed
|
|
||||||
fccb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Check failed
|
|
||||||
auto res = drogon::HttpResponse::newHttpResponse();
|
|
||||||
res->setStatusCode(k400BadRequest);
|
|
||||||
fcb(res);
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <drogon/HttpFilter.h>
|
|
||||||
using namespace drogon;
|
|
||||||
namespace filters::relays
|
|
||||||
{
|
|
||||||
|
|
||||||
class valid_json : public HttpFilter<valid_json>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
valid_json() = default;
|
|
||||||
void doFilter(const HttpRequestPtr &req,
|
|
||||||
FilterCallback &&fcb,
|
|
||||||
FilterChainCallback &&fccb) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
#include "schedules_valid_json.h"
|
|
||||||
|
|
||||||
using namespace drogon;
|
|
||||||
using namespace filters::schedules;
|
|
||||||
|
|
||||||
void valid_json::doFilter(const HttpRequestPtr &req,
|
|
||||||
FilterCallback &&fcb,
|
|
||||||
FilterChainCallback &&fccb)
|
|
||||||
{
|
|
||||||
if(req->getMethod() == Options)
|
|
||||||
{
|
|
||||||
fccb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value body = *req->jsonObject();
|
|
||||||
|
|
||||||
bool is_valid = true;
|
|
||||||
|
|
||||||
is_valid &= body["name"].type() == Json::ValueType::stringValue;
|
|
||||||
is_valid &= body["periods"].type() == Json::ValueType::arrayValue;
|
|
||||||
|
|
||||||
if(is_valid)
|
|
||||||
{
|
|
||||||
//Passed
|
|
||||||
fccb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//Check failed
|
|
||||||
auto res = drogon::HttpResponse::newHttpResponse();
|
|
||||||
res->setStatusCode(k400BadRequest);
|
|
||||||
fcb(res);
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <drogon/HttpFilter.h>
|
|
||||||
using namespace drogon;
|
|
||||||
namespace filters::schedules
|
|
||||||
{
|
|
||||||
|
|
||||||
class valid_json : public HttpFilter<valid_json>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
valid_json() = default;
|
|
||||||
void doFilter(const HttpRequestPtr &req,
|
|
||||||
FilterCallback &&fcb,
|
|
||||||
FilterChainCallback &&fccb) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
#include <sqlite3.h>
|
|
||||||
#include "globals.h"
|
|
||||||
|
|
||||||
namespace globals
|
|
||||||
{
|
|
||||||
sqlite3 *db;
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
#ifndef EMGAUWA_CORE_GLOBALS_H
|
|
||||||
#define EMGAUWA_CORE_GLOBALS_H
|
|
||||||
|
|
||||||
namespace globals
|
|
||||||
{
|
|
||||||
extern sqlite3 *db;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //EMGAUWA_CORE_GLOBALS_H
|
|
28
handlers/connection.c
Normal file
28
handlers/connection.c
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#include <mongoose.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <router.h>
|
||||||
|
#include <handlers.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
handler_connection(struct mg_connection *c, int ev, void *p)
|
||||||
|
{
|
||||||
|
if (ev == MG_EV_HTTP_REQUEST)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("new http request\n");
|
||||||
|
struct http_message *hm = (struct http_message *) p;
|
||||||
|
|
||||||
|
endpoint_t *endpoint = router_find_endpoint(hm->uri.p, hm->uri.len, &hm->method);
|
||||||
|
|
||||||
|
if(endpoint && endpoint->func)
|
||||||
|
{
|
||||||
|
endpoint->func(c, endpoint->args, p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mg_send_head(c, 501, 0, "Content-Type: text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
//mg_printf(c, "%.*s", (int)hm->message.len, hm->message.p);
|
||||||
|
//mg_printf(c, "%.*s", (int)hm->body.len, hm->body.p);
|
||||||
|
}
|
||||||
|
}
|
43
helpers.h
43
helpers.h
|
@ -1,43 +0,0 @@
|
||||||
#ifndef EMGAUWA_CORE_HELPERS_H
|
|
||||||
#define EMGAUWA_CORE_HELPERS_H
|
|
||||||
|
|
||||||
#include <json/value.h>
|
|
||||||
#include <models/period.h>
|
|
||||||
#include <models/period_list.h>
|
|
||||||
#include <sqlite3.h>
|
|
||||||
|
|
||||||
namespace helpers
|
|
||||||
{
|
|
||||||
int
|
|
||||||
bind_tcp_server(const char *addr, const char *port, int max_client_backlog);
|
|
||||||
|
|
||||||
int
|
|
||||||
get_server_port(int fd);
|
|
||||||
|
|
||||||
int
|
|
||||||
send_udp_broadcast(const char *addr, uint16_t port, const char* message);
|
|
||||||
|
|
||||||
period_list*
|
|
||||||
parse_periods(Json::Value periods_json);
|
|
||||||
|
|
||||||
typedef struct sql_filter_builder
|
|
||||||
{
|
|
||||||
sql_filter_builder(const char *col_name, const void *value, intptr_t bind_func, const char *logic);
|
|
||||||
|
|
||||||
const char *col_name;
|
|
||||||
const void *value;
|
|
||||||
intptr_t bind_func;
|
|
||||||
const char *logic;
|
|
||||||
} sql_filter_builder;
|
|
||||||
|
|
||||||
sqlite3_stmt*
|
|
||||||
create_sql_filtered_query(const char *sql, sql_filter_builder **filters);
|
|
||||||
|
|
||||||
int
|
|
||||||
open_tcp_connection(char* host, char* port);
|
|
||||||
|
|
||||||
int
|
|
||||||
migrate_sql();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //EMGAUWA_CORE_HELPERS_H
|
|
52
helpers/bind_server.c
Normal file
52
helpers/bind_server.c
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
LOG_ERROR("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)
|
||||||
|
{
|
||||||
|
LOG_ERROR("error binding socket: %s\n", strerror(errno));
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((status = listen(fd, max_client_backlog)) == -1)
|
||||||
|
{
|
||||||
|
LOG_ERROR("error setting up listener: %s\n", strerror(errno));
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(res);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
|
@ -1,43 +0,0 @@
|
||||||
#include <netdb.h>
|
|
||||||
#include <trantor/utils/Logger.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
helpers::bind_tcp_server(const char *addr, const char *port, int max_client_backlog)
|
|
||||||
{
|
|
||||||
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, &hints, &res)) != 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error getting address info: " << gai_strerror(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
||||||
|
|
||||||
if ((status = bind(fd, res->ai_addr, res->ai_addrlen)) == -1)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error binding socket. " << status;
|
|
||||||
freeaddrinfo(res);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((status = listen(fd, max_client_backlog)) == -1)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error setting up listener. " << status;
|
|
||||||
freeaddrinfo(res);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
freeaddrinfo(res);
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
39
helpers/connect_server.c
Normal file
39
helpers/connect_server.c
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#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
|
||||||
|
LOG_ERROR("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) {
|
||||||
|
LOG_ERROR("connect() failed\n");
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(res);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
|
@ -1,58 +0,0 @@
|
||||||
#include <helpers.h>
|
|
||||||
#include <drogon/drogon.h>
|
|
||||||
#include <globals.h>
|
|
||||||
|
|
||||||
sqlite3_stmt*
|
|
||||||
helpers::create_sql_filtered_query(const char *sql, sql_filter_builder **filters)
|
|
||||||
{
|
|
||||||
char *old_query = (char*)sql;
|
|
||||||
char *new_query;
|
|
||||||
|
|
||||||
sql_filter_builder *filter;
|
|
||||||
int filter_count = 0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
filter = filters[filter_count];
|
|
||||||
|
|
||||||
filter_count++;
|
|
||||||
asprintf(&new_query, " %s %s=?%d %s", old_query, filter->col_name, filter_count, filter->logic);
|
|
||||||
|
|
||||||
if(old_query != sql)
|
|
||||||
{
|
|
||||||
free(old_query);
|
|
||||||
}
|
|
||||||
old_query = new_query;
|
|
||||||
} while(filter->logic[0] != ';');
|
|
||||||
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, new_query, -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
free(new_query);
|
|
||||||
|
|
||||||
for(int i = 0; i < filter_count; i++)
|
|
||||||
{
|
|
||||||
filter = filters[i];
|
|
||||||
|
|
||||||
if(filter->bind_func == (intptr_t)&sqlite3_bind_int)
|
|
||||||
{
|
|
||||||
sqlite3_bind_int(stmt, i + 1, (int)(intptr_t)filter->value);
|
|
||||||
}
|
|
||||||
if(filter->bind_func == (intptr_t)&sqlite3_bind_text)
|
|
||||||
{
|
|
||||||
sqlite3_bind_text(stmt, i + 1, (char*)filter->value, -1, SQLITE_STATIC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stmt;
|
|
||||||
}
|
|
||||||
|
|
||||||
helpers::sql_filter_builder::sql_filter_builder(const char *col_name, const void *value, intptr_t bind_func,
|
|
||||||
const char *logic)
|
|
||||||
{
|
|
||||||
this->col_name = col_name;
|
|
||||||
this->value = value;
|
|
||||||
this->bind_func = bind_func;
|
|
||||||
this->logic = logic;
|
|
||||||
}
|
|
12
helpers/get_day_of_week.c
Normal file
12
helpers/get_day_of_week.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <helpers.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
helper_get_weekday(const time_t timestamp_now)
|
||||||
|
{
|
||||||
|
struct tm *now = localtime(×tamp_now);
|
||||||
|
int wday_sun_sat = now->tm_wday;
|
||||||
|
int wday_mon_sun = (wday_sun_sat + 6) % 7;
|
||||||
|
return wday_mon_sun;
|
||||||
|
}
|
24
helpers/get_port.c
Normal file
24
helpers/get_port.c
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <helpers.h>
|
||||||
|
#include <logger.h>
|
||||||
|
|
||||||
|
uint16_t
|
||||||
|
helper_get_port(int sock)
|
||||||
|
{
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
socklen_t len = sizeof(sin);
|
||||||
|
if (getsockname(sock, (struct sockaddr *)&sin, &len) == -1)
|
||||||
|
{
|
||||||
|
LOG_ERROR("could not get socket name for port: %s\n", strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return ntohs(sin.sin_port);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,18 +0,0 @@
|
||||||
#include <netdb.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
|
|
||||||
int
|
|
||||||
helpers::get_server_port(int fd)
|
|
||||||
{
|
|
||||||
if(fd == -1)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
struct sockaddr_in sin{};
|
|
||||||
socklen_t addr_len = sizeof(sin);
|
|
||||||
if(getsockname(fd, (struct sockaddr *)&sin, &addr_len) == 0)
|
|
||||||
{
|
|
||||||
return ntohs(sin.sin_port);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
#include <helpers.h>
|
|
||||||
#include <globals.h>
|
|
||||||
#include <drogon/trantor/trantor/utils/Logger.h>
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#include <sql/migration_0.h>
|
|
||||||
|
|
||||||
int
|
|
||||||
helpers::migrate_sql()
|
|
||||||
{
|
|
||||||
uint16_t version_num = 0;
|
|
||||||
int s, rc;
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
sqlite3_prepare_v2(globals::db, "SELECT version_num FROM meta LIMIT 1;", -1, &stmt, nullptr);
|
|
||||||
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;
|
|
||||||
char* sql;
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
switch(version_num)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
rc = sqlite3_exec(globals::db, (const char *)sql_migration_0_sql, nullptr, nullptr, &err_msg);
|
|
||||||
if(rc != 0)
|
|
||||||
{
|
|
||||||
LOG_FATAL << "Couldn't migrate LEVEL 0 (" << err_msg << ")";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
new_version_num = 1;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(version_num == 0)
|
|
||||||
{
|
|
||||||
sqlite3_prepare_v2(globals::db, "INSERT INTO meta (version_num) VALUES (?1);", -1, &stmt, nullptr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sqlite3_prepare_v2(globals::db, "UPDATE meta SET version_num=?1;", -1, &stmt, nullptr);
|
|
||||||
}
|
|
||||||
sqlite3_bind_int(stmt, 1, new_version_num);
|
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
|
||||||
if (rc != SQLITE_DONE)
|
|
||||||
{
|
|
||||||
LOG_FATAL << "Couldn't write new Schema Version";
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc == SQLITE_DONE;
|
|
||||||
}
|
|
57
helpers/open_discovery_socket.c
Normal file
57
helpers/open_discovery_socket.c
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <logger.h>
|
||||||
|
#include <helpers.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
helper_open_discovery_socket(uint16_t discovery_port)
|
||||||
|
{
|
||||||
|
struct addrinfo hints, *res;
|
||||||
|
int fd, status;
|
||||||
|
|
||||||
|
memset(&hints, 0, sizeof hints);
|
||||||
|
hints.ai_family = AF_INET; // use ipv4
|
||||||
|
hints.ai_socktype = SOCK_DGRAM; //set socket flag
|
||||||
|
hints.ai_flags = AI_PASSIVE; // get my IP
|
||||||
|
|
||||||
|
char discovery_port_str[6];
|
||||||
|
sprintf(discovery_port_str, "%u", discovery_port);
|
||||||
|
|
||||||
|
//get connection info for our computer
|
||||||
|
if ((status = getaddrinfo(NULL, discovery_port_str, &hints, &res)) != 0)
|
||||||
|
{
|
||||||
|
LOG_FATAL("getaddrinfo: %s\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)
|
||||||
|
{
|
||||||
|
LOG_FATAL("setsockopt: %s\n", strerror(errno));
|
||||||
|
freeaddrinfo(res);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(fd, res->ai_addr, res->ai_addrlen) == -1)
|
||||||
|
{
|
||||||
|
LOG_FATAL("bind: %s\n", strerror(errno));
|
||||||
|
freeaddrinfo(res);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(res);
|
||||||
|
|
||||||
|
LOG_INFO("opened discovery socket on port %u\n", discovery_port);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
|
@ -1,33 +0,0 @@
|
||||||
#include <helpers.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <trantor/utils/Logger.h>
|
|
||||||
|
|
||||||
int
|
|
||||||
helpers::open_tcp_connection(char *host, char *port)
|
|
||||||
{
|
|
||||||
int s, status;
|
|
||||||
struct addrinfo hints{}, *res;
|
|
||||||
memset(&hints, 0, sizeof hints);
|
|
||||||
hints.ai_family = AF_INET;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
|
|
||||||
if ((status = getaddrinfo(host, port, &hints, &res)) != 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error getting address info: " << gai_strerror(status);
|
|
||||||
freeaddrinfo(res);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //creating Socket
|
|
||||||
|
|
||||||
if ((status = connect(s, res->ai_addr, res->ai_addrlen)) != 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error opening connection " << status;
|
|
||||||
freeaddrinfo(res);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
freeaddrinfo(res);
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
62
helpers/parse_cli.c
Normal file
62
helpers/parse_cli.c
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <argparse.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <helpers.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)
|
||||||
|
{
|
||||||
|
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_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(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;
|
||||||
|
}
|
||||||
|
LOG_FATAL("bad action '%s' given ('start', 'test')\n", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_FATAL("no action given ('start', 'test')\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
|
@ -1,68 +0,0 @@
|
||||||
#include <helpers.h>
|
|
||||||
#include <drogon/drogon.h>
|
|
||||||
#include <models/period_list.h>
|
|
||||||
|
|
||||||
static int
|
|
||||||
parse_HHMM(const char *begin, uint16_t *h, uint16_t *m)
|
|
||||||
{
|
|
||||||
uint16_t tmp_h, tmp_m;
|
|
||||||
char *check = nullptr;
|
|
||||||
|
|
||||||
tmp_h = (uint16_t)strtol(begin, &check, 10);
|
|
||||||
if(begin == check)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
begin = check + 1;
|
|
||||||
tmp_m = (uint16_t)strtol(begin, &check, 10);
|
|
||||||
if(begin == check)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*h = tmp_h;
|
|
||||||
*m = tmp_m;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
period_list*
|
|
||||||
helpers::parse_periods(Json::Value periods_json)
|
|
||||||
{
|
|
||||||
auto result = new period_list();
|
|
||||||
|
|
||||||
for (Json::Value::ArrayIndex i = 0; i != periods_json.size(); i++)
|
|
||||||
{
|
|
||||||
Json::Value p = periods_json[i];
|
|
||||||
if(!(p.isMember("start") && p.isMember("end")))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const char *start_str = p["start"].asCString();
|
|
||||||
const char *end_str = p["end"].asCString();
|
|
||||||
|
|
||||||
uint16_t h, m, start, end;
|
|
||||||
|
|
||||||
if(parse_HHMM(start_str, &h, &m))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
start = (uint16_t)((h * 60) + m);
|
|
||||||
|
|
||||||
if(parse_HHMM(end_str, &h, &m))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
end = (uint16_t)((h * 60) + m);
|
|
||||||
|
|
||||||
if(start < 0 || start > 24 * 60 || end < 0 || end > 24 * 60)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
result->add_period(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <trantor/utils/Logger.h>
|
|
||||||
#include "config.h"
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
|
|
||||||
int
|
|
||||||
helpers::send_udp_broadcast(const char *addr, uint16_t port, const char* message)
|
|
||||||
{
|
|
||||||
struct sockaddr_in their_addr{};
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error creating socket";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int broadcast = 1;
|
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) < 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error setting broadcast";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(&their_addr, 0, sizeof(their_addr));
|
|
||||||
their_addr.sin_family = AF_INET;
|
|
||||||
their_addr.sin_port = htons(port);
|
|
||||||
their_addr.sin_addr.s_addr = inet_addr(addr);
|
|
||||||
|
|
||||||
if(sendto(fd, message, strlen(message), 0, (struct sockaddr *)&their_addr, sizeof(their_addr)) < 0)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error sending broadcast " << errno << " " << strerror(errno);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
18
include/colors.h
Normal file
18
include/colors.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef CORE_COLORS_H
|
||||||
|
#define CORE_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 //CORE_COLORS_H
|
24
include/config.h
Normal file
24
include/config.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef CORE_CONFIG_H
|
||||||
|
#define CORE_CONFIG_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <confini.h>
|
||||||
|
#include <enums.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char *file;
|
||||||
|
char *database;
|
||||||
|
log_level_t log_level;
|
||||||
|
run_type_t run_type;
|
||||||
|
char server_port[6];
|
||||||
|
uint16_t discovery_port;
|
||||||
|
} config_t;
|
||||||
|
|
||||||
|
extern config_t global_config;
|
||||||
|
|
||||||
|
int
|
||||||
|
config_load(IniDispatch *disp, void *config_void);
|
||||||
|
|
||||||
|
#endif /* CORE_CONFIG_H */
|
31
include/constants.h
Normal file
31
include/constants.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef CORE_CONTANTS_H
|
||||||
|
#define CORE_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 /* CORE_CONTANTS_H */
|
11
include/database.h
Normal file
11
include/database.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef CORE_DATABASE_H
|
||||||
|
#define CORE_DATABASE_H
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
extern sqlite3 *global_database;
|
||||||
|
|
||||||
|
int
|
||||||
|
database_migrate();
|
||||||
|
|
||||||
|
#endif /* CORE_DATABASE_H */
|
13
include/drivers.h
Normal file
13
include/drivers.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef CORE_DRIVERS_H
|
||||||
|
#define CORE_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 /* CORE_DRIVERS_H */
|
15
include/endpoints/api_v1_schedules.h
Normal file
15
include/endpoints/api_v1_schedules.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef CORE_ENDPOINTS_API_V1_SCHEDULES_H
|
||||||
|
#define CORE_ENDPOINTS_API_V1_SCHEDULES_H
|
||||||
|
|
||||||
|
#include <router.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_schedules_POST(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm);
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_schedules_GET(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm);
|
||||||
|
|
||||||
|
void
|
||||||
|
api_v1_schedules_STR_GET(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm);
|
||||||
|
|
||||||
|
#endif /* CORE_ENDPOINTS_API_V1_SCHEDULES_H */
|
64
include/enums.h
Normal file
64
include/enums.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
#ifndef CORE_ENUMS_H
|
||||||
|
#define CORE_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
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
} control_mapping_t;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
COMMAND_CODE_GET_TIME = 1,
|
||||||
|
COMMAND_CODE_GET_ID = 2,
|
||||||
|
COMMAND_CODE_SET_NAME = 100,
|
||||||
|
COMMAND_CODE_GET_NAME = 101,
|
||||||
|
COMMAND_CODE_SET_SCHEDULE = 102,
|
||||||
|
COMMAND_CODE_GET_SCHEDULE = 103,
|
||||||
|
COMMAND_CODE_SET_RELAY_NAME = 104,
|
||||||
|
COMMAND_CODE_GET_RELAY_NAME = 105,
|
||||||
|
} command_code_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;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
LOG_LEVEL_TRACE = 5,
|
||||||
|
LOG_LEVEL_DEBUG = 4,
|
||||||
|
LOG_LEVEL_INFO = 3,
|
||||||
|
LOG_LEVEL_WARN = 2,
|
||||||
|
LOG_LEVEL_ERROR = 1,
|
||||||
|
LOG_LEVEL_FATAL = 0,
|
||||||
|
} log_level_t;
|
||||||
|
|
||||||
|
#endif /* CORE_ENUMS_H */
|
7
include/handlers.h
Normal file
7
include/handlers.h
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef CORE_HANDLERS_H
|
||||||
|
#define CORE_HANDLERS_H
|
||||||
|
|
||||||
|
void
|
||||||
|
handler_connection(struct mg_connection *c, int ev, void *p);
|
||||||
|
|
||||||
|
#endif /* CORE_HANDLERS_H */
|
34
include/helpers.h
Normal file
34
include/helpers.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef CORE_HELPERS_H
|
||||||
|
#define CORE_HELPERS_H
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <confini.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 time_t timestamp_now);
|
||||||
|
|
||||||
|
#endif /* CORE_HELPERS_H */
|
21
include/logger.h
Normal file
21
include/logger.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef CORE_LOGGER_H
|
||||||
|
#define CORE_LOGGER_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <colors.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <macros.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
logger_log(FILE *stream, log_level_t level, const char *filename, int line, const char *func, const char *msg, ...);
|
||||||
|
|
||||||
|
#define LOG_TRACE(...) logger_log(stdout, LOG_LEVEL_TRACE, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
|
#define LOG_DEBUG(...) logger_log(stdout, LOG_LEVEL_DEBUG, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
|
#define LOG_INFO(...) logger_log(stdout, LOG_LEVEL_INFO , __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
|
#define LOG_WARN(...) logger_log(stdout, LOG_LEVEL_WARN , __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
|
#define LOG_ERROR(...) logger_log(stderr, LOG_LEVEL_ERROR, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
|
#define LOG_FATAL(...) logger_log(stderr, LOG_LEVEL_FATAL, __FILENAME__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#endif //CORE_LOGGER_H
|
13
include/macros.h
Normal file
13
include/macros.h
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#ifndef CORE_MACROS_H
|
||||||
|
#define CORE_MACROS_H
|
||||||
|
|
||||||
|
#include <colors.h>
|
||||||
|
#include <logger.h>
|
||||||
|
|
||||||
|
#ifndef SOURCE_PATH_SIZE
|
||||||
|
#define SOURCE_PATH_SIZE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)
|
||||||
|
|
||||||
|
#endif //CORE_MACROS_H
|
45
include/models/controller.h
Normal file
45
include/models/controller.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef CORE_MODELS_CONTROLLER_H
|
||||||
|
#define CORE_MODELS_CONTROLLER_H
|
||||||
|
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
#include <helpers.h>
|
||||||
|
#include <models/relay.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uuid_t id;
|
||||||
|
char name[128];
|
||||||
|
char ip[17];
|
||||||
|
int active;
|
||||||
|
int port;
|
||||||
|
int relay_count;
|
||||||
|
relay_t **relays;
|
||||||
|
} controller_t;
|
||||||
|
|
||||||
|
void
|
||||||
|
controller_free(controller_t* contoller);
|
||||||
|
|
||||||
|
int
|
||||||
|
controller_save(controller_t* contoller);
|
||||||
|
|
||||||
|
int
|
||||||
|
controller_remove(controller_t* contoller);
|
||||||
|
|
||||||
|
char*
|
||||||
|
controller_to_json(controller_t* contoller);
|
||||||
|
|
||||||
|
controller_t**
|
||||||
|
controller_get_by_simple(const char *key, const void *value, intptr_t bind_func, int bind_func_param);
|
||||||
|
|
||||||
|
controller_t**
|
||||||
|
controller_get_all();
|
||||||
|
|
||||||
|
int
|
||||||
|
controller_command(int command_code, char *payload, uint32_t payload_size);
|
||||||
|
|
||||||
|
void
|
||||||
|
controller_free_list(controller_t **controllers_list);
|
||||||
|
|
||||||
|
#endif /* CORE_MODELS_CONTROLLER_H */
|
19
include/models/junction_relay_schedule.h
Normal file
19
include/models/junction_relay_schedule.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef CORE_MODELS_JUNCTION_RELAY_SCHEDULE_H
|
||||||
|
#define CORE_MODELS_JUNCTION_RELAY_SCHEDULE_H
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_relay_schedule_get_schedule_id(uint8_t weekday, int relay_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_relay_schedule_insert(uint8_t weekday, int relay_id, int schedule_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_relay_schedule_remove(uint8_t weekday, int relay_id, int schedule_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_relay_schedule_remove_for_relay(int relay_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_relay_schedule_remove_for_schedule(int schedule_id);
|
||||||
|
|
||||||
|
#endif /* CORE_MODELS_JUNCTION_RELAY_SCHEDULE_H */
|
32
include/models/junction_tag.h
Normal file
32
include/models/junction_tag.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef CORE_MODELS_JUNCTION_TAG_H
|
||||||
|
#define CORE_MODELS_JUNCTION_TAG_H
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_tag_get_relays_for_tag_id(int tag_id);
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_tag_get_schedules_for_tag_id(int tag_id);
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_tag_get_tags_for_relay_id(int relay_id);
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_tag_get_tags_for_schedule_id(int schedule_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_tag_insert(int tag_id, int relay_id, int schedule_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_tag_remove(int tag_id, int relay_id, int schedule_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_tag_remove_for_tag(int tag_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_tag_remove_for_relay(int relay_id);
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_tag_remove_for_schedule(int schedule_id);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CORE_MODELS_JUNCTION_TAG_H */
|
21
include/models/period.h
Normal file
21
include/models/period.h
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef CORE_PERIOD_H
|
||||||
|
#define CORE_PERIOD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t start;
|
||||||
|
uint16_t end;
|
||||||
|
} period_t;
|
||||||
|
|
||||||
|
period_t*
|
||||||
|
period_create(uint16_t start, uint16_t end);
|
||||||
|
|
||||||
|
int
|
||||||
|
period_includes_time(period_t *period, uint16_t timestamp);
|
||||||
|
|
||||||
|
int
|
||||||
|
period_helper_parse_hhmm(const char *hhmm_str, uint16_t *hhmm);
|
||||||
|
|
||||||
|
#endif /* CORE_PERIOD_H */
|
49
include/models/relay.h
Normal file
49
include/models/relay.h
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef CORE_RELAY_H
|
||||||
|
#define CORE_RELAY_H
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
|
||||||
|
#include <helpers.h>
|
||||||
|
#include <database.h>
|
||||||
|
#include <models/schedule.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
char name[128];
|
||||||
|
int number;
|
||||||
|
uuid_t controller_id;
|
||||||
|
int active_schedule_id;
|
||||||
|
schedule_t *active_schedule;
|
||||||
|
schedule_t *schedules[7];
|
||||||
|
} relay_t;
|
||||||
|
|
||||||
|
bool
|
||||||
|
relay_save();
|
||||||
|
|
||||||
|
bool
|
||||||
|
relay_remove();
|
||||||
|
|
||||||
|
char*
|
||||||
|
relay_to_json();
|
||||||
|
|
||||||
|
void
|
||||||
|
relay_free_list(relay_t **relays_list);
|
||||||
|
|
||||||
|
relay_t**
|
||||||
|
relay_get_by_simple(const char *key, const void *value, intptr_t bind_func, int bind_func_param);
|
||||||
|
|
||||||
|
relay_t*
|
||||||
|
relay_get_by_id(int id);
|
||||||
|
|
||||||
|
relay_t*
|
||||||
|
relay_get_relay_for_controller(uuid_t controller_id, int relay_num);
|
||||||
|
|
||||||
|
bool
|
||||||
|
relay_valid_num_is_for_controller(uuid_t controller_id, int relay_num);
|
||||||
|
|
||||||
|
relay_t**
|
||||||
|
relay_get_all();
|
||||||
|
|
||||||
|
#endif /* CORE_RELAY_H */
|
58
include/models/schedule.h
Normal file
58
include/models/schedule.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef CORE_SCHEDULE_H
|
||||||
|
#define CORE_SCHEDULE_H
|
||||||
|
|
||||||
|
#include <uuid/uuid.h>
|
||||||
|
|
||||||
|
#include <cJSON.h>
|
||||||
|
#include <constants.h>
|
||||||
|
#include <models/period.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
uuid_t uid;
|
||||||
|
char name[MAX_NAME_LENGTH + 1];
|
||||||
|
uint16_t periods_count;
|
||||||
|
period_t *periods;
|
||||||
|
} schedule_t;
|
||||||
|
|
||||||
|
int
|
||||||
|
schedule_save(schedule_t *schedule);
|
||||||
|
|
||||||
|
int
|
||||||
|
schedule_remove(schedule_t *schedule);
|
||||||
|
|
||||||
|
void
|
||||||
|
schedule_free(schedule_t *schedule);
|
||||||
|
|
||||||
|
void
|
||||||
|
schedule_free_list(schedule_t **schedule);
|
||||||
|
|
||||||
|
cJSON*
|
||||||
|
schedule_to_json(schedule_t *schedule);
|
||||||
|
|
||||||
|
void
|
||||||
|
schedule_free_list(schedule_t **schedules_list);
|
||||||
|
|
||||||
|
uint16_t*
|
||||||
|
schedule_periods_to_blob(schedule_t *schedule);
|
||||||
|
|
||||||
|
schedule_t**
|
||||||
|
schedule_get_by_simple(const char *key, const void *value, intptr_t bind_func, int bind_func_param);
|
||||||
|
|
||||||
|
schedule_t*
|
||||||
|
schedule_get_by_id_or_off(int id);
|
||||||
|
|
||||||
|
schedule_t*
|
||||||
|
schedule_get_by_id(int id);
|
||||||
|
|
||||||
|
schedule_t**
|
||||||
|
schedule_get_all();
|
||||||
|
|
||||||
|
int
|
||||||
|
schedule_uid_parse(const char *uid_str, uuid_t result);
|
||||||
|
|
||||||
|
void
|
||||||
|
schedule_uid_unparse(const uuid_t uid, char *result);
|
||||||
|
|
||||||
|
#endif /* CORE_SCHEDULE_H */
|
17
include/models/tag.h
Normal file
17
include/models/tag.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef CORE_MODELS_TAG_H
|
||||||
|
#define CORE_MODELS_TAG_H
|
||||||
|
|
||||||
|
int
|
||||||
|
tag_save(int id, const char *tag);
|
||||||
|
|
||||||
|
int
|
||||||
|
tag_remove(int id);
|
||||||
|
|
||||||
|
char*
|
||||||
|
tag_get_tag(int id);
|
||||||
|
|
||||||
|
int
|
||||||
|
tag_get_id(const char* tag);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* CORE_MODELS_TAG_H */
|
52
include/router.h
Normal file
52
include/router.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef CORE_ROUTER_H
|
||||||
|
#define CORE_ROUTER_H
|
||||||
|
|
||||||
|
#include <mongoose.h>
|
||||||
|
|
||||||
|
#define ENDPOINTS_MAX_COUNT 16
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
ENDPOINT_ARG_TYPE_INT,
|
||||||
|
ENDPOINT_ARG_TYPE_STR
|
||||||
|
} endpoint_arg_type_e;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
endpoint_arg_type_e type;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int v_int;
|
||||||
|
char *v_str;
|
||||||
|
} value;
|
||||||
|
} endpoint_args_t;
|
||||||
|
|
||||||
|
typedef void (*endpoint_func_f)(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
char **route;
|
||||||
|
char *route_keeper;
|
||||||
|
int methods;
|
||||||
|
endpoint_func_f func;
|
||||||
|
|
||||||
|
int args_count;
|
||||||
|
endpoint_args_t *args;
|
||||||
|
|
||||||
|
int possible_route;
|
||||||
|
int args_found;
|
||||||
|
} endpoint_t;
|
||||||
|
|
||||||
|
void
|
||||||
|
router_init();
|
||||||
|
|
||||||
|
void
|
||||||
|
router_register_endpoint(const char *route, int methods, endpoint_func_f func);
|
||||||
|
|
||||||
|
endpoint_t*
|
||||||
|
router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method);
|
||||||
|
|
||||||
|
void
|
||||||
|
router_free();
|
||||||
|
|
||||||
|
#endif /* CORE_ROUTER_H */
|
59
logger.c
Normal file
59
logger.c
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <logger.h>
|
||||||
|
|
||||||
|
#define COLOR_TRACE COLOR_GREEN
|
||||||
|
#define COLOR_DEBUG COLOR_BLUE
|
||||||
|
#define COLOR_INFO COLOR_CYAN
|
||||||
|
#define COLOR_WARN COLOR_YELLOW
|
||||||
|
#define COLOR_ERROR COLOR_RED
|
||||||
|
#define COLOR_FATAL COLOR_MAGENTA
|
||||||
|
|
||||||
|
void
|
||||||
|
logger_log(FILE *stream, log_level_t level, const char *filename, int line, const char *func, const char *msg, ...)
|
||||||
|
{
|
||||||
|
if(global_config.log_level < level)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(level)
|
||||||
|
{
|
||||||
|
case LOG_LEVEL_TRACE:
|
||||||
|
fprintf(stream, COLOR_TRACE "[TRACE] ");
|
||||||
|
break;
|
||||||
|
case LOG_LEVEL_DEBUG:
|
||||||
|
fprintf(stream, COLOR_DEBUG "[DEBUG] ");
|
||||||
|
break;
|
||||||
|
case LOG_LEVEL_INFO:
|
||||||
|
fprintf(stream, COLOR_INFO "[INFO ] ");
|
||||||
|
break;
|
||||||
|
case LOG_LEVEL_WARN:
|
||||||
|
fprintf(stream, COLOR_WARN "[WARN ] ");
|
||||||
|
break;
|
||||||
|
case LOG_LEVEL_ERROR:
|
||||||
|
fprintf(stream, COLOR_ERROR "[ERROR] ");
|
||||||
|
break;
|
||||||
|
case LOG_LEVEL_FATAL:
|
||||||
|
fprintf(stream, COLOR_FATAL "[FATAL] ");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stream, COLOR_NONE "[LOG ] ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char timestamp_str[32];
|
||||||
|
time_t rawtime;
|
||||||
|
time(&rawtime);
|
||||||
|
strftime(timestamp_str, 32, "%Y-%m-%d %H:%M:%S", localtime(&rawtime));
|
||||||
|
|
||||||
|
fprintf(stream, "%s %s:%d:%s " COLOR_NONE, timestamp_str, filename, line, func);
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, msg);
|
||||||
|
vfprintf(stream, msg, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
107
main.c
Normal file
107
main.c
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <mongoose.h>
|
||||||
|
#include <router.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <database.h>
|
||||||
|
#include <handlers.h>
|
||||||
|
#include <enums.h>
|
||||||
|
#include <helpers.h>
|
||||||
|
#include <confini.h>
|
||||||
|
#include <models/controller.h>
|
||||||
|
|
||||||
|
static struct mg_mgr mgr;
|
||||||
|
|
||||||
|
static void
|
||||||
|
terminate(int signum)
|
||||||
|
{
|
||||||
|
LOG_INFO("terminating controller (%d)\n", signum);
|
||||||
|
|
||||||
|
mg_mgr_free(&mgr);
|
||||||
|
|
||||||
|
sqlite3_close(global_database);
|
||||||
|
|
||||||
|
free(global_config.database);
|
||||||
|
|
||||||
|
router_free();
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
signal(SIGINT, terminate);
|
||||||
|
signal(SIGABRT, terminate);
|
||||||
|
signal(SIGTERM, terminate);
|
||||||
|
|
||||||
|
|
||||||
|
/******************** LOAD CONFIG ********************/
|
||||||
|
|
||||||
|
global_config.file = "core.ini";
|
||||||
|
global_config.log_level = LOG_LEVEL_INFO;
|
||||||
|
|
||||||
|
helper_parse_cli(argc, argv, &global_config);
|
||||||
|
|
||||||
|
FILE * const ini_file = fopen(global_config.file, "rb");
|
||||||
|
if(ini_file == NULL)
|
||||||
|
{
|
||||||
|
LOG_FATAL("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))
|
||||||
|
{
|
||||||
|
LOG_FATAL("unable to parse ini file\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(ini_file);
|
||||||
|
|
||||||
|
|
||||||
|
/******************** SETUP DATABASE ********************/
|
||||||
|
|
||||||
|
int rc = sqlite3_open(global_config.database, &global_database);
|
||||||
|
|
||||||
|
if(rc) {
|
||||||
|
LOG_FATAL("can't open database: %s\n", sqlite3_errmsg(global_database));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(database_migrate())
|
||||||
|
{
|
||||||
|
terminate(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************** INIT ROUTER ********************/
|
||||||
|
|
||||||
|
router_init();
|
||||||
|
|
||||||
|
|
||||||
|
/******************** START MAIN LOOP ********************/
|
||||||
|
|
||||||
|
struct mg_connection *c;
|
||||||
|
|
||||||
|
mg_mgr_init(&mgr, NULL);
|
||||||
|
c = mg_bind(&mgr, global_config.server_port, handler_connection);
|
||||||
|
mg_set_protocol_http_websocket(c);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
mg_mgr_poll(&mgr, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate(0);
|
||||||
|
}
|
68
main.cc
68
main.cc
|
@ -1,68 +0,0 @@
|
||||||
#include <drogon/drogon.h>
|
|
||||||
#include <sqlite3.h>
|
|
||||||
|
|
||||||
#include <models/controller_dbo.h>
|
|
||||||
#include <csignal>
|
|
||||||
|
|
||||||
#include "globals.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_cors_headers(const drogon::HttpRequestPtr &requestPtr, const drogon::HttpResponsePtr &responsePtr)
|
|
||||||
{
|
|
||||||
responsePtr->addHeader("Access-Control-Allow-Origin", "*");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
terminate(int signum)
|
|
||||||
{
|
|
||||||
LOG_INFO << "Terminating Server (" << signum << ")";
|
|
||||||
|
|
||||||
sqlite3_close(globals::db);
|
|
||||||
|
|
||||||
quick_exit(signum);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static void test()
|
|
||||||
{
|
|
||||||
LOG_DEBUG << "LOOP";
|
|
||||||
}*/
|
|
||||||
|
|
||||||
int
|
|
||||||
main()
|
|
||||||
{
|
|
||||||
signal(SIGINT, terminate);
|
|
||||||
signal(SIGABRT, terminate);
|
|
||||||
signal(SIGTERM, terminate);
|
|
||||||
signal(SIGKILL, terminate);
|
|
||||||
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* Open database */
|
|
||||||
rc = sqlite3_open("core.sqlite", &globals::db);
|
|
||||||
|
|
||||||
if( rc ) {
|
|
||||||
LOG_FATAL << "Can't open database: " << sqlite3_errmsg(globals::db);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!helpers::migrate_sql())
|
|
||||||
{
|
|
||||||
terminate(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Load config file
|
|
||||||
drogon::app().loadConfigFile("config.json");
|
|
||||||
|
|
||||||
drogon::app().registerPostHandlingAdvice(add_cors_headers);
|
|
||||||
drogon::app().instance().setCustom404Page(drogon::HttpResponse::newFileResponse("./static/index.html", "", drogon::CT_TEXT_HTML));
|
|
||||||
|
|
||||||
//drogon::app().getLoop()->runEvery(1, &test);
|
|
||||||
|
|
||||||
//Run HTTP framework,the method will block in the internal event loop
|
|
||||||
|
|
||||||
LOG_INFO << "Starting Emgauwa Core (" << config::version << ")";
|
|
||||||
|
|
||||||
drogon::app().run();
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,246 +0,0 @@
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <trantor/utils/Logger.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include "controller_dbo.h"
|
|
||||||
#include "globals.h"
|
|
||||||
|
|
||||||
controller_dbo::~controller_dbo()
|
|
||||||
{
|
|
||||||
if(this->relays)
|
|
||||||
{
|
|
||||||
relay_dbo::free_list(this->relays);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool controller_db_update_insert(controller_dbo *controller, sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
sqlite3_bind_text(stmt, 1, controller->id, -1, SQLITE_STATIC);
|
|
||||||
sqlite3_bind_text(stmt, 2, controller->name, -1, SQLITE_STATIC);
|
|
||||||
sqlite3_bind_text(stmt, 3, controller->ip, -1, SQLITE_STATIC);
|
|
||||||
sqlite3_bind_int(stmt, 4, controller->active);
|
|
||||||
sqlite3_bind_int(stmt, 5, controller->port);
|
|
||||||
sqlite3_bind_int(stmt, 6, controller->relay_count);
|
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
|
||||||
if (rc != SQLITE_DONE)
|
|
||||||
{
|
|
||||||
printf("ERROR inserting data: %s\n", sqlite3_errmsg(globals::db));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static controller_dbo*
|
|
||||||
controller_db_select_mapper(sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
auto *new_controller = new controller_dbo();
|
|
||||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
|
||||||
{
|
|
||||||
const char *name = sqlite3_column_name(stmt, i);
|
|
||||||
switch(name[0])
|
|
||||||
{
|
|
||||||
case 'a': // active
|
|
||||||
new_controller->active = (bool)sqlite3_column_int(stmt, i);
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
switch(name[1])
|
|
||||||
{
|
|
||||||
case 'd': // id
|
|
||||||
strncpy(new_controller->id, (const char*)sqlite3_column_text(stmt, i), 32);
|
|
||||||
break;
|
|
||||||
case 'p': // ip
|
|
||||||
strncpy(new_controller->ip, (const char*)sqlite3_column_text(stmt, i), 16);
|
|
||||||
break;
|
|
||||||
default: // ignore columns not implemented
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'n': // name
|
|
||||||
strncpy(new_controller->name, (const char*)sqlite3_column_text(stmt, i), 127);
|
|
||||||
break;
|
|
||||||
case 'p': // port
|
|
||||||
new_controller->port = sqlite3_column_int(stmt, i);
|
|
||||||
break;
|
|
||||||
case 'r': // relay_count
|
|
||||||
new_controller->relay_count = sqlite3_column_int(stmt, i);
|
|
||||||
break;
|
|
||||||
default: // ignore columns not implemented
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new_controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
static controller_dbo**
|
|
||||||
controller_db_select(sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
auto **all_controllers = (controller_dbo**)malloc(sizeof(controller_dbo*));
|
|
||||||
|
|
||||||
int row = 0;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s = sqlite3_step(stmt);
|
|
||||||
if (s == SQLITE_ROW)
|
|
||||||
{
|
|
||||||
controller_dbo *new_controller = controller_db_select_mapper(stmt);
|
|
||||||
new_controller->relays = relay_dbo::get_by_simple("controller_id", new_controller->id, (intptr_t)&sqlite3_bind_text);
|
|
||||||
row++;
|
|
||||||
|
|
||||||
all_controllers = (controller_dbo**)realloc(all_controllers, sizeof(controller_dbo*) * (row + 1));
|
|
||||||
all_controllers[row - 1] = new_controller;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (s == SQLITE_DONE)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error Selecting controllers from database";
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
all_controllers[row] = nullptr;
|
|
||||||
|
|
||||||
return all_controllers;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
controller_dbo::update()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "UPDATE controllers set name = ?2, ip = ?3, active = ?4, port = ?5, relay_count = ?6 WHERE id = ?1;", -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
return controller_db_update_insert(this, stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
controller_dbo::insert()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "INSERT INTO controllers(id, name, ip, active, port, relay_count) values (?1, ?2, ?3, ?4, ?5, ?6);", -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
return controller_db_update_insert(this, stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
controller_dbo::remove()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "DELETE FROM controllers WHERE id=?1;", -1, &stmt, nullptr);
|
|
||||||
sqlite3_bind_text(stmt, 1, this->id, -1, SQLITE_STATIC);
|
|
||||||
rc = sqlite3_step(stmt);
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
return rc == SQLITE_DONE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
controller_dbo::to_json()
|
|
||||||
{
|
|
||||||
Json::Value controller_json;
|
|
||||||
controller_json["name"] = this->name;
|
|
||||||
controller_json["id"] = this->id;
|
|
||||||
controller_json["ip"] = this->ip;
|
|
||||||
//controller_json["port"] = this->port;
|
|
||||||
controller_json["relay_count"] = this->relay_count;
|
|
||||||
controller_json["active"] = this->active;
|
|
||||||
|
|
||||||
|
|
||||||
controller_json["relays"] = Json::arrayValue;
|
|
||||||
|
|
||||||
for(int i = 0; this->relays[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
controller_json["relays"].append(this->relays[i]->to_json());
|
|
||||||
}
|
|
||||||
|
|
||||||
return controller_json;
|
|
||||||
}
|
|
||||||
|
|
||||||
controller_dbo**
|
|
||||||
controller_dbo::get_all()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "SELECT * FROM controllers;", -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
return controller_db_select(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
controller_dbo**
|
|
||||||
controller_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_func)
|
|
||||||
{
|
|
||||||
helpers::sql_filter_builder *filters[1];
|
|
||||||
helpers::sql_filter_builder filter
|
|
||||||
{
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
bind_func,
|
|
||||||
";"
|
|
||||||
};
|
|
||||||
filters[0] = &filter;
|
|
||||||
sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM controllers WHERE", filters);
|
|
||||||
|
|
||||||
return controller_db_select(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
controller_dbo**
|
|
||||||
controller_dbo::get_by(helpers::sql_filter_builder **filters)
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM controllers WHERE", filters);
|
|
||||||
|
|
||||||
return controller_db_select(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
controller_dbo::command(int command_code, const char *payload)
|
|
||||||
{
|
|
||||||
char port_str[6];
|
|
||||||
sprintf(port_str, "%d", this->port);
|
|
||||||
|
|
||||||
int controller_socket = helpers::open_tcp_connection(this->ip, port_str);
|
|
||||||
|
|
||||||
if(!controller_socket)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Can't open command socket " << this->ip << ":" << port_str;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_DEBUG << "Commanding (" << command_code << ") " << payload;
|
|
||||||
send(controller_socket, &command_code, 1, 0);
|
|
||||||
send(controller_socket, payload, strlen(payload), 0);
|
|
||||||
close(controller_socket);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
controller_dbo::free_list(controller_dbo **controllers_list)
|
|
||||||
{
|
|
||||||
for(int i = 0; controllers_list[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
delete controllers_list[i];
|
|
||||||
}
|
|
||||||
free(controllers_list);
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
#ifndef EMGAUWA_CORE_controller_DBO_H
|
|
||||||
#define EMGAUWA_CORE_controller_DBO_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <sqlite3.h>
|
|
||||||
#include <json/value.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include "relay_dbo.h"
|
|
||||||
|
|
||||||
class controller_dbo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
char id[33];
|
|
||||||
char name[128];
|
|
||||||
char ip[17];
|
|
||||||
bool active;
|
|
||||||
int port;
|
|
||||||
int relay_count;
|
|
||||||
relay_dbo **relays;
|
|
||||||
|
|
||||||
~controller_dbo();
|
|
||||||
|
|
||||||
bool
|
|
||||||
update();
|
|
||||||
|
|
||||||
bool
|
|
||||||
insert();
|
|
||||||
|
|
||||||
bool
|
|
||||||
remove();
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
to_json();
|
|
||||||
|
|
||||||
static controller_dbo**
|
|
||||||
get_by_simple(const char *key, const void *value, intptr_t bind_func);
|
|
||||||
|
|
||||||
static controller_dbo**
|
|
||||||
get_by(helpers::sql_filter_builder **filters);
|
|
||||||
|
|
||||||
static controller_dbo**
|
|
||||||
get_all();
|
|
||||||
|
|
||||||
bool
|
|
||||||
command(int command_code, const char *payload);
|
|
||||||
|
|
||||||
static void
|
|
||||||
free_list(controller_dbo **controllers_list);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //EMGAUWA_CORE_controller_DBO_H
|
|
114
models/junction_relay_schedule.c
Normal file
114
models/junction_relay_schedule.c
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <models/junction_relay_schedule.h>
|
||||||
|
#include <logger.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)
|
||||||
|
{
|
||||||
|
LOG_ERROR("error inserting data: %s", sqlite3_errmsg(global_database));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_relay_schedule_get_schedule_id(uint8_t weekday, int relay_id)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT schedule_id FROM junction_relay_schedule WHERE weekday=?1 AND relay_id=?2 LIMIT 1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, weekday);
|
||||||
|
sqlite3_bind_int(stmt, 2, relay_id);
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = sqlite3_step(stmt);
|
||||||
|
if (s == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
result = sqlite3_column_int(stmt, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s == SQLITE_DONE)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("error reading from database: %s", sqlite3_errstr(s));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_relay_schedule_remove(uint8_t weekday, int relay_id, int schedule_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "DELETE FROM junction_relay_schedule WHERE weekday=?1 AND schedule_id=?2 AND relay_id=?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);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return rc == SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_remove_for_schedule(int schedule_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "DELETE FROM junction_relay_schedule WHERE schedule_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return rc == SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
175
models/junction_tag.c
Normal file
175
models/junction_tag.c
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <sqlite3.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <models/junction_tag.h>
|
||||||
|
#include <database.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_tag_insert(int tag_id, int relay_id, int schedule_id)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "INSERT INTO junction_tag(tag_id, schedule_id, relay_id) values (?1, ?2, ?3);", -1, &stmt, NULL);
|
||||||
|
|
||||||
|
sqlite3_bind_int(stmt, 1, tag_id);
|
||||||
|
sqlite3_bind_int(stmt, 2, schedule_id);
|
||||||
|
sqlite3_bind_int(stmt, 3, relay_id);
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (rc != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
printf("ERROR inserting data: %s\n", sqlite3_errmsg(global_database));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int*
|
||||||
|
get_ids(sqlite3_stmt *stmt)
|
||||||
|
{
|
||||||
|
int *ids = malloc(sizeof(int));
|
||||||
|
int new_id;
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = sqlite3_step(stmt);
|
||||||
|
if (s == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
new_id = sqlite3_column_int(stmt, 0);
|
||||||
|
row++;
|
||||||
|
|
||||||
|
ids = (int*)realloc(ids, sizeof(int) * (row + 1));
|
||||||
|
ids[row - 1] = new_id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s == SQLITE_DONE)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("error selecting relays from database: %s\n", sqlite3_errstr(s));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
ids[row] = 0;
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_tag_get_relays_for_tag_id(int tag_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT relay_id FROM junction_tag WHERE tag_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, tag_id);
|
||||||
|
|
||||||
|
return get_ids(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_tag_get_schedules_for_tag_id(int tag_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT schedule_id FROM junction_tag WHERE tag_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, tag_id);
|
||||||
|
|
||||||
|
return get_ids(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_tag_get_tags_for_relay_id(int relay_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT tag_id FROM junction_tag WHERE relay_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, relay_id);
|
||||||
|
|
||||||
|
return get_ids(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int*
|
||||||
|
junction_tag_get_tags_for_schedule_id(int schedule_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT tag_id FROM junction_tag WHERE schedule_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||||
|
|
||||||
|
return get_ids(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_tag_remove(int tag_id, int relay_id, int schedule_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "DELETE FROM junction_tag WHERE tag_id=?1 AND schedule_id=?2 AND relay_id=?3;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, tag_id);
|
||||||
|
sqlite3_bind_int(stmt, 2, schedule_id);
|
||||||
|
sqlite3_bind_int(stmt, 3, relay_id);
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return rc == SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_tag_remove_for_tag(int tag_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "DELETE FROM junction_tag WHERE tag_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, tag_id);
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return rc == SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
junction_tag_remove_for_relay(int relay_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "DELETE FROM junction_tag 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_tag_remove_for_schedule(int schedule_id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "DELETE FROM junction_tag WHERE schedule_id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return rc == SQLITE_DONE;
|
||||||
|
}
|
37
models/period.c
Normal file
37
models/period.c
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <models/period.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
period_helper_parse_hhmm(const char *hhmm_str, uint16_t *hhmm)
|
||||||
|
{
|
||||||
|
if(strlen(hhmm_str) != 5)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(hhmm_str[2] != ':')
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
for(int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
if(i != 2)
|
||||||
|
{
|
||||||
|
if(hhmm_str[i] < '0' || hhmm_str[i] > '9')
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t tmp_h, tmp_m;
|
||||||
|
tmp_h = (uint16_t)strtol(&hhmm_str[0], NULL, 10);
|
||||||
|
tmp_m = (uint16_t)strtol(&hhmm_str[3], NULL, 10);
|
||||||
|
|
||||||
|
*hhmm = (tmp_h * 60) + tmp_m;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -1,39 +0,0 @@
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdint>
|
|
||||||
#include "period.h"
|
|
||||||
|
|
||||||
period::period(uint16_t start, uint16_t end)
|
|
||||||
{
|
|
||||||
this->start = start;
|
|
||||||
this->end = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
period::to_json()
|
|
||||||
{
|
|
||||||
Json::Value result;
|
|
||||||
|
|
||||||
char start_str[8], end_str[8];
|
|
||||||
|
|
||||||
sprintf(start_str, "%02d:%02d", this->start / 60, this->start % 60);
|
|
||||||
sprintf(end_str, "%02d:%02d", this->end / 60, this->end % 60);
|
|
||||||
|
|
||||||
result["start"] = std::string(start_str);
|
|
||||||
result["end"] = std::string(end_str);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
period::is_in_period(uint16_t timestamp)
|
|
||||||
{
|
|
||||||
if(this->start < this->end)
|
|
||||||
{
|
|
||||||
return this->start <= timestamp and timestamp <= this->end;
|
|
||||||
}
|
|
||||||
if(this->start > this->end)
|
|
||||||
{
|
|
||||||
return this->end <= timestamp and timestamp <= this->start;
|
|
||||||
}
|
|
||||||
return this->start == timestamp;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
#ifndef EMGAUWA_CORE_PERIOD_H
|
|
||||||
#define EMGAUWA_CORE_PERIOD_H
|
|
||||||
|
|
||||||
|
|
||||||
#include <json/json.h>
|
|
||||||
|
|
||||||
class period
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint16_t start;
|
|
||||||
uint16_t end;
|
|
||||||
|
|
||||||
period(uint16_t start, uint16_t end);
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
to_json();
|
|
||||||
|
|
||||||
bool
|
|
||||||
is_in_period(uint16_t timestamp);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //EMGAUWA_CORE_PERIOD_H
|
|
|
@ -1,70 +0,0 @@
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include "period_list.h"
|
|
||||||
|
|
||||||
period_list::period_list()
|
|
||||||
{
|
|
||||||
this->periods = (period**)malloc(0);
|
|
||||||
this->length = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
period_list::period_list(const uint16_t* periods_blob)
|
|
||||||
{
|
|
||||||
this->length = periods_blob[0];
|
|
||||||
this->periods = (period**)malloc(sizeof(period*) * this->length);
|
|
||||||
|
|
||||||
for(int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
auto new_period = new period(periods_blob[(i * 2) + 1], periods_blob[(i * 2) + 2]);
|
|
||||||
periods[i] = new_period;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
period_list::period_list(period **periods, uint16_t length)
|
|
||||||
{
|
|
||||||
this->periods = periods;
|
|
||||||
this->length = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
period_list::~period_list()
|
|
||||||
{
|
|
||||||
for(int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
delete this->periods[i];
|
|
||||||
}
|
|
||||||
free(this->periods);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
period_list::add_period(uint16_t start, uint16_t end)
|
|
||||||
{
|
|
||||||
this->length++;
|
|
||||||
this->periods = (period**)realloc(this->periods, sizeof(period*) * this->length);
|
|
||||||
this->periods[this->length - 1] = new period(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
period_list::to_json()
|
|
||||||
{
|
|
||||||
Json::Value result(Json::arrayValue);
|
|
||||||
for(int i = 0; i < this->length; i++)
|
|
||||||
{
|
|
||||||
result.append(this->periods[i]->to_json());
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t*
|
|
||||||
period_list::to_db_blob()
|
|
||||||
{
|
|
||||||
auto result = (uint16_t*)malloc(sizeof(uint16_t) * ((this->length * 2) + 1));
|
|
||||||
|
|
||||||
result[0] = this->length;
|
|
||||||
|
|
||||||
for(int i = 0; i < this->length; i++)
|
|
||||||
{
|
|
||||||
result[(i * 2) + 1] = this->periods[i]->start;
|
|
||||||
result[(i * 2) + 2] = this->periods[i]->end;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
#ifndef EMGAUWA_CORE_PERIOD_LIST_H
|
|
||||||
#define EMGAUWA_CORE_PERIOD_LIST_H
|
|
||||||
|
|
||||||
#include <json/json.h>
|
|
||||||
#include "period.h"
|
|
||||||
|
|
||||||
class period_list
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
period **periods;
|
|
||||||
uint16_t length;
|
|
||||||
|
|
||||||
period_list();
|
|
||||||
explicit period_list(const uint16_t *periods_blob);
|
|
||||||
period_list(period **periods, uint16_t length);
|
|
||||||
~period_list();
|
|
||||||
|
|
||||||
void
|
|
||||||
add_period(uint16_t start, uint16_t end);
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
to_json();
|
|
||||||
|
|
||||||
uint16_t*
|
|
||||||
to_db_blob();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //EMGAUWA_CORE_PERIOD_LIST_H
|
|
|
@ -1,257 +0,0 @@
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <trantor/utils/Logger.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include "relay_dbo.h"
|
|
||||||
#include "globals.h"
|
|
||||||
#include "controller_dbo.h"
|
|
||||||
#include "schedule_dbo.h"
|
|
||||||
|
|
||||||
static bool relay_db_update_insert(relay_dbo *relay, sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
sqlite3_bind_text(stmt, 4, relay->active_schedule_id, -1, SQLITE_STATIC);
|
|
||||||
sqlite3_bind_text(stmt, 5, relay->controller_id, -1, SQLITE_STATIC);
|
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
|
||||||
if (rc != SQLITE_DONE)
|
|
||||||
{
|
|
||||||
printf("ERROR inserting data: %s\n", sqlite3_errmsg(globals::db));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static relay_dbo*
|
|
||||||
relay_db_select_mapper(sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
auto *new_relay = new relay_dbo();
|
|
||||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
|
||||||
{
|
|
||||||
const char *name = sqlite3_column_name(stmt, i);
|
|
||||||
switch(name[0])
|
|
||||||
{
|
|
||||||
case 'a': // active_schedule_id
|
|
||||||
strncpy(new_relay->active_schedule_id, (const char*)sqlite3_column_text(stmt, i), 32);
|
|
||||||
break;
|
|
||||||
case 'c': // controller_id
|
|
||||||
strncpy(new_relay->controller_id, (const char*)sqlite3_column_text(stmt, i), 32);
|
|
||||||
break;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
default: // ignore columns not implemented
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new_relay;
|
|
||||||
}
|
|
||||||
|
|
||||||
static relay_dbo**
|
|
||||||
relay_db_select(sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
auto **all_relays = (relay_dbo**)malloc(sizeof(relay_dbo*));
|
|
||||||
|
|
||||||
int row = 0;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s = sqlite3_step(stmt);
|
|
||||||
if (s == SQLITE_ROW)
|
|
||||||
{
|
|
||||||
relay_dbo *new_relay = relay_db_select_mapper(stmt);
|
|
||||||
|
|
||||||
new_relay->reload_active_schedule();
|
|
||||||
|
|
||||||
row++;
|
|
||||||
|
|
||||||
all_relays = (relay_dbo**)realloc(all_relays, sizeof(relay_dbo*) * (row + 1));
|
|
||||||
all_relays[row - 1] = new_relay;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (s == SQLITE_DONE)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error Selecting relays from database: " << sqlite3_errstr(s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
all_relays[row] = nullptr;
|
|
||||||
|
|
||||||
return all_relays;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
relay_dbo::reload_active_schedule()
|
|
||||||
{
|
|
||||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("id", this->active_schedule_id, (intptr_t)&sqlite3_bind_text);
|
|
||||||
|
|
||||||
if(!schedules[0])
|
|
||||||
{
|
|
||||||
free(schedules);
|
|
||||||
schedules = schedule_dbo::get_by_simple("id", "off", (intptr_t)&sqlite3_bind_text);
|
|
||||||
strcpy(this->active_schedule_id, "off");
|
|
||||||
}
|
|
||||||
|
|
||||||
this->active_schedule = schedules[0];
|
|
||||||
|
|
||||||
free(schedules);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
relay_dbo::update()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "UPDATE relays set number = ?2, name = ?3, active_schedule_id = ?4, controller_id = ?5 WHERE id = ?1;", -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
return relay_db_update_insert(this, stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
relay_dbo::insert()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "INSERT INTO relays(number, name, active_schedule_id, controller_id) values (?2, ?3, ?4, ?5);", -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
return relay_db_update_insert(this, stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
relay_dbo::remove()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "DELETE FROM relays WHERE id=?1;", -1, &stmt, nullptr);
|
|
||||||
sqlite3_bind_int(stmt, 1, this->id);
|
|
||||||
rc = sqlite3_step(stmt);
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
return rc == SQLITE_DONE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
relay_dbo::to_json()
|
|
||||||
{
|
|
||||||
Json::Value relay_json;
|
|
||||||
// relay_json["id"] = this->id;
|
|
||||||
relay_json["name"] = this->name;
|
|
||||||
relay_json["number"] = this->number;
|
|
||||||
relay_json["active_schedule_id"] = this->active_schedule_id;
|
|
||||||
relay_json["controller_id"] = this->controller_id;
|
|
||||||
relay_json["active_schedule"] = this->active_schedule->to_json();
|
|
||||||
|
|
||||||
return relay_json;
|
|
||||||
}
|
|
||||||
|
|
||||||
relay_dbo**
|
|
||||||
relay_dbo::get_all()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "SELECT * FROM relays;", -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
return relay_db_select(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
relay_dbo**
|
|
||||||
relay_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_func)
|
|
||||||
{
|
|
||||||
helpers::sql_filter_builder *filters[1];
|
|
||||||
helpers::sql_filter_builder filter
|
|
||||||
{
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
bind_func,
|
|
||||||
";"
|
|
||||||
};
|
|
||||||
filters[0] = &filter;
|
|
||||||
sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM relays WHERE", filters);
|
|
||||||
|
|
||||||
return relay_db_select(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
relay_dbo**
|
|
||||||
relay_dbo::get_by(helpers::sql_filter_builder **filters)
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM relays WHERE", filters);
|
|
||||||
return relay_db_select(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
relay_dbo*
|
|
||||||
relay_dbo::get_relay_for_controller(const char *controller_id, int relay_num)
|
|
||||||
{
|
|
||||||
helpers::sql_filter_builder *filters[2];
|
|
||||||
helpers::sql_filter_builder filter(
|
|
||||||
"number",
|
|
||||||
(void*)(intptr_t)relay_num,
|
|
||||||
(intptr_t)&sqlite3_bind_int,
|
|
||||||
"AND"
|
|
||||||
);
|
|
||||||
helpers::sql_filter_builder filter2(
|
|
||||||
"controller_id",
|
|
||||||
(void*)controller_id,
|
|
||||||
(intptr_t)sqlite3_bind_text,
|
|
||||||
";"
|
|
||||||
);
|
|
||||||
filters[0] = &filter;
|
|
||||||
filters[1] = &filter2;
|
|
||||||
|
|
||||||
auto relays = relay_dbo::get_by(filters);
|
|
||||||
|
|
||||||
relay_dbo *relay = relays[0];
|
|
||||||
free(relays);
|
|
||||||
return relay;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
relay_dbo::valid_num_for_controller(const char *search_controller_id, int relay_num)
|
|
||||||
{
|
|
||||||
controller_dbo **controllers = controller_dbo::get_by_simple("id", search_controller_id, (intptr_t)&sqlite3_bind_text);
|
|
||||||
|
|
||||||
bool valid_id_and_num = controllers[0] && controllers[0]->relay_count > relay_num;
|
|
||||||
controller_dbo::free_list(controllers);
|
|
||||||
|
|
||||||
return valid_id_and_num;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
relay_dbo::free_list(relay_dbo **relays_list)
|
|
||||||
{
|
|
||||||
for(int i = 0; relays_list[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
delete relays_list[i];
|
|
||||||
}
|
|
||||||
free(relays_list);
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
#ifndef EMGAUWA_CORE_RELAY_DBO_H
|
|
||||||
#define EMGAUWA_CORE_RELAY_DBO_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <sqlite3.h>
|
|
||||||
#include <json/value.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include "schedule_dbo.h"
|
|
||||||
|
|
||||||
class relay_dbo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
int id;
|
|
||||||
char name[128];
|
|
||||||
int number;
|
|
||||||
char controller_id[33];
|
|
||||||
char active_schedule_id[33];
|
|
||||||
schedule_dbo *active_schedule;
|
|
||||||
|
|
||||||
void
|
|
||||||
reload_active_schedule();
|
|
||||||
|
|
||||||
bool
|
|
||||||
update();
|
|
||||||
|
|
||||||
bool
|
|
||||||
insert();
|
|
||||||
|
|
||||||
bool
|
|
||||||
remove();
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
to_json();
|
|
||||||
|
|
||||||
static void
|
|
||||||
free_list(relay_dbo **relays_list);
|
|
||||||
|
|
||||||
static relay_dbo**
|
|
||||||
get_by_simple(const char *key, const void *value, intptr_t bind_func);
|
|
||||||
|
|
||||||
static relay_dbo**
|
|
||||||
get_by(helpers::sql_filter_builder **filters);
|
|
||||||
|
|
||||||
static relay_dbo*
|
|
||||||
get_relay_for_controller(const char *controller_id, int relay_num);
|
|
||||||
|
|
||||||
static bool
|
|
||||||
valid_num_for_controller(const char *search_controller_id, int relay_num);
|
|
||||||
|
|
||||||
static relay_dbo**
|
|
||||||
get_all();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //EMGAUWA_CORE_RELAY_DBO_H
|
|
325
models/schedule.c
Normal file
325
models/schedule.c
Normal file
|
@ -0,0 +1,325 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
#include <cJSON.h>
|
||||||
|
#include <logger.h>
|
||||||
|
#include <database.h>
|
||||||
|
#include <models/schedule.h>
|
||||||
|
#include <models/junction_tag.h>
|
||||||
|
#include <models/tag.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
db_update_insert(schedule_t *schedule, sqlite3_stmt *stmt)
|
||||||
|
{
|
||||||
|
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_text(stmt, 3, schedule->name, -1, SQLITE_STATIC);
|
||||||
|
sqlite3_bind_blob(stmt, 4, 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 'n': // name
|
||||||
|
strncpy(new_schedule->name, (const char*)sqlite3_column_text(stmt, i), 127);
|
||||||
|
new_schedule->name[127] = '\0';
|
||||||
|
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;
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
LOG_ERROR("srror 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)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
if(schedule->id)
|
||||||
|
{
|
||||||
|
sqlite3_prepare_v2(global_database, "UPDATE schedules SET uid = ?2, name = ?3, periods = ?4 WHERE id=?1;", -1, &stmt, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sqlite3_prepare_v2(global_database, "INSERT INTO schedules(uid, name, periods) values (?2, ?3, ?4);", -1, &stmt, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = db_update_insert(schedule, stmt);
|
||||||
|
|
||||||
|
if(result)
|
||||||
|
{
|
||||||
|
if(schedule->id)
|
||||||
|
{
|
||||||
|
LOG_ERROR("error inserting data: %s\n", sqlite3_errmsg(global_database));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("error updating data: %s\n", sqlite3_errmsg(global_database));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
schedule->id = sqlite3_last_insert_rowid(global_database);
|
||||||
|
}
|
||||||
|
return 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t*
|
||||||
|
schedule_periods_to_blob(schedule_t *schedule)
|
||||||
|
{
|
||||||
|
uint16_t *blob = malloc(sizeof(uint16_t) * ((schedule->periods_count * 2) + 1));
|
||||||
|
|
||||||
|
blob[0] = schedule->periods_count;
|
||||||
|
|
||||||
|
for(int i = 0; i < schedule->periods_count; i++)
|
||||||
|
{
|
||||||
|
blob[(i * 2) + 1] = schedule->periods[i].start;
|
||||||
|
blob[(i * 2) + 2] = schedule->periods[i].end;
|
||||||
|
}
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON*
|
||||||
|
schedule_to_json(schedule_t *schedule)
|
||||||
|
{
|
||||||
|
cJSON *json = cJSON_CreateObject();
|
||||||
|
|
||||||
|
cJSON *json_name = cJSON_CreateString(schedule->name);
|
||||||
|
if(json_name == NULL)
|
||||||
|
{
|
||||||
|
cJSON_Delete(json);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json, "name", json_name);
|
||||||
|
|
||||||
|
char uuid_str[UUID_STR_LEN];
|
||||||
|
schedule_uid_unparse(schedule->uid, uuid_str);
|
||||||
|
cJSON *json_id = cJSON_CreateString(uuid_str);
|
||||||
|
if(json_name == NULL)
|
||||||
|
{
|
||||||
|
cJSON_Delete(json);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json, "id", json_id);
|
||||||
|
|
||||||
|
cJSON *json_periods = cJSON_CreateArray();
|
||||||
|
if(json_periods == NULL)
|
||||||
|
{
|
||||||
|
cJSON_Delete(json);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json, "periods", json_periods);
|
||||||
|
|
||||||
|
for(int i = 0; i < schedule->periods_count; ++i)
|
||||||
|
{
|
||||||
|
cJSON *json_period = cJSON_CreateObject();
|
||||||
|
if (json_period == NULL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char start_str[8], end_str[8];
|
||||||
|
period_t *period = &schedule->periods[i];
|
||||||
|
sprintf(start_str, "%02d:%02d", period->start / 60, period->start % 60);
|
||||||
|
sprintf(end_str, "%02d:%02d", period->end / 60, period->end % 60);
|
||||||
|
|
||||||
|
cJSON *json_period_start = cJSON_CreateString(start_str);
|
||||||
|
if (json_period_start == NULL)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("failed to add start period from string '%s'\n", start_str);
|
||||||
|
cJSON_Delete(json_period);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json_period, "start", json_period_start);
|
||||||
|
|
||||||
|
cJSON *json_period_end = cJSON_CreateString(end_str);
|
||||||
|
if (json_period_end == NULL)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("failed to add end period from string '%s'\n", end_str);
|
||||||
|
cJSON_Delete(json_period);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json_period, "end", json_period_end);
|
||||||
|
|
||||||
|
cJSON_AddItemToArray(json_periods, json_period);
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json_tags = cJSON_CreateArray();
|
||||||
|
int *tags_ids = junction_tag_get_tags_for_schedule_id(schedule->id);
|
||||||
|
if(tags_ids != NULL)
|
||||||
|
{
|
||||||
|
for(int i = 0; tags_ids[i] != 0; ++i)
|
||||||
|
{
|
||||||
|
char *tag = tag_get_tag(tags_ids[i]);
|
||||||
|
if(tag == NULL)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *json_tag = cJSON_CreateString(tag);
|
||||||
|
if (json_tag == NULL)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("failed to add tag from string '%s'\n", tag);
|
||||||
|
free(tag);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cJSON_AddItemToArray(json_tags, json_tag);
|
||||||
|
free(tag);
|
||||||
|
}
|
||||||
|
free(tags_ids);
|
||||||
|
}
|
||||||
|
cJSON_AddItemToObject(json, "tags", json_tags);
|
||||||
|
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
schedule_t**
|
||||||
|
schedule_get_all()
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules;", -1, &stmt, NULL);
|
||||||
|
|
||||||
|
return schedule_db_select(stmt);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
|
@ -1,197 +0,0 @@
|
||||||
#include <cstring>
|
|
||||||
#include <trantor/utils/Logger.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include "schedule_dbo.h"
|
|
||||||
#include "globals.h"
|
|
||||||
#include "period.h"
|
|
||||||
|
|
||||||
static bool schedule_db_update_insert(schedule_dbo *schedule, sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
uint16_t *periods_blob = schedule->periods->to_db_blob();
|
|
||||||
int blob_size = (int)sizeof(uint16_t) * ((periods_blob[0] * 2) + 1);
|
|
||||||
|
|
||||||
sqlite3_bind_text(stmt, 1, schedule->id, -1, SQLITE_STATIC);
|
|
||||||
sqlite3_bind_text(stmt, 2, schedule->name, -1, SQLITE_STATIC);
|
|
||||||
sqlite3_bind_blob(stmt, 3, periods_blob, blob_size, SQLITE_STATIC);
|
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
free(periods_blob);
|
|
||||||
|
|
||||||
if (rc != SQLITE_DONE)
|
|
||||||
{
|
|
||||||
LOG_ERROR << "ERROR inserting/updating data: " << sqlite3_errmsg(globals::db);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static schedule_dbo*
|
|
||||||
schedule_db_select_mapper(sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
auto new_schedule = new schedule_dbo();
|
|
||||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
|
||||||
{
|
|
||||||
const char *name = sqlite3_column_name(stmt, i);
|
|
||||||
switch(name[0])
|
|
||||||
{
|
|
||||||
case 'i': // id
|
|
||||||
strncpy(new_schedule->id, (const char*)sqlite3_column_text(stmt, i), 32);
|
|
||||||
new_schedule->id[32] = '\0';
|
|
||||||
break;
|
|
||||||
case 'n': // name
|
|
||||||
strncpy(new_schedule->name, (const char*)sqlite3_column_text(stmt, i), 127);
|
|
||||||
new_schedule->name[127] = '\0';
|
|
||||||
break;
|
|
||||||
case 'p': // periods
|
|
||||||
new_schedule->periods = new period_list((const uint16_t*)sqlite3_column_blob(stmt, i));
|
|
||||||
break;
|
|
||||||
default: // ignore columns not implemented
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new_schedule;
|
|
||||||
}
|
|
||||||
|
|
||||||
static schedule_dbo**
|
|
||||||
schedule_db_select(sqlite3_stmt *stmt)
|
|
||||||
{
|
|
||||||
auto **all_schedules = (schedule_dbo**)malloc(sizeof(schedule_dbo*));
|
|
||||||
|
|
||||||
int row = 0;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
int s;
|
|
||||||
|
|
||||||
s = sqlite3_step(stmt);
|
|
||||||
if (s == SQLITE_ROW)
|
|
||||||
{
|
|
||||||
schedule_dbo *new_schedule = schedule_db_select_mapper(stmt);
|
|
||||||
row++;
|
|
||||||
|
|
||||||
all_schedules = (schedule_dbo**)realloc(all_schedules, sizeof(schedule_dbo*) * (row + 1));
|
|
||||||
all_schedules[row - 1] = new_schedule;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(s == SQLITE_DONE)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_ERROR << "Error Selecting schedules from database: " << sqlite3_errstr(s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
all_schedules[row] = nullptr;
|
|
||||||
|
|
||||||
return all_schedules;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
schedule_dbo::update()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "UPDATE schedules SET name = ?2, periods = ?3 WHERE id=?1;", -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
return schedule_db_update_insert(this, stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
schedule_dbo::insert()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "INSERT INTO schedules(id, name, periods) values (?1, ?2, ?3);", -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
return schedule_db_update_insert(this, stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
schedule_dbo::remove()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "DELETE FROM schedules WHERE id=?1;", -1, &stmt, nullptr);
|
|
||||||
sqlite3_bind_text(stmt, 1, this->id, -1, SQLITE_STATIC);
|
|
||||||
rc = sqlite3_step(stmt);
|
|
||||||
|
|
||||||
sqlite3_finalize(stmt);
|
|
||||||
|
|
||||||
return rc == SQLITE_DONE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule_dbo::~schedule_dbo()
|
|
||||||
{
|
|
||||||
delete this->periods;
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
schedule_dbo::to_json()
|
|
||||||
{
|
|
||||||
Json::Value schedule_json;
|
|
||||||
schedule_json["name"] = this->name;
|
|
||||||
schedule_json["id"] = this->id;
|
|
||||||
schedule_json["periods"] = this->periods->to_json();
|
|
||||||
|
|
||||||
return schedule_json;
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule_dbo**
|
|
||||||
schedule_dbo::get_all()
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt;
|
|
||||||
|
|
||||||
sqlite3_prepare_v2(globals::db, "SELECT * FROM schedules;", -1, &stmt, nullptr);
|
|
||||||
|
|
||||||
return schedule_db_select(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule_dbo**
|
|
||||||
schedule_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_func)
|
|
||||||
{
|
|
||||||
helpers::sql_filter_builder *filters[1];
|
|
||||||
helpers::sql_filter_builder filter
|
|
||||||
{
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
bind_func,
|
|
||||||
";"
|
|
||||||
};
|
|
||||||
filters[0] = &filter;
|
|
||||||
|
|
||||||
sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM schedules WHERE", filters);
|
|
||||||
|
|
||||||
return schedule_db_select(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
schedule_dbo**
|
|
||||||
schedule_dbo::get_by(helpers::sql_filter_builder **filters)
|
|
||||||
{
|
|
||||||
sqlite3_stmt *stmt = helpers::create_sql_filtered_query("SELECT * FROM schedules WHERE", filters);
|
|
||||||
|
|
||||||
return schedule_db_select(stmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
schedule_dbo::free_list(schedule_dbo **schedules_list)
|
|
||||||
{
|
|
||||||
for(int i = 0; schedules_list[i] != nullptr; i++)
|
|
||||||
{
|
|
||||||
delete schedules_list[i];
|
|
||||||
}
|
|
||||||
free(schedules_list);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
#ifndef EMGAUWA_CORE_SCHEDULE_DBO_H
|
|
||||||
#define EMGAUWA_CORE_SCHEDULE_DBO_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <sqlite3.h>
|
|
||||||
#include <json/value.h>
|
|
||||||
#include <helpers.h>
|
|
||||||
#include "period.h"
|
|
||||||
#include "period_list.h"
|
|
||||||
|
|
||||||
class schedule_dbo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
char id[33];
|
|
||||||
char name[128];
|
|
||||||
period_list *periods;
|
|
||||||
|
|
||||||
bool
|
|
||||||
update();
|
|
||||||
|
|
||||||
bool
|
|
||||||
insert();
|
|
||||||
|
|
||||||
bool
|
|
||||||
remove();
|
|
||||||
|
|
||||||
~schedule_dbo();
|
|
||||||
|
|
||||||
Json::Value
|
|
||||||
to_json();
|
|
||||||
|
|
||||||
static void
|
|
||||||
free_list(schedule_dbo **schedules_list);
|
|
||||||
|
|
||||||
static schedule_dbo**
|
|
||||||
get_by_simple(const char *key, const void *value, intptr_t bind_func);
|
|
||||||
|
|
||||||
static schedule_dbo**
|
|
||||||
get_by(helpers::sql_filter_builder **filters);
|
|
||||||
|
|
||||||
static schedule_dbo**
|
|
||||||
get_all();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //EMGAUWA_CORE_SCHEDULE_DBO_H
|
|
133
models/tag.c
Normal file
133
models/tag.c
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include <logger.h>
|
||||||
|
#include <database.h>
|
||||||
|
#include <models/tag.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
tag_save(int id, const char *tag)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
if(id)
|
||||||
|
{
|
||||||
|
sqlite3_prepare_v2(global_database, "UPDATE tags SET tag = ?2 WHERE id = ?1;", -1, &stmt, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sqlite3_prepare_v2(global_database, "INSERT INTO tags(tag) values (?2);", -1, &stmt, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_bind_int(stmt, 1, id);
|
||||||
|
sqlite3_bind_text(stmt, 2, tag, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
if (rc != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
LOG_ERROR("error saving tag: %s\n", sqlite3_errmsg(global_database));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
tag_get_tag(int id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT tag FROM tags WHERE id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, id);
|
||||||
|
|
||||||
|
char *result = NULL;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = sqlite3_step(stmt);
|
||||||
|
if (s == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
const char *found_tag = (const char *)sqlite3_column_text(stmt, 0);
|
||||||
|
result = (char*)malloc(sizeof(char) * (strlen(found_tag) + 1));
|
||||||
|
strcpy(result, found_tag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s == SQLITE_DONE)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("error selecting tags from database: %s\n", sqlite3_errstr(s));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
tag_get_id(const char *tag)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "SELECT id FROM tags WHERE tag=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_text(stmt, 1, tag, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
|
||||||
|
s = sqlite3_step(stmt);
|
||||||
|
if (s == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
result = sqlite3_column_int(stmt, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (s == SQLITE_DONE)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR("error selecting tags from database: %s\n", sqlite3_errstr(s));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
tag_remove(int id)
|
||||||
|
{
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
sqlite3_prepare_v2(global_database, "DELETE FROM tags WHERE id=?1;", -1, &stmt, NULL);
|
||||||
|
sqlite3_bind_int(stmt, 1, id);
|
||||||
|
|
||||||
|
rc = sqlite3_step(stmt);
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
|
return rc == SQLITE_DONE;
|
||||||
|
}
|
||||||
|
|
275
router.c
Normal file
275
router.c
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <logger.h>
|
||||||
|
#include <router.h>
|
||||||
|
|
||||||
|
#include <endpoints/api_v1_schedules.h>
|
||||||
|
|
||||||
|
static const int HTTP_METHOD_GET = (1 << 0);
|
||||||
|
static const int HTTP_METHOD_POST = (1 << 1);
|
||||||
|
static const int HTTP_METHOD_PUT = (1 << 2);
|
||||||
|
static const int HTTP_METHOD_DELETE = (1 << 3);
|
||||||
|
static const int HTTP_METHOD_OPTIONS = (1 << 4);
|
||||||
|
|
||||||
|
static endpoint_t endpoints[ENDPOINTS_MAX_COUNT];
|
||||||
|
static endpoint_t endpoint_index;
|
||||||
|
static endpoint_t endpoint_not_found;
|
||||||
|
static int endpoints_registered = 0;
|
||||||
|
static const char delimiter[2] = "/";
|
||||||
|
|
||||||
|
static void
|
||||||
|
endpoint_index_func(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm)
|
||||||
|
{
|
||||||
|
(void)args;
|
||||||
|
mg_send_head(c, 200, hm->body.len, "Content-Type: text/plain");
|
||||||
|
//mg_printf(c, "%.*s", (int)hm->message.len, hm->message.p);
|
||||||
|
mg_printf(c, "%.*s", (int)hm->body.len, hm->body.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
endpoint_not_found_func(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm)
|
||||||
|
{
|
||||||
|
(void)args;
|
||||||
|
mg_send_head(c, 404, hm->body.len, "Content-Type: text/plain");
|
||||||
|
//mg_printf(c, "%.*s", (int)hm->message.len, hm->message.p);
|
||||||
|
mg_printf(c, "%.*s", (int)hm->body.len, hm->body.p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
router_init()
|
||||||
|
{
|
||||||
|
// add index endpoint
|
||||||
|
endpoint_index.route = NULL;
|
||||||
|
endpoint_index.func = endpoint_index_func;
|
||||||
|
endpoint_index.methods = 0;
|
||||||
|
endpoint_index.args_count = 0;
|
||||||
|
endpoint_index.args = NULL;
|
||||||
|
|
||||||
|
// add 404 endpoint
|
||||||
|
endpoint_not_found.route = NULL;
|
||||||
|
endpoint_not_found.func = endpoint_not_found_func;
|
||||||
|
endpoint_not_found.methods = 0;
|
||||||
|
endpoint_not_found.args_count = 0;
|
||||||
|
endpoint_not_found.args = NULL;
|
||||||
|
|
||||||
|
router_register_endpoint("/api/v1/schedules/", HTTP_METHOD_GET, api_v1_schedules_GET);
|
||||||
|
router_register_endpoint("/api/v1/schedules/", HTTP_METHOD_POST, api_v1_schedules_POST);
|
||||||
|
router_register_endpoint("/api/v1/schedules/{str}/", HTTP_METHOD_GET, api_v1_schedules_STR_GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
router_register_endpoint(const char *route, int methods, endpoint_func_f func)
|
||||||
|
{
|
||||||
|
if(endpoints_registered >= ENDPOINTS_MAX_COUNT)
|
||||||
|
{
|
||||||
|
LOG_ERROR("can't register more than %d endpoints\n", ENDPOINTS_MAX_COUNT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint_t *endpoint = &endpoints[endpoints_registered];
|
||||||
|
|
||||||
|
int route_parts_count = 1;
|
||||||
|
size_t route_len = strlen(route);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < route_len; ++i)
|
||||||
|
{
|
||||||
|
if(route[i] == delimiter[0] || route[i] == '\0')
|
||||||
|
{
|
||||||
|
++route_parts_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// +1 for NULL terminator
|
||||||
|
endpoint->route = malloc(sizeof(char*) * (route_parts_count + 1));
|
||||||
|
endpoint->route_keeper = malloc(sizeof(char) * (route_len + 1));
|
||||||
|
strncpy(endpoint->route_keeper, route, route_len);
|
||||||
|
endpoint->route_keeper[route_len] = '\0';
|
||||||
|
|
||||||
|
int route_part = 0;
|
||||||
|
int route_args_count = 0;
|
||||||
|
|
||||||
|
char *route_token = strtok(endpoint->route_keeper, delimiter);
|
||||||
|
while(route_token)
|
||||||
|
{
|
||||||
|
if(strcmp(route_token, "{int}") == 0)
|
||||||
|
{
|
||||||
|
++route_args_count;
|
||||||
|
}
|
||||||
|
if(strcmp(route_token, "{str}") == 0)
|
||||||
|
{
|
||||||
|
++route_args_count;
|
||||||
|
}
|
||||||
|
endpoint->route[route_part] = route_token;
|
||||||
|
++route_part;
|
||||||
|
route_token = strtok(NULL, delimiter);
|
||||||
|
}
|
||||||
|
endpoint->route[route_part] = NULL;
|
||||||
|
|
||||||
|
endpoint->args_count = route_args_count;
|
||||||
|
endpoint->args = NULL;
|
||||||
|
if(route_args_count)
|
||||||
|
{
|
||||||
|
endpoint->args = malloc(sizeof(endpoint_args_t) * route_args_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint->func = func;
|
||||||
|
endpoint->methods = methods;
|
||||||
|
++endpoints_registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_method_int_for_str(struct mg_str *method_str)
|
||||||
|
{
|
||||||
|
if(strncmp(method_str->p, "GET", method_str->len) == 0)
|
||||||
|
{
|
||||||
|
return HTTP_METHOD_GET;
|
||||||
|
}
|
||||||
|
if(strncmp(method_str->p, "POST", method_str->len) == 0)
|
||||||
|
{
|
||||||
|
return HTTP_METHOD_POST;
|
||||||
|
}
|
||||||
|
if(strncmp(method_str->p, "PUT", method_str->len) == 0)
|
||||||
|
{
|
||||||
|
return HTTP_METHOD_PUT;
|
||||||
|
}
|
||||||
|
if(strncmp(method_str->p, "DELETE", method_str->len) == 0)
|
||||||
|
{
|
||||||
|
return HTTP_METHOD_DELETE;
|
||||||
|
}
|
||||||
|
if(strncmp(method_str->p, "OPTIONS", method_str->len) == 0)
|
||||||
|
{
|
||||||
|
return HTTP_METHOD_OPTIONS;
|
||||||
|
}
|
||||||
|
return HTTP_METHOD_GET;
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint_t*
|
||||||
|
router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method_str)
|
||||||
|
{
|
||||||
|
(void)uri_str;
|
||||||
|
(void)uri_len;
|
||||||
|
char *uri = malloc(sizeof(char) * (uri_len + 1));
|
||||||
|
strncpy(uri, uri_str, uri_len);
|
||||||
|
uri[uri_len] = '\0';
|
||||||
|
|
||||||
|
int method = get_method_int_for_str(method_str);
|
||||||
|
|
||||||
|
for(int i = 0; i < endpoints_registered; ++i)
|
||||||
|
{
|
||||||
|
if(endpoints[i].methods & method)
|
||||||
|
{
|
||||||
|
endpoints[i].possible_route = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
endpoints[i].possible_route = 0;
|
||||||
|
}
|
||||||
|
endpoints[i].args_found = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *uri_token;
|
||||||
|
char *uri_token_save;
|
||||||
|
uri_token = strtok_r(uri, delimiter, &uri_token_save);
|
||||||
|
|
||||||
|
int route_part = 0;
|
||||||
|
|
||||||
|
if(!uri_token)
|
||||||
|
{
|
||||||
|
free(uri);
|
||||||
|
return &endpoint_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(uri_token)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < endpoints_registered; ++i)
|
||||||
|
{
|
||||||
|
if(!endpoints[i].possible_route)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(!endpoints[i].route[route_part])
|
||||||
|
{
|
||||||
|
endpoints[i].possible_route = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(uri_token_save[0] == '\0' && endpoints[i].route[route_part + 1])
|
||||||
|
{
|
||||||
|
endpoints[i].possible_route = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strcmp(endpoints[i].route[route_part], uri_token) == 0)
|
||||||
|
{
|
||||||
|
endpoints[i].possible_route += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(strcmp(endpoints[i].route[route_part], "{int}") == 0)
|
||||||
|
{
|
||||||
|
char *endptr;
|
||||||
|
errno = 0;
|
||||||
|
int found_arg_int = strtol(uri_token, &endptr, 10);
|
||||||
|
if(errno || (endptr && *endptr != '\0'))
|
||||||
|
{
|
||||||
|
endpoints[i].possible_route = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
endpoints[i].possible_route += 2;
|
||||||
|
|
||||||
|
endpoints[i].args[endpoints[i].args_found].type = ENDPOINT_ARG_TYPE_INT;
|
||||||
|
endpoints[i].args[endpoints[i].args_found].value.v_int = found_arg_int;
|
||||||
|
++endpoints[i].args_found;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(strcmp(endpoints[i].route[route_part], "{str}") == 0)
|
||||||
|
{
|
||||||
|
endpoints[i].possible_route += 1;
|
||||||
|
|
||||||
|
endpoints[i].args[endpoints[i].args_found].type = ENDPOINT_ARG_TYPE_STR;
|
||||||
|
endpoints[i].args[endpoints[i].args_found].value.v_str = uri_token;
|
||||||
|
++endpoints[i].args_found;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
endpoints[i].possible_route = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
uri_token = strtok_r(NULL, delimiter, &uri_token_save);
|
||||||
|
++route_part;
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint_t *best_endpoint = &endpoint_not_found;
|
||||||
|
for(int i = 0; i < endpoints_registered; ++i)
|
||||||
|
{
|
||||||
|
int rating = endpoints[i].possible_route;
|
||||||
|
if(rating > best_endpoint->possible_route)
|
||||||
|
{
|
||||||
|
best_endpoint = &endpoints[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < best_endpoint->args_count; ++i)
|
||||||
|
{
|
||||||
|
if(best_endpoint->args[i].type == ENDPOINT_ARG_TYPE_STR)
|
||||||
|
{
|
||||||
|
char *arg_value_str = malloc(sizeof(char) * (strlen(best_endpoint->args[i].value.v_str) + 1));
|
||||||
|
strcpy(arg_value_str, best_endpoint->args[i].value.v_str);
|
||||||
|
best_endpoint->args[i].value.v_str = arg_value_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(uri);
|
||||||
|
|
||||||
|
return best_endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
router_free()
|
||||||
|
{
|
||||||
|
for(int i = 0; i < endpoints_registered; ++i)
|
||||||
|
{
|
||||||
|
free(endpoints[i].route_keeper);
|
||||||
|
free(endpoints[i].route);
|
||||||
|
if(endpoints[i].args)
|
||||||
|
{
|
||||||
|
free(endpoints[i].args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,40 +1,83 @@
|
||||||
create table meta
|
create table meta
|
||||||
(
|
(
|
||||||
version_num int not null
|
version_num INTEGER
|
||||||
|
NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
create table controllers
|
create table controllers
|
||||||
(
|
(
|
||||||
id VARCHAR(33) not null
|
id INTEGER
|
||||||
primary key
|
PRIMARY KEY
|
||||||
unique,
|
AUTOINCREMENT,
|
||||||
|
uid BLOB
|
||||||
|
NOT NULL
|
||||||
|
UNIQUE,
|
||||||
name VARCHAR(128),
|
name VARCHAR(128),
|
||||||
ip VARCHAR(16),
|
ip VARCHAR(16),
|
||||||
port INTEGER,
|
port INTEGER,
|
||||||
relay_count INTEGER,
|
relay_count INTEGER,
|
||||||
active BOOLEAN not null
|
active BOOLEAN
|
||||||
|
NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
create table relays
|
create table relays
|
||||||
(
|
(
|
||||||
id INTEGER not null
|
id INTEGER
|
||||||
primary key
|
PRIMARY KEY
|
||||||
unique,
|
AUTOINCREMENT,
|
||||||
name VARCHAR(128),
|
name VARCHAR(128),
|
||||||
number INTEGER not null,
|
number INTEGER
|
||||||
controller_id VARCHAR(33) not null
|
NOT NULL,
|
||||||
references controllers (id),
|
controller_id INTEGER
|
||||||
active_schedule_id VARCHAR(33)
|
NOT NULL
|
||||||
references schedules
|
REFERENCES controllers (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create table schedules
|
create table schedules
|
||||||
(
|
(
|
||||||
id VARCHAR(33) not null
|
id INTEGER
|
||||||
primary key
|
PRIMARY KEY
|
||||||
unique,
|
AUTOINCREMENT,
|
||||||
|
uid BLOB
|
||||||
|
NOT NULL
|
||||||
|
UNIQUE,
|
||||||
name VARCHAR(128),
|
name VARCHAR(128),
|
||||||
periods BLOB
|
periods BLOB
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO schedules (id, name, periods) VALUES ('off', 'off', x'00');
|
create table tags
|
||||||
|
(
|
||||||
|
id INTEGER
|
||||||
|
PRIMARY KEY
|
||||||
|
AUTOINCREMENT,
|
||||||
|
tag VARCHAR(128)
|
||||||
|
NOT NULL
|
||||||
|
UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
create table junction_tag
|
||||||
|
(
|
||||||
|
tag_id INTEGER
|
||||||
|
NOT NULL
|
||||||
|
REFERENCES tags (id),
|
||||||
|
relay_id INTEGER
|
||||||
|
REFERENCES relays (id),
|
||||||
|
schedule_id INTEGER
|
||||||
|
REFERENCES schedules (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
create table junction_relay_schedule
|
||||||
|
(
|
||||||
|
weekday SMALLINT
|
||||||
|
NOT NULL,
|
||||||
|
relay_id INTEGER
|
||||||
|
NOT NULL
|
||||||
|
REFERENCES relays (id),
|
||||||
|
schedule_id INTEGER
|
||||||
|
NOT NULL
|
||||||
|
REFERENCES schedules (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO schedules (uid, name, periods) VALUES (x'6f666600000000000000000000000000', 'off', x'00');
|
||||||
|
INSERT INTO schedules (uid, name, periods) VALUES (x'6f6e0000000000000000000000000000', 'on', x'010000009F05');
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,18 @@
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -marm")
|
set(CMAKE_C_COMPILER /usr/bin/arm-none-eabi-gcc)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpu=vfp")
|
set(CMAKE_C_COMPILER_WORKS 1)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv6zk+fp")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=arm1176jzf-s")
|
set(ARM-SYSROOT /usr/arm-none-eabi)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=arm1176jzf-s")
|
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpu=vfp")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv6zk+fp")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mcpu=arm1176jzf-s")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mtune=arm1176jzf-s")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --sysroot=${ARM-SYSROOT}" CACHE INTERNAL "" FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} --sysroot=${ARM-SYSROOT}" CACHE INTERNAL "" FORCE)
|
||||||
|
|
||||||
|
set(CMAKE_FIND_ROOT_PATH ${ARM-SYSROOT})
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
|
384
vendor/argparse.c
vendored
Normal file
384
vendor/argparse.c
vendored
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
/**
|
||||||
|
* 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
Normal file
130
vendor/argparse.h
vendored
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
/**
|
||||||
|
* 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
|
3074
vendor/cJSON.c
vendored
Normal file
3074
vendor/cJSON.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
293
vendor/cJSON.h
vendored
Normal file
293
vendor/cJSON.h
vendored
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
/*
|
||||||
|
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef cJSON__h
|
||||||
|
#define cJSON__h
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
|
||||||
|
#define __WINDOWS__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
|
||||||
|
/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
|
||||||
|
|
||||||
|
CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
|
||||||
|
CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
|
||||||
|
CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
|
||||||
|
|
||||||
|
For *nix builds that support visibility attribute, you can define similar behavior by
|
||||||
|
|
||||||
|
setting default visibility to hidden by adding
|
||||||
|
-fvisibility=hidden (for gcc)
|
||||||
|
or
|
||||||
|
-xldscope=hidden (for sun cc)
|
||||||
|
to CFLAGS
|
||||||
|
|
||||||
|
then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CJSON_CDECL __cdecl
|
||||||
|
#define CJSON_STDCALL __stdcall
|
||||||
|
|
||||||
|
/* export symbols by default, this is necessary for copy pasting the C and header file */
|
||||||
|
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
|
||||||
|
#define CJSON_EXPORT_SYMBOLS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CJSON_HIDE_SYMBOLS)
|
||||||
|
#define CJSON_PUBLIC(type) type CJSON_STDCALL
|
||||||
|
#elif defined(CJSON_EXPORT_SYMBOLS)
|
||||||
|
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
|
||||||
|
#elif defined(CJSON_IMPORT_SYMBOLS)
|
||||||
|
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
|
||||||
|
#endif
|
||||||
|
#else /* !__WINDOWS__ */
|
||||||
|
#define CJSON_CDECL
|
||||||
|
#define CJSON_STDCALL
|
||||||
|
|
||||||
|
#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
|
||||||
|
#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
|
||||||
|
#else
|
||||||
|
#define CJSON_PUBLIC(type) type
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* project version */
|
||||||
|
#define CJSON_VERSION_MAJOR 1
|
||||||
|
#define CJSON_VERSION_MINOR 7
|
||||||
|
#define CJSON_VERSION_PATCH 13
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
/* cJSON Types: */
|
||||||
|
#define cJSON_Invalid (0)
|
||||||
|
#define cJSON_False (1 << 0)
|
||||||
|
#define cJSON_True (1 << 1)
|
||||||
|
#define cJSON_NULL (1 << 2)
|
||||||
|
#define cJSON_Number (1 << 3)
|
||||||
|
#define cJSON_String (1 << 4)
|
||||||
|
#define cJSON_Array (1 << 5)
|
||||||
|
#define cJSON_Object (1 << 6)
|
||||||
|
#define cJSON_Raw (1 << 7) /* raw json */
|
||||||
|
|
||||||
|
#define cJSON_IsReference 256
|
||||||
|
#define cJSON_StringIsConst 512
|
||||||
|
|
||||||
|
/* The cJSON structure: */
|
||||||
|
typedef struct cJSON
|
||||||
|
{
|
||||||
|
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
|
||||||
|
struct cJSON *next;
|
||||||
|
struct cJSON *prev;
|
||||||
|
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
|
||||||
|
struct cJSON *child;
|
||||||
|
|
||||||
|
/* The type of the item, as above. */
|
||||||
|
int type;
|
||||||
|
|
||||||
|
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
|
||||||
|
char *valuestring;
|
||||||
|
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
|
||||||
|
int valueint;
|
||||||
|
/* The item's number, if type==cJSON_Number */
|
||||||
|
double valuedouble;
|
||||||
|
|
||||||
|
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
|
||||||
|
char *string;
|
||||||
|
} cJSON;
|
||||||
|
|
||||||
|
typedef struct cJSON_Hooks
|
||||||
|
{
|
||||||
|
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
|
||||||
|
void *(CJSON_CDECL *malloc_fn)(size_t sz);
|
||||||
|
void (CJSON_CDECL *free_fn)(void *ptr);
|
||||||
|
} cJSON_Hooks;
|
||||||
|
|
||||||
|
typedef int cJSON_bool;
|
||||||
|
|
||||||
|
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
|
||||||
|
* This is to prevent stack overflows. */
|
||||||
|
#ifndef CJSON_NESTING_LIMIT
|
||||||
|
#define CJSON_NESTING_LIMIT 1000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* returns the version of cJSON as a string */
|
||||||
|
CJSON_PUBLIC(const char*) cJSON_Version(void);
|
||||||
|
|
||||||
|
/* Supply malloc, realloc and free functions to cJSON */
|
||||||
|
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
|
||||||
|
|
||||||
|
/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
|
||||||
|
/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
|
||||||
|
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||||
|
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
|
||||||
|
|
||||||
|
/* Render a cJSON entity to text for transfer/storage. */
|
||||||
|
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
|
||||||
|
/* Render a cJSON entity to text for transfer/storage without any formatting. */
|
||||||
|
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
|
||||||
|
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
|
||||||
|
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
|
||||||
|
/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
|
||||||
|
/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
|
||||||
|
/* Delete a cJSON entity and all subentities. */
|
||||||
|
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
|
||||||
|
|
||||||
|
/* Returns the number of items in an array (or object). */
|
||||||
|
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
|
||||||
|
/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
|
||||||
|
/* Get item "string" from object. Case insensitive. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
|
||||||
|
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
|
||||||
|
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
|
||||||
|
|
||||||
|
/* Check item type and return its value */
|
||||||
|
CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item);
|
||||||
|
CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item);
|
||||||
|
|
||||||
|
/* These functions check the type of an item */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
|
||||||
|
|
||||||
|
/* These calls create a cJSON item of the appropriate type. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
|
||||||
|
/* raw json */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
|
||||||
|
|
||||||
|
/* Create a string where valuestring references a string so
|
||||||
|
* it will not be freed by cJSON_Delete */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
|
||||||
|
/* Create an object/array that only references it's elements so
|
||||||
|
* they will not be freed by cJSON_Delete */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
|
||||||
|
|
||||||
|
/* These utilities create an Array of count items.
|
||||||
|
* The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
|
||||||
|
|
||||||
|
/* Append item to the specified array/object. */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
|
||||||
|
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
|
||||||
|
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
|
||||||
|
* writing to `item->string` */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
|
||||||
|
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
|
||||||
|
|
||||||
|
/* Remove/Detach items from Arrays/Objects. */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
|
||||||
|
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||||
|
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
|
||||||
|
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
|
||||||
|
|
||||||
|
/* Update array items. */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
|
||||||
|
|
||||||
|
/* Duplicate a cJSON item */
|
||||||
|
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
||||||
|
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||||
|
* need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||||
|
* The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||||
|
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||||
|
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
||||||
|
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
||||||
|
|
||||||
|
/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
|
||||||
|
* The input pointer json cannot point to a read-only address area, such as a string constant,
|
||||||
|
* but should point to a readable and writable adress area. */
|
||||||
|
CJSON_PUBLIC(void) cJSON_Minify(char *json);
|
||||||
|
|
||||||
|
/* Helper functions for creating and adding items to an object at the same time.
|
||||||
|
* They return the added item or NULL on failure. */
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
|
||||||
|
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
|
||||||
|
|
||||||
|
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
|
||||||
|
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
|
||||||
|
/* helper for the cJSON_SetNumberValue macro */
|
||||||
|
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
|
||||||
|
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
|
||||||
|
/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
|
||||||
|
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
|
||||||
|
|
||||||
|
/* Macro for iterating over an array or object */
|
||||||
|
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
|
||||||
|
|
||||||
|
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
|
||||||
|
CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
|
||||||
|
CJSON_PUBLIC(void) cJSON_free(void *object);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
5016
vendor/confini.c
vendored
Normal file
5016
vendor/confini.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
547
vendor/confini.h
vendored
Normal file
547
vendor/confini.h
vendored
Normal file
|
@ -0,0 +1,547 @@
|
||||||
|
/* -*- 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 */
|
||||||
|
|
16123
vendor/mongoose.c
vendored
Normal file
16123
vendor/mongoose.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
6277
vendor/mongoose.h
vendored
Normal file
6277
vendor/mongoose.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
6440
vendor/mpack.c
vendored
Normal file
6440
vendor/mpack.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
7151
vendor/mpack.h
vendored
Normal file
7151
vendor/mpack.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue