Merge branch 'rewrite' into dev
This commit is contained in:
commit
b68cdd224e
129 changed files with 40046 additions and 3787 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -1,5 +1,7 @@
|
|||
build
|
||||
cmake-build-debug
|
||||
.idea
|
||||
build/
|
||||
docs/
|
||||
|
||||
sql/*.h
|
||||
tests/testing_tmp/
|
||||
tests/testing_bak/
|
||||
|
||||
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
|
|
@ -1,71 +1,28 @@
|
|||
cmake_minimum_required (VERSION 3.2)
|
||||
cmake_minimum_required (VERSION 3.7)
|
||||
project(core)
|
||||
|
||||
include(CheckIncludeFileCXX)
|
||||
add_executable(core main.c)
|
||||
|
||||
check_include_file_cxx(any HAS_ANY)
|
||||
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_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wpedantic -Werror -Wall -Wextra -luuid -lsqlite3 -g -fprofile-arcs -ftest-coverage")
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
string(LENGTH "${CMAKE_SOURCE_DIR}/" SOURCE_PATH_SIZE)
|
||||
add_definitions("-DSOURCE_PATH_SIZE=${SOURCE_PATH_SIZE}")
|
||||
|
||||
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -Wall -Wextra -Werror -g -latomic")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||
add_definitions("-DMG_ENABLE_EXTRA_ERRORS_DESC")
|
||||
|
||||
##########
|
||||
# If you include the drogon source code locally in your project, use this method to add drogon
|
||||
# add_subdirectory(drogon)
|
||||
# set(Drogon_DIR ${PROJECT_BINARY_DIR}/drogon)
|
||||
# find_package(Drogon CONFIG REQUIRED NO_DEFAULT_PATH)
|
||||
# include_directories(${DROGON_INCLUDE_DIRS})
|
||||
# link_libraries(${DROGON_LIBRARIES})
|
||||
##########
|
||||
aux_source_directory(vendor VENDOR_SRC) # vendor first to put their warnings on top
|
||||
aux_source_directory(. SRC_DIR)
|
||||
aux_source_directory(models MODELS_SRC)
|
||||
aux_source_directory(helpers HELPERS_SRC)
|
||||
aux_source_directory(handlers HANDLERS_SRC)
|
||||
aux_source_directory(endpoints ENDPOINTS_SRC)
|
||||
|
||||
# find_package(Drogon CONFIG REQUIRED)
|
||||
# include_directories(${DROGON_INCLUDE_DIRS})
|
||||
# link_libraries(${DROGON_LIBRARIES})
|
||||
configure_file("core.ini" "core.ini" COPYONLY)
|
||||
|
||||
if(CMAKE_CXX_STANDARD LESS 17)
|
||||
#With C++14, use boost to support any and string_view
|
||||
message(STATUS "use c++14")
|
||||
find_package(Boost 1.61.0 REQUIRED)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
else()
|
||||
message(STATUS "use c++17")
|
||||
endif()
|
||||
target_sources(core PRIVATE ${VENDOR_SRC} ${SRC_DIR} ${HANDLERS_SRC} ${HELPERS_SRC} ${MODELS_SRC} ${ENDPOINTS_SRC})
|
||||
target_include_directories(core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
target_include_directories(core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor)
|
||||
|
||||
aux_source_directory(./ SRC_DIR)
|
||||
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)
|
||||
foreach(cspFile ${SCP_LIST})
|
||||
message(STATUS "cspFile:" ${cspFile})
|
||||
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)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_executable(core ${SRC_DIR} ${CTL_SRC} ${FILTER_SRC} ${VIEWSRC} ${PLUGIN_SRC} ${MODEL_SRC} ${HELPER_SRC})
|
||||
|
||||
add_subdirectory(drogon)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
|
||||
|
||||
add_custom_target(migrations
|
||||
COMMAND ./compile_migrations.sh
|
||||
|
@ -73,23 +30,32 @@ add_custom_target(migrations
|
|||
)
|
||||
|
||||
add_custom_target(run
|
||||
COMMAND core
|
||||
COMMAND ./core start
|
||||
DEPENDS core
|
||||
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
|
||||
)
|
||||
add_custom_target(debug
|
||||
COMMAND valgrind ./core
|
||||
COMMAND valgrind ./core start
|
||||
DEPENDS core
|
||||
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
|
||||
)
|
||||
add_custom_target(debug-full
|
||||
COMMAND valgrind --leak-check=full --show-leak-kinds=all ./core
|
||||
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}
|
||||
)
|
||||
|
||||
add_custom_target(test
|
||||
COMMAND ./run_tests.sh ${CMAKE_BINARY_DIR}/core ${CMAKE_SOURCE_DIR}/config.testing.json
|
||||
COMMAND ./run_tests.sh ${CMAKE_BINARY_DIR}/core "dev"
|
||||
DEPENDS core
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tavern_tests
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||
)
|
||||
add_custom_target(coverage
|
||||
COMMAND gcovr -s --root ${CMAKE_SOURCE_DIR} -e ${CMAKE_SOURCE_DIR}/vendor --html-details ${CMAKE_BINARY_DIR}/coverage.html --html-title "Emgauwa Core Coverage" ${CMAKE_BINARY_DIR}/CMakeFiles/core.dir
|
||||
DEPENDS test
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
|
141
command.c
Normal file
141
command.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <command.h>
|
||||
#include <mpack.h>
|
||||
#include <logger.h>
|
||||
#include <helpers.h>
|
||||
#include <enums.h>
|
||||
#include <models/controller.h>
|
||||
|
||||
int
|
||||
command_set_relay_schedule(relay_t *relay)
|
||||
{
|
||||
controller_t *controller = controller_get_by_id(relay->controller_id);
|
||||
if(!controller)
|
||||
{
|
||||
LOG_ERROR("couldn't find controller\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char* payload;
|
||||
size_t payload_size;
|
||||
mpack_writer_t writer;
|
||||
mpack_writer_init_growable(&writer, &payload, &payload_size);
|
||||
|
||||
// 3 = code, relay num, relay name, schedules(array)
|
||||
mpack_start_map(&writer, 3);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_CODE);
|
||||
mpack_write_u8(&writer, COMMAND_CODE_SET_SCHEDULE);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_RELAY_NUM);
|
||||
mpack_write_u8(&writer, relay->number);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_SCHEDULES_ARRAY);
|
||||
// 7 = days of week
|
||||
mpack_start_array(&writer, 7);
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
uint16_t *periods_blob = schedule_periods_to_blob(relay->schedules[i]);
|
||||
uint16_t periods_count = periods_blob[0];
|
||||
|
||||
// 3 = code, relaynum, schedules(array)
|
||||
mpack_start_map(&writer, 3);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_PERIODS_COUNT);
|
||||
mpack_write_u16(&writer, periods_count);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_SCHEDULE_ID);
|
||||
mpack_write_bin(&writer, (char*)relay->schedules[0]->uid, sizeof(uuid_t));
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_PERIODS_BLOB);
|
||||
// periods + 1 to skip length in periods[0]
|
||||
// periods_count * 2 because each uint16_t is a timestamp. 2 are start and end
|
||||
mpack_write_bin(&writer, (char*)(periods_blob + 1), sizeof(uint16_t) * periods_count * 2);
|
||||
|
||||
mpack_finish_map(&writer);
|
||||
|
||||
free(periods_blob);
|
||||
}
|
||||
mpack_finish_array(&writer);
|
||||
|
||||
mpack_finish_map(&writer);
|
||||
|
||||
// finish writing
|
||||
if (mpack_writer_destroy(&writer) != mpack_ok)
|
||||
{
|
||||
LOG_ERROR("an error occurred encoding the data");
|
||||
controller_free(controller);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int result = command_send(controller, COMMAND_CODE_SET_SCHEDULE, payload, payload_size);
|
||||
|
||||
controller_free(controller);
|
||||
free(payload);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
command_set_controller_name(controller_t *controller)
|
||||
{
|
||||
char* payload;
|
||||
size_t payload_size;
|
||||
mpack_writer_t writer;
|
||||
mpack_writer_init_growable(&writer, &payload, &payload_size);
|
||||
|
||||
// write the example on the msgpack homepage
|
||||
mpack_start_map(&writer, 2);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_CODE);
|
||||
mpack_write_u8(&writer, COMMAND_CODE_SET_NAME);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_NAME);
|
||||
mpack_write_cstr(&writer, controller->name);
|
||||
|
||||
mpack_finish_map(&writer);
|
||||
|
||||
// finish writing
|
||||
if (mpack_writer_destroy(&writer) != mpack_ok)
|
||||
{
|
||||
LOG_ERROR("an error occurred encoding the data");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int result = command_send(controller, COMMAND_CODE_SET_NAME, payload, payload_size);
|
||||
|
||||
free(payload);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
command_send(controller_t *controller, int command_code, char *payload, uint32_t payload_size)
|
||||
{
|
||||
LOG_DEBUG("commanding %d\n", command_code);
|
||||
|
||||
int bytes_transferred;
|
||||
|
||||
int fd_controller = helper_connect_tcp_server(controller->ip, controller->port);
|
||||
|
||||
if(fd_controller == -1)
|
||||
{
|
||||
LOG_ERROR("can't open command socket %s:%d\n", controller->ip, controller->port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if((bytes_transferred = send(fd_controller, &payload_size, sizeof(payload_size), 0)) <= 0)
|
||||
{
|
||||
LOG_ERROR("error during sending size\n");
|
||||
return 1;
|
||||
}
|
||||
if((bytes_transferred = send(fd_controller, payload, payload_size, 0)) <= 0)
|
||||
{
|
||||
LOG_ERROR("error during sending\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(fd_controller);
|
||||
return 0;
|
||||
}
|
|
@ -6,7 +6,7 @@ migration_num=0;
|
|||
|
||||
while [ -f sql/migration_$migration_num.sql ]
|
||||
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++));
|
||||
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;
|
||||
}
|
||||
|
23
config.cc
23
config.cc
|
@ -1,23 +0,0 @@
|
|||
#include "config.h"
|
||||
|
||||
namespace config
|
||||
{
|
||||
char version[] = "0.0.1";
|
||||
|
||||
uint16_t discover_max_client_backlog = 20;
|
||||
uint16_t discover_port_dev = 4421;
|
||||
uint16_t discover_port_test = 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
|
12
config.json
12
config.json
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"listeners": [
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"port": 5000,
|
||||
"https": false
|
||||
}
|
||||
],
|
||||
"custom_config": {
|
||||
"db_name": "core.sqlite"
|
||||
}
|
||||
}
|
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,12 +0,0 @@
|
|||
{
|
||||
"listeners": [
|
||||
{
|
||||
"address": "0.0.0.0",
|
||||
"port": 5000,
|
||||
"https": false
|
||||
}
|
||||
],
|
||||
"custom_config": {
|
||||
"db_name": "core.testing.sqlite"
|
||||
}
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
#include <models/controller_dbo.h>
|
||||
#include <config.h>
|
||||
#include <enums.h>
|
||||
#include <mpack.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_str)
|
||||
{
|
||||
uuid_t controller_id;
|
||||
if(uuid_parse(controller_id_str.c_str(), controller_id))
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
controller_dbo **controllers = controller_dbo::get_by_simple("id", controller_id, (intptr_t) &sqlite3_bind_blob, sizeof(uuid_t));
|
||||
|
||||
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::get_one_by_tag(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
const std::string &controller_tag)
|
||||
{
|
||||
controller_dbo **controllers = controller_dbo::get_by_simple("tag", controller_tag.c_str(), (intptr_t) &sqlite3_bind_text, -1);
|
||||
|
||||
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_str)
|
||||
{
|
||||
uuid_t controller_id;
|
||||
if(uuid_parse(controller_id_str.c_str(), controller_id))
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
controller_dbo **controllers = controller_dbo::get_by_simple("id", controller_id, (intptr_t) &sqlite3_bind_blob, sizeof(uuid_t));
|
||||
|
||||
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_str)
|
||||
{
|
||||
Json::Value body = *req->getJsonObject();
|
||||
|
||||
uuid_t controller_id;
|
||||
if(uuid_parse(controller_id_str.c_str(), controller_id))
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
controller_dbo **controllers = controller_dbo::get_by_simple("id", controller_id, (intptr_t) &sqlite3_bind_blob, sizeof(uuid_t));
|
||||
|
||||
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())
|
||||
{
|
||||
char* data;
|
||||
size_t size;
|
||||
mpack_writer_t writer;
|
||||
mpack_writer_init_growable(&writer, &data, &size);
|
||||
|
||||
// write the example on the msgpack homepage
|
||||
mpack_start_map(&writer, 2);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_CODE);
|
||||
mpack_write_u8(&writer, config::command_code_set_name);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_NAME);
|
||||
mpack_write_cstr(&writer, controllers[0]->name);
|
||||
|
||||
mpack_finish_map(&writer);
|
||||
|
||||
// finish writing
|
||||
if (mpack_writer_destroy(&writer) != mpack_ok) {
|
||||
LOG_ERROR << "an error occurred encoding the data";
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
controllers[0]->command(config::command_code_set_name, data, size);
|
||||
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,33 +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::get_one_by_tag, "/tag/{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_str);
|
||||
static void get_one_by_tag(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& controller_tag);
|
||||
static void delete_one_by_id(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& controller_id_str);
|
||||
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_str);
|
||||
static void get_relays_one_by_id_and_num(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& controller_id_str, 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_str, int relay_num);
|
||||
};
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <mpack.h>
|
||||
#include <config.h>
|
||||
#include <helpers.h>
|
||||
#include <models/controller_dbo.h>
|
||||
#include "api_v1_controllers.h"
|
||||
|
||||
using namespace api::v1;
|
||||
|
||||
enum DISCOVERY_MAPPING
|
||||
{
|
||||
DISCOVERY_MAPPING_ID = 0,
|
||||
DISCOVERY_MAPPING_NAME = 1,
|
||||
DISCOVERY_MAPPING_COMMAND_PORT = 2,
|
||||
DISCOVERY_MAPPING_RELAY_COUNT = 3,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int16_t payload[1];
|
||||
payload[0] = discover_server_port;
|
||||
|
||||
if(helpers::send_udp_broadcast("255.255.255.255", config::discover_port_dev, payload, sizeof(payload)) < 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];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t payload_length;
|
||||
|
||||
if(recv(client_fd, &payload_length, sizeof(payload_length), 0) <= 0)
|
||||
{
|
||||
LOG_ERROR << "Error Receiving header from client";
|
||||
continue;
|
||||
}
|
||||
|
||||
char *answer_payload = (char*)malloc((payload_length));
|
||||
ssize_t bytes_transferred;
|
||||
|
||||
if((bytes_transferred = 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;
|
||||
}
|
||||
|
||||
uuid_t discovered_id;
|
||||
|
||||
mpack_tree_t tree;
|
||||
mpack_tree_init_data(&tree, answer_payload, payload_length);
|
||||
mpack_tree_parse(&tree);
|
||||
mpack_node_t root = mpack_tree_root(&tree);
|
||||
|
||||
memcpy(discovered_id, mpack_node_data(mpack_node_map_uint(root, DISCOVERY_MAPPING_ID)), sizeof(uuid_t));
|
||||
|
||||
uint16_t discovered_command_port = mpack_node_u16(mpack_node_map_uint(root, DISCOVERY_MAPPING_COMMAND_PORT));
|
||||
uint8_t discovered_relay_count = mpack_node_u8(mpack_node_map_uint(root, DISCOVERY_MAPPING_RELAY_COUNT));
|
||||
const char *discovered_name = mpack_node_str(mpack_node_map_uint(root, DISCOVERY_MAPPING_NAME));
|
||||
|
||||
bool found_discovered_in_list = false;
|
||||
|
||||
for(int i = 0; known_controllers[i] != nullptr; i++)
|
||||
{
|
||||
if(!found_discovered_in_list)
|
||||
{
|
||||
if(uuid_compare(known_controllers[i]->id, discovered_id) == 0)
|
||||
{
|
||||
known_controllers[i]->active = true;
|
||||
strncpy(known_controllers[i]->name, discovered_name, 128);
|
||||
known_controllers[i]->name[127] = '\0';
|
||||
known_controllers[i]->port = discovered_command_port;
|
||||
known_controllers[i]->relay_count = discovered_relay_count;
|
||||
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));
|
||||
memcpy(discovered_controller.id, discovered_id, sizeof(uuid_t));
|
||||
strcpy(discovered_controller.name, discovered_name);
|
||||
discovered_controller.relay_count = discovered_relay_count;
|
||||
discovered_controller.port = discovered_command_port;
|
||||
discovered_controller.active = true;
|
||||
|
||||
discovered_controller.insert();
|
||||
}
|
||||
mpack_tree_destroy(&tree);
|
||||
free(answer_payload);
|
||||
|
||||
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,268 +0,0 @@
|
|||
#include <netdb.h>
|
||||
#include <models/relay_dbo.h>
|
||||
#include <helpers.h>
|
||||
#include <mpack.h>
|
||||
#include <models/controller_dbo.h>
|
||||
#include <models/schedule_dbo.h>
|
||||
#include <models/junction_tag_dbo.h>
|
||||
#include <models/tag_dbo.h>
|
||||
#include <config.h>
|
||||
#include <enums.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_str)
|
||||
{
|
||||
uuid_t controller_id;
|
||||
if(uuid_parse(controller_id_str.c_str(), controller_id))
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
relay_dbo **all_controller_relays = relay_dbo::get_by_simple("controller_id", (void *) controller_id, (intptr_t) sqlite3_bind_blob, sizeof(uuid_t));
|
||||
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_str,
|
||||
int relay_num)
|
||||
{
|
||||
uuid_t controller_id;
|
||||
if(uuid_parse(controller_id_str.c_str(), controller_id))
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
relay_dbo *relay = relay_dbo::get_relay_for_controller(controller_id, 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_str,
|
||||
int relay_num)
|
||||
{
|
||||
uuid_t controller_id;
|
||||
if(uuid_parse(controller_id_str.c_str(), controller_id))
|
||||
{
|
||||
LOG_DEBUG << "bad uuid";
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
if(!relay_dbo::valid_num_for_controller(controller_id, relay_num))
|
||||
{
|
||||
LOG_DEBUG << "invalid num for controller";
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
Json::Value body = *req->getJsonObject();
|
||||
bool set_name = body["name"].type() == Json::ValueType::stringValue;
|
||||
bool set_tags = body["tags"].type() == Json::ValueType::arrayValue;
|
||||
bool set_schedules = body["schedules"].type() == Json::ValueType::arrayValue;
|
||||
bool set_active_schedule = body["active_schedule"].type() == Json::ValueType::objectValue;
|
||||
|
||||
relay_dbo *relay = relay_dbo::get_relay_for_controller(controller_id, relay_num);
|
||||
|
||||
schedule_dbo **schedule_list;
|
||||
schedule_dbo *active_schedule;
|
||||
schedule_dbo *schedules[7];
|
||||
|
||||
if(set_schedules)
|
||||
{
|
||||
uuid_t schedules_ids[7];
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
if(schedule_dbo::parse_uid(body["schedules"][i]["id"].asCString(), schedules_ids[i]))
|
||||
{
|
||||
LOG_DEBUG << "parse_uid failed for schedule " << i;
|
||||
auto res = drogon::HttpResponse::newHttpResponse();
|
||||
res->setStatusCode(k400BadRequest);
|
||||
callback(res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
schedule_list = schedule_dbo::get_by_simple("uid", schedules_ids[i], (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
|
||||
schedules[i] = schedule_list[0];
|
||||
free(schedule_list);
|
||||
}
|
||||
}
|
||||
|
||||
if(set_active_schedule)
|
||||
{
|
||||
uuid_t active_schedule_id;
|
||||
if(schedule_dbo::parse_uid(body["active_schedule"]["id"].asCString(), active_schedule_id))
|
||||
{
|
||||
LOG_DEBUG << "bad active_schedule uuid";
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
schedule_list = schedule_dbo::get_by_simple("uid", active_schedule_id, (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
|
||||
active_schedule = schedule_list[0];
|
||||
free(schedule_list);
|
||||
}
|
||||
|
||||
if(!relay)
|
||||
{
|
||||
relay = new relay_dbo();
|
||||
relay->number = relay_num;
|
||||
uuid_copy(relay->controller_id, controller_id);
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
relay->schedules[i] = schedule_dbo::get_by_id_or_off(0);
|
||||
}
|
||||
}
|
||||
|
||||
if(set_name)
|
||||
{
|
||||
strncpy(relay->name, body["name"].asCString(), 127);
|
||||
}
|
||||
|
||||
if(set_tags)
|
||||
{
|
||||
junction_tag_dbo::remove_for_relay(relay->id);
|
||||
|
||||
for(int i = 0; i < body["tags"].size(); ++i)
|
||||
{
|
||||
const char *tag = body["tags"][i].asCString();
|
||||
int tag_id = tag_dbo::get_id(tag);
|
||||
if(tag_id == 0)
|
||||
{
|
||||
tag_dbo::save(tag_id, tag);
|
||||
tag_id = tag_dbo::get_id(tag);
|
||||
}
|
||||
junction_tag_dbo::insert(tag_id, relay->id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(set_schedules)
|
||||
{
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
relay->schedules[i] = schedules[i];
|
||||
}
|
||||
relay->active_schedule = schedules[helpers::get_day_of_week()];
|
||||
}
|
||||
|
||||
if(set_active_schedule)
|
||||
{
|
||||
relay->schedules[helpers::get_day_of_week()] = active_schedule;
|
||||
relay->active_schedule = active_schedule;
|
||||
}
|
||||
|
||||
if(!relay->save())
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
callback(resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto controllers = controller_dbo::get_by_simple("id", controller_id, (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
|
||||
|
||||
char* data;
|
||||
size_t size;
|
||||
mpack_writer_t writer;
|
||||
mpack_writer_init_growable(&writer, &data, &size);
|
||||
|
||||
// 3 = code, relay num, relay name, schedules(array)
|
||||
mpack_start_map(&writer, 3);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_CODE);
|
||||
mpack_write_u8(&writer, config::command_code_set_schedule);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_RELAY_NUM);
|
||||
mpack_write_u8(&writer, relay->number);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_SCHEDULES_ARRAY);
|
||||
// 7 = days of week
|
||||
mpack_start_array(&writer, 7);
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
uint16_t *periods = relay->schedules[i]->periods->to_blob();
|
||||
uint16_t periods_count = periods[0];
|
||||
|
||||
// 3 = code, relaynum, schedules(array)
|
||||
mpack_start_map(&writer, 3);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_PERIODS_COUNT);
|
||||
mpack_write_u16(&writer, periods_count);
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_SCHEDULE_ID);
|
||||
mpack_write_bin(&writer, (char*)relay->schedules[0]->uid, sizeof(uuid_t));
|
||||
|
||||
mpack_write_uint(&writer, COMMAND_MAPPING_PERIODS_BLOB);
|
||||
// periods + 1 to skip length in periods[0]
|
||||
// periods_count * 2 because each uint16_t is a timestamp. 2 are start and end
|
||||
mpack_write_bin(&writer, (char*)(periods + 1), sizeof(uint16_t) * periods_count * 2);
|
||||
|
||||
mpack_finish_map(&writer);
|
||||
|
||||
free(periods);
|
||||
}
|
||||
mpack_finish_array(&writer);
|
||||
|
||||
mpack_finish_map(&writer);
|
||||
|
||||
// finish writing
|
||||
if (mpack_writer_destroy(&writer) != mpack_ok) {
|
||||
LOG_ERROR << "an error occurred encoding the data";
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
controllers[0]->command(config::command_code_set_name, data, size);
|
||||
|
||||
auto resp = HttpResponse::newHttpJsonResponse(relay->to_json());
|
||||
callback(resp);
|
||||
|
||||
controller_dbo::free_list(controllers);
|
||||
}
|
||||
|
||||
delete relay;
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
#include <netdb.h>
|
||||
#include <models/relay_dbo.h>
|
||||
#include <models/tag_dbo.h>
|
||||
#include <models/junction_tag_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);
|
||||
}
|
||||
|
||||
void
|
||||
relays::get_by_tag(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
const std::string &tag)
|
||||
{
|
||||
int tag_id = tag_dbo::get_id(tag.c_str());
|
||||
int *relays_ids = junction_tag_dbo::get_relays_for_tag_id(tag_id);
|
||||
if(relays_ids == nullptr)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
Json::Value relays_json(Json::arrayValue);
|
||||
|
||||
for(int i = 0; relays_ids[i] != 0; ++i)
|
||||
{
|
||||
relay_dbo *relay = relay_dbo::get_by_id(relays_ids[i]);
|
||||
if(relay)
|
||||
{
|
||||
relays_json.append(relay->to_json());
|
||||
}
|
||||
}
|
||||
|
||||
auto resp = HttpResponse::newHttpJsonResponse(relays_json);
|
||||
callback(resp);
|
||||
free(relays_ids);
|
||||
}
|
|
@ -1,17 +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_ADD(relays::get_by_tag, "/tag/{1}", Get, Options);
|
||||
METHOD_LIST_END
|
||||
|
||||
static void get_all(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback);
|
||||
static void get_by_tag(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& tag);
|
||||
};
|
||||
}
|
|
@ -1,277 +0,0 @@
|
|||
#include <netdb.h>
|
||||
#include <models/schedule_dbo.h>
|
||||
#include <models/tag_dbo.h>
|
||||
#include <models/junction_tag_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_str)
|
||||
{
|
||||
uuid_t schedule_id;
|
||||
if(schedule_dbo::parse_uid(schedule_id_str.c_str(), schedule_id))
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
|
||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("uid", schedule_id, (intptr_t) &sqlite3_bind_blob, sizeof(uuid_t));
|
||||
|
||||
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_str)
|
||||
{
|
||||
if(strcmp(schedule_id_str.c_str(), "off") == 0 || strcmp(schedule_id_str.c_str(), "on") == 0)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k403Forbidden);
|
||||
callback(resp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
uuid_t schedule_id;
|
||||
if(schedule_dbo::parse_uid(schedule_id_str.c_str(), schedule_id))
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("uid", schedule_id, (intptr_t) &sqlite3_bind_blob, sizeof(uuid_t));
|
||||
|
||||
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();
|
||||
|
||||
bool set_name = body["name"].type() == Json::ValueType::stringValue;
|
||||
bool set_tags = body["tags"].type() == Json::ValueType::arrayValue;
|
||||
//bool set_periods = body["periods"].type() == Json::ValueType::arrayValue;
|
||||
|
||||
if(!set_name || !set_name)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
schedule_dbo new_schedule{};
|
||||
|
||||
strncpy(new_schedule.name, body["name"].asCString(), 127);
|
||||
new_schedule.name[127] = '\0';
|
||||
uuid_generate(new_schedule.uid);
|
||||
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::post_list(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback)
|
||||
{
|
||||
Json::Value body = *req->jsonObject();
|
||||
|
||||
Json::Value schedules_json(Json::arrayValue);
|
||||
|
||||
for(int i = 0; i < body.size(); ++i)
|
||||
{
|
||||
bool set_name = body[i]["name"].type() == Json::ValueType::stringValue;
|
||||
bool set_tags = body[i]["tags"].type() == Json::ValueType::arrayValue;
|
||||
//bool set_periods = body[i]["periods"].type() == Json::ValueType::arrayValue;
|
||||
|
||||
if(!set_name || !set_name)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
schedule_dbo new_schedule{};
|
||||
|
||||
strncpy(new_schedule.name, body[i]["name"].asCString(), 127);
|
||||
new_schedule.name[127] = '\0';
|
||||
uuid_generate(new_schedule.uid);
|
||||
new_schedule.periods = helpers::parse_periods(body[i]["periods"]);
|
||||
|
||||
if(new_schedule.insert())
|
||||
{
|
||||
schedules_json.append(new_schedule.to_json());
|
||||
}
|
||||
}
|
||||
|
||||
auto resp = HttpResponse::newHttpJsonResponse(schedules_json);
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
void
|
||||
schedules::put_one_by_id(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
const std::string &schedule_id_str)
|
||||
{
|
||||
uuid_t schedule_id;
|
||||
if(schedule_dbo::parse_uid(schedule_id_str.c_str(), schedule_id))
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k400BadRequest);
|
||||
callback(resp);
|
||||
return;
|
||||
}
|
||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("uid", schedule_id, (intptr_t) &sqlite3_bind_blob, sizeof(uuid_t));
|
||||
|
||||
if(schedules[0] == nullptr)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k404NotFound);
|
||||
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
Json::Value body = *req->jsonObject();
|
||||
|
||||
bool set_name = body["name"].type() == Json::ValueType::stringValue;
|
||||
bool set_tags = body["tags"].type() == Json::ValueType::arrayValue;
|
||||
bool set_periods = body["periods"].type() == Json::ValueType::arrayValue;
|
||||
|
||||
if(set_name)
|
||||
{
|
||||
strncpy(schedules[0]->name, body["name"].asCString(), 127);
|
||||
schedules[0]->name[127] = '\0';
|
||||
}
|
||||
|
||||
if(set_periods)
|
||||
{
|
||||
// if neither "off" nor "on" allow overwrite of periods
|
||||
if(strcmp(schedule_id_str.c_str(), "off") && strcmp(schedule_id_str.c_str(), "on"))
|
||||
{
|
||||
delete schedules[0]->periods;
|
||||
schedules[0]->periods = helpers::parse_periods(body["periods"]);
|
||||
}
|
||||
}
|
||||
|
||||
if(set_tags)
|
||||
{
|
||||
junction_tag_dbo::remove_for_schedule(schedules[0]->id);
|
||||
|
||||
for(int i = 0; i < body["tags"].size(); ++i)
|
||||
{
|
||||
const char *tag = body["tags"][i].asCString();
|
||||
int tag_id = tag_dbo::get_id(tag);
|
||||
if(tag_id == 0)
|
||||
{
|
||||
tag_dbo::save(tag_id, tag);
|
||||
tag_id = tag_dbo::get_id(tag);
|
||||
}
|
||||
junction_tag_dbo::insert(tag_id, 0, schedules[0]->id);
|
||||
}
|
||||
}
|
||||
|
||||
if(!schedules[0]->update())
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
callback(resp);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto resp = HttpResponse::newHttpJsonResponse(schedules[0]->to_json());
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
schedule_dbo::free_list(schedules);
|
||||
}
|
||||
|
||||
void
|
||||
schedules::get_by_tag(const HttpRequestPtr &req, std::function<void(const HttpResponsePtr &)> &&callback,
|
||||
const std::string &tag)
|
||||
{
|
||||
int tag_id = tag_dbo::get_id(tag.c_str());
|
||||
int *schedules_ids = junction_tag_dbo::get_schedules_for_tag_id(tag_id);
|
||||
if(schedules_ids == nullptr)
|
||||
{
|
||||
auto resp = HttpResponse::newHttpResponse();
|
||||
resp->setStatusCode(k500InternalServerError);
|
||||
callback(resp);
|
||||
}
|
||||
|
||||
Json::Value schedules_json(Json::arrayValue);
|
||||
|
||||
for(int i = 0; schedules_ids[i] != 0; ++i)
|
||||
{
|
||||
schedule_dbo *schedule = schedule_dbo::get_by_id(schedules_ids[i]);
|
||||
if(schedule)
|
||||
{
|
||||
schedules_json.append(schedule->to_json());
|
||||
}
|
||||
}
|
||||
|
||||
auto resp = HttpResponse::newHttpJsonResponse(schedules_json);
|
||||
callback(resp);
|
||||
free(schedules_ids);
|
||||
}
|
|
@ -1,31 +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::post_list, "/list", Post, Options, "filters::json_required");
|
||||
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(schedules::get_by_tag, "/tag/{1}", Get, Options);
|
||||
//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 post_list(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);
|
||||
static void get_by_tag(const HttpRequestPtr& req, std::function<void (const HttpResponsePtr &)> &&callback, const std::string& tag);
|
||||
//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);
|
||||
};
|
||||
}
|
7
core.ini
Normal file
7
core.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
[core]
|
||||
server-port = 5000
|
||||
|
||||
: 4421 for dev-env; 4420 for testing-env; 4419 for prod-env; 4422 for testing
|
||||
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;
|
||||
}
|
1
drogon
1
drogon
|
@ -1 +0,0 @@
|
|||
Subproject commit 543d1a8c8062b3873ef89c64ffd7394c6dd7c7e8
|
48
endpoints/api_v1_controllers.c
Normal file
48
endpoints/api_v1_controllers.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <constants.h>
|
||||
#include <endpoints/api_v1_controllers.h>
|
||||
#include <logger.h>
|
||||
#include <models/junction_tag.h>
|
||||
#include <models/controller.h>
|
||||
#include <models/tag.h>
|
||||
|
||||
void
|
||||
api_v1_controllers_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)args;
|
||||
(void)hm;
|
||||
controller_t** all_controllers = controller_get_all();
|
||||
|
||||
cJSON *json = cJSON_CreateArray();
|
||||
|
||||
for(int i = 0; all_controllers[i] != NULL; ++i)
|
||||
{
|
||||
cJSON *json_controller = controller_to_json(all_controllers[i]);
|
||||
|
||||
cJSON_AddItemToArray(json, json_controller);
|
||||
}
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print controllers json\n");
|
||||
|
||||
static const char content[] = "failed to print json for controllers";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
controller_free_list(all_controllers);
|
||||
}
|
240
endpoints/api_v1_controllers_STR.c
Normal file
240
endpoints/api_v1_controllers_STR.c
Normal file
|
@ -0,0 +1,240 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <command.h>
|
||||
#include <constants.h>
|
||||
#include <endpoints/api_v1_controllers.h>
|
||||
#include <logger.h>
|
||||
#include <models/controller.h>
|
||||
#include <models/tag.h>
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
uuid_t target_uid;
|
||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||
{
|
||||
LOG_DEBUG("failed to unparse uid\n");
|
||||
|
||||
static const char content[] = "given id was invalid";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
controller_t* controller = controller_get_by_uid(target_uid);
|
||||
|
||||
if(!controller)
|
||||
{
|
||||
LOG_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no controller for id found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json = controller_to_json(controller);
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print controller json\n");
|
||||
|
||||
static const char content[] = "failed to print json for controller";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
controller_free(controller);
|
||||
}
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_PUT(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
uuid_t target_uid;
|
||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||
{
|
||||
LOG_DEBUG("failed to unparse uid\n");
|
||||
|
||||
static const char content[] = "given id was invalid";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
controller_t* controller = controller_get_by_uid(target_uid);
|
||||
|
||||
if(!controller)
|
||||
{
|
||||
LOG_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no controller for id found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json = cJSON_ParseWithLength(hm->body.p, hm->body.len);
|
||||
|
||||
if(json == NULL)
|
||||
{
|
||||
const char *error_ptr = cJSON_GetErrorPtr();
|
||||
if (error_ptr != NULL)
|
||||
{
|
||||
LOG_DEBUG("error before: %s\n", error_ptr);
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
|
||||
static const char content[] = "no valid json was supplied";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||
if(cJSON_IsString(json_name) && json_name->valuestring)
|
||||
{
|
||||
strncpy(controller->name, json_name->valuestring, MAX_NAME_LENGTH);
|
||||
controller->name[MAX_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
cJSON *json_ip = cJSON_GetObjectItemCaseSensitive(json, "ip");
|
||||
if(cJSON_IsString(json_ip) && json_ip->valuestring)
|
||||
{
|
||||
strncpy(controller->ip, json_ip->valuestring, IP_LENGTH);
|
||||
controller->ip[IP_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
if(controller_save(controller))
|
||||
{
|
||||
LOG_ERROR("failed to save controller\n");
|
||||
free(controller);
|
||||
cJSON_Delete(json);
|
||||
|
||||
static const char content[] = "failed to save controller to database";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
json = controller_to_json(controller);
|
||||
|
||||
command_set_controller_name(controller);
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print controller json\n");
|
||||
|
||||
static const char content[] = "failed to print json for controller";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
controller_free(controller);
|
||||
}
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_DELETE(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
const char *target_uid_str = args[0].value.v_str;
|
||||
|
||||
uuid_t target_uid;
|
||||
if(uuid_parse(target_uid_str, target_uid))
|
||||
{
|
||||
LOG_DEBUG("failed to unparse uid\n");
|
||||
|
||||
static const char content[] = "given id was invalid";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
controller_t* controller = controller_get_by_uid(target_uid);
|
||||
|
||||
if(!controller)
|
||||
{
|
||||
LOG_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no controller for id found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(controller_remove(controller))
|
||||
{
|
||||
LOG_ERROR("failed to remove controller from database\n");
|
||||
|
||||
static const char content[] = "failed to remove controller from database";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = 0;
|
||||
response->content = "";
|
||||
response->alloced_content = false;
|
||||
}
|
||||
controller_free(controller);
|
||||
return;
|
||||
}
|
77
endpoints/api_v1_controllers_STR_relays.c
Normal file
77
endpoints/api_v1_controllers_STR_relays.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <constants.h>
|
||||
#include <endpoints/api_v1_controllers.h>
|
||||
#include <logger.h>
|
||||
#include <models/controller.h>
|
||||
#include <models/tag.h>
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_relays_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
uuid_t target_uid;
|
||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||
{
|
||||
LOG_DEBUG("failed to unparse uid\n");
|
||||
|
||||
static const char content[] = "given id was invalid";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
controller_t* controller = controller_get_by_uid(target_uid);
|
||||
|
||||
if(!controller)
|
||||
{
|
||||
LOG_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no controller for id found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
relay_t** all_relays = relay_get_by_controller_id(controller->id);
|
||||
|
||||
cJSON *json = cJSON_CreateArray();
|
||||
|
||||
for(int i = 0; all_relays[i] != NULL; ++i)
|
||||
{
|
||||
cJSON *json_relay = relay_to_json(all_relays[i]);
|
||||
|
||||
cJSON_AddItemToArray(json, json_relay);
|
||||
}
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print relays json\n");
|
||||
|
||||
static const char content[] = "failed to print json for relays";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
relay_free_list(all_relays);
|
||||
controller_free(controller);
|
||||
}
|
315
endpoints/api_v1_controllers_STR_relays_INT.c
Normal file
315
endpoints/api_v1_controllers_STR_relays_INT.c
Normal file
|
@ -0,0 +1,315 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <command.h>
|
||||
#include <constants.h>
|
||||
#include <endpoints/api_v1_controllers.h>
|
||||
#include <logger.h>
|
||||
#include <models/junction_tag.h>
|
||||
#include <models/controller.h>
|
||||
#include <models/relay.h>
|
||||
#include <models/tag.h>
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_relays_INT_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
uuid_t target_uid;
|
||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||
{
|
||||
LOG_DEBUG("failed to unparse uid\n");
|
||||
|
||||
static const char content[] = "given id was invalid";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
controller_t* controller = controller_get_by_uid(target_uid);
|
||||
|
||||
if(!controller)
|
||||
{
|
||||
LOG_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no controller for id found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
relay_t* relay = relay_get_for_controller(controller->id, args[1].value.v_int);
|
||||
|
||||
if(!relay)
|
||||
{
|
||||
LOG_DEBUG("could not find a relay with num %d for controller '%s'\n", args[1].value.v_int, args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no relay for this controller found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json = relay_to_json(relay);
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print relay json\n");
|
||||
|
||||
static const char content[] = "failed to print json for relay";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
relay_free(relay);
|
||||
controller_free(controller);
|
||||
}
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_relays_INT_PUT(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
uuid_t target_uid;
|
||||
if(uuid_parse(args[0].value.v_str, target_uid))
|
||||
{
|
||||
LOG_DEBUG("failed to unparse uid\n");
|
||||
|
||||
static const char content[] = "given id was invalid";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
controller_t* controller = controller_get_by_uid(target_uid);
|
||||
|
||||
if(!controller)
|
||||
{
|
||||
LOG_DEBUG("could not find a controller for uid '%s'\n", args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no controller for id found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
relay_t* relay = relay_get_for_controller(controller->id, args[1].value.v_int);
|
||||
|
||||
if(!relay)
|
||||
{
|
||||
relay = malloc(sizeof(relay_t));
|
||||
relay->id = 0;
|
||||
relay->number = args[1].value.v_int;
|
||||
snprintf(relay->name, MAX_NAME_LENGTH, "Relay %d", relay->number);
|
||||
relay->name[MAX_NAME_LENGTH] = '\0';
|
||||
relay->controller_id = controller->id;
|
||||
|
||||
uuid_t tmp_uuid;
|
||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||
memcpy(tmp_uuid, "off", 3);
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
relay->schedules[i] = schedule_get_by_uid(tmp_uuid);
|
||||
}
|
||||
time_t timestamp = time(NULL);
|
||||
struct tm *time_struct = localtime(×tamp);
|
||||
relay->active_schedule = relay->schedules[helper_get_weekday(time_struct)];
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
static const char content[] = "no valid json was supplied";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||
if(cJSON_IsString(json_name) && json_name->valuestring)
|
||||
{
|
||||
strncpy(relay->name, json_name->valuestring, MAX_NAME_LENGTH);
|
||||
relay->name[MAX_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
|
||||
cJSON *json_schedule;
|
||||
cJSON *json_schedules = cJSON_GetObjectItemCaseSensitive(json, "schedules");
|
||||
|
||||
if(cJSON_GetArraySize(json_schedules) == 7)
|
||||
{
|
||||
int schedule_position = 0;
|
||||
cJSON_ArrayForEach(json_schedule, json_schedules)
|
||||
{
|
||||
cJSON *json_schedule_uid = cJSON_GetObjectItemCaseSensitive(json_schedule, "id");
|
||||
if(!cJSON_IsString(json_schedule_uid) || (json_schedule_uid->valuestring == NULL))
|
||||
{
|
||||
LOG_DEBUG("schedules[%d] is missing uid\n", schedule_position);
|
||||
cJSON_Delete(json);
|
||||
|
||||
static const char content[] = "at least one schedule is missing an id";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
uuid_t target_uid;
|
||||
if(schedule_uid_parse(json_schedule_uid->valuestring, target_uid))
|
||||
{
|
||||
LOG_DEBUG("schedules[%d] has bad uid\n", schedule_position);
|
||||
cJSON_Delete(json);
|
||||
|
||||
static const char content[] = "at least one schedule has a bad id";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
schedule_free(relay->schedules[schedule_position]);
|
||||
relay->schedules[schedule_position] = schedule_get_by_uid_or_off(target_uid);
|
||||
|
||||
++schedule_position;
|
||||
}
|
||||
}
|
||||
|
||||
cJSON *json_active_schedule = cJSON_GetObjectItemCaseSensitive(json, "active_schedule");
|
||||
if(cJSON_IsObject(json_active_schedule))
|
||||
{
|
||||
cJSON *json_active_schedule_uid = cJSON_GetObjectItemCaseSensitive(json_active_schedule, "id");
|
||||
if(cJSON_IsString(json_active_schedule_uid) && json_active_schedule_uid->valuestring)
|
||||
{
|
||||
time_t timestamp = time(NULL);
|
||||
struct tm *time_struct = localtime(×tamp);
|
||||
int day_of_week = helper_get_weekday(time_struct);
|
||||
|
||||
schedule_free(relay->schedules[day_of_week]);
|
||||
|
||||
uuid_t target_uid;
|
||||
if(schedule_uid_parse(json_active_schedule_uid->valuestring, target_uid))
|
||||
{
|
||||
LOG_DEBUG("active_schedule has bad uid\n");
|
||||
cJSON_Delete(json);
|
||||
|
||||
static const char content[] = "active_schedule has a bad id";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
relay->schedules[day_of_week] = schedule_get_by_uid_or_off(target_uid);
|
||||
}
|
||||
}
|
||||
|
||||
if(relay_save(relay))
|
||||
{
|
||||
LOG_ERROR("failed to save relay\n");
|
||||
free(controller);
|
||||
cJSON_Delete(json);
|
||||
|
||||
static const char content[] = "failed to save relay to database";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json_tag;
|
||||
cJSON *json_tags = cJSON_GetObjectItemCaseSensitive(json, "tags");
|
||||
if(cJSON_IsArray(json_tags))
|
||||
{
|
||||
junction_tag_remove_for_relay(relay->id);
|
||||
}
|
||||
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, relay->id, 0);
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
json = relay_to_json(relay);
|
||||
|
||||
command_set_relay_schedule(relay);
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print relay json\n");
|
||||
|
||||
static const char content[] = "failed to print json for relay";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
relay_free(relay);
|
||||
controller_free(controller);
|
||||
}
|
307
endpoints/api_v1_controllers_discover.c
Normal file
307
endpoints/api_v1_controllers_discover.c
Normal file
|
@ -0,0 +1,307 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <constants.h>
|
||||
#include <endpoints/api_v1_controllers.h>
|
||||
#include <models/controller.h>
|
||||
#include <logger.h>
|
||||
#include <mpack.h>
|
||||
|
||||
#define DISCOVERY_TIMEOUT_MS 2000
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DISCOVERY_MAPPING_ID = 0,
|
||||
DISCOVERY_MAPPING_NAME = 1,
|
||||
DISCOVERY_MAPPING_COMMAND_PORT = 2,
|
||||
DISCOVERY_MAPPING_RELAY_COUNT = 3,
|
||||
} discovery_mapping_t;
|
||||
|
||||
static int
|
||||
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: %s\n", 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: %s\n", status);
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((status = listen(fd, max_client_backlog)) == -1)
|
||||
{
|
||||
LOG_ERROR("error setting up listener: %s\n", status);
|
||||
freeaddrinfo(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int
|
||||
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;
|
||||
}
|
||||
|
||||
static int
|
||||
send_udp_broadcast(const char *addr, uint16_t port, void *message, size_t length)
|
||||
{
|
||||
struct sockaddr_in their_addr;
|
||||
int fd;
|
||||
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
|
||||
{
|
||||
LOG_ERROR("error creating socket\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int broadcast = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) < 0)
|
||||
{
|
||||
LOG_ERROR("error setting broadcast\n");
|
||||
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, length, 0, (struct sockaddr *)&their_addr, sizeof(their_addr)) < 0)
|
||||
{
|
||||
LOG_ERROR("error sending broadcast (%d): '%s'\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
api_v1_controllers_discover_POST(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
int discover_server_socket = bind_tcp_server("0.0.0.0", "0", 20);
|
||||
int discover_server_port = get_server_port(discover_server_socket);
|
||||
|
||||
if(discover_server_port == -1)
|
||||
{
|
||||
LOG_ERROR("failed to get server port for discovery\n");
|
||||
static const char content[] = "";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t payload[1];
|
||||
payload[0] = discover_server_port;
|
||||
|
||||
if(send_udp_broadcast("255.255.255.255", global_config.discovery_port, payload, sizeof(payload)) < 0)
|
||||
{
|
||||
LOG_ERROR("failed to send UDP broadcast\n");
|
||||
static const char content[] = "";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
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];
|
||||
|
||||
controller_t **known_controllers = controller_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 = DISCOVERY_TIMEOUT_MS / 1000;
|
||||
timeout.tv_usec = (DISCOVERY_TIMEOUT_MS % 1000) * 1000;
|
||||
|
||||
s_ret = select(discover_server_socket + 1, &accept_fds, NULL, NULL, &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 %s\n", strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t payload_length;
|
||||
|
||||
if(recv(client_fd, &payload_length, sizeof(payload_length), 0) <= 0)
|
||||
{
|
||||
LOG_ERROR("error receiving header from client\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
char *answer_payload = (char*)malloc((payload_length));
|
||||
ssize_t bytes_transferred;
|
||||
|
||||
if((bytes_transferred = recv(client_fd, answer_payload, payload_length, 0)) <= 0)
|
||||
{
|
||||
LOG_ERROR("error receiving payload from client\n");
|
||||
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\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
uuid_t discovered_id;
|
||||
|
||||
mpack_tree_t tree;
|
||||
mpack_tree_init_data(&tree, answer_payload, payload_length);
|
||||
mpack_tree_parse(&tree);
|
||||
mpack_node_t root = mpack_tree_root(&tree);
|
||||
|
||||
memcpy(discovered_id, mpack_node_data(mpack_node_map_uint(root, DISCOVERY_MAPPING_ID)), sizeof(uuid_t));
|
||||
|
||||
uint16_t discovered_command_port = mpack_node_u16(mpack_node_map_uint(root, DISCOVERY_MAPPING_COMMAND_PORT));
|
||||
uint8_t discovered_relay_count = mpack_node_u8(mpack_node_map_uint(root, DISCOVERY_MAPPING_RELAY_COUNT));
|
||||
const char *discovered_name = mpack_node_str(mpack_node_map_uint(root, DISCOVERY_MAPPING_NAME));
|
||||
size_t discovered_name_len = mpack_node_strlen(mpack_node_map_uint(root, DISCOVERY_MAPPING_NAME));
|
||||
|
||||
if(discovered_name_len > MAX_NAME_LENGTH)
|
||||
{
|
||||
discovered_name_len = MAX_NAME_LENGTH;
|
||||
}
|
||||
|
||||
bool found_discovered_in_list = 0;
|
||||
|
||||
for(int i = 0; known_controllers[i] != NULL; i++)
|
||||
{
|
||||
if(!found_discovered_in_list)
|
||||
{
|
||||
if(uuid_compare(known_controllers[i]->uid, discovered_id) == 0)
|
||||
{
|
||||
known_controllers[i]->active = 1;
|
||||
strncpy(known_controllers[i]->name, discovered_name, discovered_name_len);
|
||||
known_controllers[i]->name[discovered_name_len] = '\0';
|
||||
known_controllers[i]->port = discovered_command_port;
|
||||
known_controllers[i]->relay_count = discovered_relay_count;
|
||||
|
||||
controller_save(known_controllers[i]);
|
||||
controller_free(known_controllers[i]);
|
||||
|
||||
found_discovered_in_list = 1;
|
||||
known_controllers[i] = known_controllers[i + 1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
known_controllers[i] = known_controllers[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
if(!found_discovered_in_list)
|
||||
{
|
||||
controller_t *discovered_controller = malloc(sizeof(controller_t));
|
||||
discovered_controller->id = 0;
|
||||
strcpy(discovered_controller->ip, inet_ntoa(addr.sin_addr));
|
||||
memcpy(discovered_controller->uid, discovered_id, sizeof(uuid_t));
|
||||
strncpy(discovered_controller->name, discovered_name, discovered_name_len);
|
||||
discovered_controller->name[discovered_name_len] = '\0';
|
||||
discovered_controller->relay_count = discovered_relay_count;
|
||||
discovered_controller->port = discovered_command_port;
|
||||
discovered_controller->active = 1;
|
||||
|
||||
controller_save(discovered_controller);
|
||||
|
||||
// TODO get relays during discovery and don't create empty ones
|
||||
relay_t **discovered_relays = malloc(sizeof(relay_t*) * (discovered_controller->relay_count + 1));
|
||||
for(int i = 0; i < discovered_controller->relay_count; ++i)
|
||||
{
|
||||
relay_t *new_relay = malloc(sizeof(relay_t));
|
||||
new_relay->id = 0;
|
||||
sprintf(new_relay->name, "Relay %d", i + 1);
|
||||
new_relay->number = i;
|
||||
new_relay->controller_id = discovered_controller->id;
|
||||
|
||||
uuid_t tmp_uuid;
|
||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||
memcpy(tmp_uuid, "off", 3);
|
||||
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
new_relay->schedules[i] = schedule_get_by_uid(tmp_uuid);
|
||||
}
|
||||
time_t timestamp = time(NULL);
|
||||
struct tm *time_struct = localtime(×tamp);
|
||||
new_relay->active_schedule = new_relay->schedules[helper_get_weekday(time_struct)];
|
||||
|
||||
relay_save(new_relay);
|
||||
|
||||
discovered_relays[i] = new_relay;
|
||||
}
|
||||
discovered_relays[discovered_controller->relay_count] = NULL;
|
||||
discovered_controller->relays = discovered_relays;
|
||||
|
||||
controller_free(discovered_controller);
|
||||
}
|
||||
mpack_tree_destroy(&tree);
|
||||
free(answer_payload);
|
||||
|
||||
discover_answer_buf[0] = 0; // TODO add discovery return codes
|
||||
send(client_fd, discover_answer_buf, sizeof(uint8_t), 0);
|
||||
close(client_fd);
|
||||
}
|
||||
}
|
||||
for(int i = 0; known_controllers[i] != NULL; i++)
|
||||
{
|
||||
known_controllers[i]->active = false;
|
||||
controller_save(known_controllers[i]);
|
||||
LOG_DEBUG("lost: %s\n", known_controllers[i]->name);
|
||||
}
|
||||
controller_free_list(known_controllers);
|
||||
|
||||
api_v1_controllers_GET(hm, args, response);
|
||||
}
|
48
endpoints/api_v1_relays.c
Normal file
48
endpoints/api_v1_relays.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <constants.h>
|
||||
#include <endpoints/api_v1_relays.h>
|
||||
#include <logger.h>
|
||||
#include <models/junction_tag.h>
|
||||
#include <models/relay.h>
|
||||
#include <models/tag.h>
|
||||
|
||||
void
|
||||
api_v1_relays_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)args;
|
||||
(void)hm;
|
||||
relay_t** all_relays = relay_get_all();
|
||||
|
||||
cJSON *json = cJSON_CreateArray();
|
||||
|
||||
for(int i = 0; all_relays[i] != NULL; ++i)
|
||||
{
|
||||
cJSON *json_relay = relay_to_json(all_relays[i]);
|
||||
|
||||
cJSON_AddItemToArray(json, json_relay);
|
||||
}
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print relays json\n");
|
||||
|
||||
static const char content[] = "failed to print json for relays";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
relay_free_list(all_relays);
|
||||
}
|
70
endpoints/api_v1_relays_tag_STR.c
Normal file
70
endpoints/api_v1_relays_tag_STR.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <constants.h>
|
||||
#include <endpoints/api_v1_relays.h>
|
||||
#include <logger.h>
|
||||
#include <models/junction_tag.h>
|
||||
#include <models/relay.h>
|
||||
#include <models/tag.h>
|
||||
|
||||
void
|
||||
api_v1_relays_tag_STR_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
int tag_id = tag_get_id(args[0].value.v_str);
|
||||
int *relays_ids = junction_tag_get_relays_for_tag_id(tag_id);
|
||||
if(relays_ids == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to load relays for tag from database\n");
|
||||
|
||||
static const char content[] = "failed to load relays for tag from database";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json = cJSON_CreateArray();
|
||||
|
||||
for(int i = 0; relays_ids[i] != 0; ++i)
|
||||
{
|
||||
relay_t* relay = relay_get_by_id(relays_ids[i]);
|
||||
|
||||
if(!relay)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
cJSON *json_relay = relay_to_json(relay);
|
||||
|
||||
cJSON_AddItemToArray(json, json_relay);
|
||||
|
||||
relay_free(relay);
|
||||
}
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print relays json\n");
|
||||
|
||||
static const char content[] = "failed to print json for relays";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
free(relays_ids);
|
||||
}
|
187
endpoints/api_v1_schedules.c
Normal file
187
endpoints/api_v1_schedules.c
Normal file
|
@ -0,0 +1,187 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <constants.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 http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(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);
|
||||
|
||||
static const char content[] = "no valid json was supplied";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||
if(!cJSON_IsString(json_name) || (json_name->valuestring == NULL))
|
||||
{
|
||||
LOG_DEBUG("no name for schedule provided\n");
|
||||
cJSON_Delete(json);
|
||||
|
||||
static const char content[] = "no name for schedule provided";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
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");
|
||||
|
||||
static const char content[] = "failed to print json for schedule";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 201;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
schedule_free(new_schedule);
|
||||
}
|
||||
|
||||
void
|
||||
api_v1_schedules_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(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");
|
||||
|
||||
static const char content[] = "failed to print json for schedules";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
schedule_free_list(all_schedules);
|
||||
}
|
324
endpoints/api_v1_schedules_STR.c
Normal file
324
endpoints/api_v1_schedules_STR.c
Normal file
|
@ -0,0 +1,324 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <constants.h>
|
||||
#include <endpoints/api_v1_schedules.h>
|
||||
#include <logger.h>
|
||||
#include <command.h>
|
||||
#include <models/junction_tag.h>
|
||||
#include <models/junction_relay_schedule.h>
|
||||
#include <models/schedule.h>
|
||||
#include <models/relay.h>
|
||||
#include <models/tag.h>
|
||||
|
||||
void
|
||||
api_v1_schedules_STR_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
uuid_t target_uid;
|
||||
if(schedule_uid_parse(args[0].value.v_str, target_uid))
|
||||
{
|
||||
LOG_DEBUG("failed to unparse uid\n");
|
||||
|
||||
static const char content[] = "given id was invalid";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
schedule_t* schedule = schedule_get_by_uid(target_uid);
|
||||
|
||||
if(!schedule)
|
||||
{
|
||||
LOG_DEBUG("could not find a schedule for uid '%s'\n", args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no schedule for id found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json = schedule_to_json(schedule);
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print schedules json\n");
|
||||
|
||||
static const char content[] = "failed to print json for schedules";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
schedule_free(schedule);
|
||||
}
|
||||
|
||||
void
|
||||
api_v1_schedules_STR_PUT(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
uuid_t target_uid;
|
||||
if(schedule_uid_parse(args[0].value.v_str, target_uid))
|
||||
{
|
||||
LOG_DEBUG("failed to unparse uid\n");
|
||||
|
||||
static const char content[] = "given id was invalid";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
schedule_t* schedule = schedule_get_by_uid(target_uid);
|
||||
|
||||
if(!schedule)
|
||||
{
|
||||
LOG_DEBUG("could not find a schedule for uid '%s'\n", args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no schedule for id found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json = cJSON_ParseWithLength(hm->body.p, hm->body.len);
|
||||
|
||||
if(json == NULL)
|
||||
{
|
||||
const char *error_ptr = cJSON_GetErrorPtr();
|
||||
if (error_ptr != NULL)
|
||||
{
|
||||
LOG_DEBUG("error before: %s\n", error_ptr);
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
|
||||
static const char content[] = "no valid json was supplied";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(json, "name");
|
||||
if(cJSON_IsString(json_name) && json_name->valuestring)
|
||||
{
|
||||
strncpy(schedule->name, json_name->valuestring, MAX_NAME_LENGTH);
|
||||
schedule->name[MAX_NAME_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
if(!schedule_is_protected(schedule))
|
||||
{
|
||||
cJSON *json_period;
|
||||
cJSON *json_periods = cJSON_GetObjectItemCaseSensitive(json, "periods");
|
||||
|
||||
int periods_count = cJSON_GetArraySize(json_periods);
|
||||
free(schedule->periods);
|
||||
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;
|
||||
}
|
||||
|
||||
schedule->periods[periods_valid].start = start;
|
||||
schedule->periods[periods_valid].end = end;
|
||||
++periods_valid;
|
||||
}
|
||||
|
||||
schedule->periods_count = periods_valid;
|
||||
}
|
||||
|
||||
if(schedule_save(schedule))
|
||||
{
|
||||
LOG_ERROR("failed to save schedule\n");
|
||||
free(schedule);
|
||||
cJSON_Delete(json);
|
||||
|
||||
static const char content[] = "failed to save schedule to database";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
relay_t **relays = relay_get_with_schedule(schedule->id);
|
||||
for(int i = 0; relays[i] != NULL; ++i)
|
||||
{
|
||||
command_set_relay_schedule(relays[i]);
|
||||
}
|
||||
|
||||
cJSON *json_tag;
|
||||
cJSON *json_tags = cJSON_GetObjectItemCaseSensitive(json, "tags");
|
||||
if(cJSON_IsArray(json_tags))
|
||||
{
|
||||
junction_tag_remove_for_schedule(schedule->id);
|
||||
}
|
||||
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, schedule->id);
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
json = schedule_to_json(schedule);
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print schedule json\n");
|
||||
|
||||
static const char content[] = "failed to print json for schedule";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
cJSON_Delete(json);
|
||||
relay_free_list(relays);
|
||||
schedule_free(schedule);
|
||||
}
|
||||
|
||||
void
|
||||
api_v1_schedules_STR_DELETE(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
const char *target_uid_str = args[0].value.v_str;
|
||||
|
||||
uuid_t target_uid;
|
||||
if(schedule_uid_parse(target_uid_str, target_uid))
|
||||
{
|
||||
LOG_DEBUG("failed to unparse uid\n");
|
||||
|
||||
static const char content[] = "given id was invalid";
|
||||
response->status_code = 400;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
schedule_t* schedule = schedule_get_by_uid(target_uid);
|
||||
|
||||
if(!schedule)
|
||||
{
|
||||
LOG_DEBUG("could not find a schedule for uid '%s'\n", args[0].value.v_str);
|
||||
|
||||
static const char content[] = "no schedule for id found";
|
||||
response->status_code = 404;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(schedule_is_protected(schedule))
|
||||
{
|
||||
static const char content[] = "target schedule is protected";
|
||||
response->status_code = 403;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
|
||||
schedule_free(schedule);
|
||||
return;
|
||||
}
|
||||
|
||||
if(schedule_remove(schedule))
|
||||
{
|
||||
LOG_ERROR("failed to remove schedule from database\n");
|
||||
|
||||
static const char content[] = "failed to remove schedule from database";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = 0;
|
||||
response->content = "";
|
||||
response->alloced_content = false;
|
||||
}
|
||||
schedule_free(schedule);
|
||||
return;
|
||||
}
|
70
endpoints/api_v1_schedules_tag_STR.c
Normal file
70
endpoints/api_v1_schedules_tag_STR.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include <cJSON.h>
|
||||
#include <macros.h>
|
||||
#include <constants.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_tag_STR_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response)
|
||||
{
|
||||
(void)hm;
|
||||
|
||||
int tag_id = tag_get_id(args[0].value.v_str);
|
||||
int *schedules_ids = junction_tag_get_schedules_for_tag_id(tag_id);
|
||||
if(schedules_ids == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to load schedules for tag from database\n");
|
||||
|
||||
static const char content[] = "failed to load schedules for tag from database";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON *json = cJSON_CreateArray();
|
||||
|
||||
for(int i = 0; schedules_ids[i] != 0; ++i)
|
||||
{
|
||||
schedule_t* schedule = schedule_get_by_id(schedules_ids[i]);
|
||||
|
||||
if(!schedule)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
cJSON *json_schedule = schedule_to_json(schedule);
|
||||
|
||||
cJSON_AddItemToArray(json, json_schedule);
|
||||
|
||||
schedule_free(schedule);
|
||||
}
|
||||
|
||||
char *json_str = cJSON_Print(json);
|
||||
if (json_str == NULL)
|
||||
{
|
||||
LOG_ERROR("failed to print schedules json\n");
|
||||
|
||||
static const char content[] = "failed to print json for schedules";
|
||||
response->status_code = 500;
|
||||
response->content_type = "text/plain";
|
||||
response->content_length = STRLEN(content);;
|
||||
response->content = content;
|
||||
response->alloced_content = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
response->status_code = 200;
|
||||
response->content_type = "application/json";
|
||||
response->content_length = strlen(json_str);
|
||||
response->content = json_str;
|
||||
response->alloced_content = true;
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
free(schedules_ids);
|
||||
}
|
|
@ -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,126 +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;
|
||||
|
||||
// level 1
|
||||
bool set_name = body["name"].type() == Json::ValueType::stringValue;
|
||||
bool set_tags = body["tags"].type() == Json::ValueType::arrayValue;
|
||||
bool set_schedules = body["schedules"].type() == Json::ValueType::arrayValue;
|
||||
bool set_active_schedule = body["active_schedule"].type() == Json::ValueType::objectValue;
|
||||
|
||||
// level 2
|
||||
if(is_valid)
|
||||
{
|
||||
if(set_tags)
|
||||
{
|
||||
for(int i = 0; i < body["tags"].size(); ++i)
|
||||
{
|
||||
is_valid &= body["tags"][i].type() == Json::ValueType::stringValue;
|
||||
}
|
||||
}
|
||||
if(set_active_schedule)
|
||||
{
|
||||
is_valid &= body["active_schedule"]["id"].type() == Json::ValueType::stringValue;
|
||||
}
|
||||
if(set_schedules)
|
||||
{
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
is_valid &= body["schedules"][i].type() == Json::ValueType::objectValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// level 3
|
||||
if(is_valid)
|
||||
{
|
||||
if(set_schedules)
|
||||
{
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
is_valid &= body["schedules"][i]["id"].type() == Json::ValueType::stringValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!is_valid)
|
||||
{
|
||||
LOG_DEBUG << "basic structe is wrong";
|
||||
auto res = drogon::HttpResponse::newHttpResponse();
|
||||
res->setStatusCode(k400BadRequest);
|
||||
fcb(res);
|
||||
return;
|
||||
}
|
||||
|
||||
if(set_active_schedule)
|
||||
{
|
||||
uuid_t active_schedule_id;
|
||||
if(schedule_dbo::parse_uid(body["active_schedule"]["id"].asCString(), active_schedule_id))
|
||||
{
|
||||
LOG_DEBUG << "parse_uid failed for active_schedule";
|
||||
auto res = drogon::HttpResponse::newHttpResponse();
|
||||
res->setStatusCode(k400BadRequest);
|
||||
fcb(res);
|
||||
return;
|
||||
}
|
||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("uid", active_schedule_id, (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
|
||||
bool schedule_found = schedules[0] != nullptr;
|
||||
schedule_dbo::free_list(schedules);
|
||||
if(!schedule_found)
|
||||
{
|
||||
LOG_DEBUG << "could not find active_schedule";
|
||||
auto res = drogon::HttpResponse::newHttpResponse();
|
||||
res->setStatusCode(k400BadRequest);
|
||||
fcb(res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(set_schedules)
|
||||
{
|
||||
uuid_t schedules_ids[7];
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
if(schedule_dbo::parse_uid(body["schedules"][i]["id"].asCString(), schedules_ids[i]))
|
||||
{
|
||||
LOG_DEBUG << "parse_uid failed for schedule " << i;
|
||||
auto res = drogon::HttpResponse::newHttpResponse();
|
||||
res->setStatusCode(k400BadRequest);
|
||||
fcb(res);
|
||||
return;
|
||||
}
|
||||
|
||||
schedule_dbo **schedules = schedule_dbo::get_by_simple("uid", schedules_ids[i], (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
|
||||
bool schedule_found = schedules[0] != nullptr;
|
||||
schedule_dbo::free_list(schedules);
|
||||
if(!schedule_found)
|
||||
{
|
||||
LOG_DEBUG << "could not find schedule " << i;
|
||||
auto res = drogon::HttpResponse::newHttpResponse();
|
||||
res->setStatusCode(k400BadRequest);
|
||||
fcb(res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Passed
|
||||
fccb();
|
||||
return;
|
||||
}
|
|
@ -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,66 +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;
|
||||
|
||||
bool set_name = body["name"].type() == Json::ValueType::stringValue;
|
||||
bool set_tags = body["tags"].type() == Json::ValueType::arrayValue;
|
||||
bool set_periods = body["periods"].type() == Json::ValueType::arrayValue;
|
||||
|
||||
// level 2
|
||||
if(is_valid)
|
||||
{
|
||||
if(set_tags)
|
||||
{
|
||||
for(int i = 0; i < body["tags"].size(); ++i)
|
||||
{
|
||||
is_valid &= body["tags"][i].type() == Json::ValueType::stringValue;
|
||||
}
|
||||
}
|
||||
if(set_periods)
|
||||
{
|
||||
for(int i = 0; i < body["periods"].size(); ++i)
|
||||
{
|
||||
is_valid &= body["periods"][i].type() == Json::ValueType::objectValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// level 3
|
||||
if(is_valid)
|
||||
{
|
||||
if(set_periods)
|
||||
{
|
||||
for(int i = 0; i < body["periods"].size(); ++i)
|
||||
{
|
||||
is_valid &= body["periods"][i]["start"].type() == Json::ValueType::stringValue;
|
||||
is_valid &= body["periods"][i]["end"].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::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
|
89
handlers/connection.c
Normal file
89
handlers/connection.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <constants.h>
|
||||
#include <mongoose.h>
|
||||
#include <logger.h>
|
||||
#include <router.h>
|
||||
#include <handlers.h>
|
||||
|
||||
#define STD_HEADERS "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\nAccess-Control-Allow-Methods: *\r\n"
|
||||
#define HEADERS_FMT STD_HEADERS "Content-Type: %s"
|
||||
|
||||
// -2 for "%s" -1 for \0
|
||||
#define HEADERS_FMT_LEN (sizeof(HEADERS_FMT) - 3)
|
||||
|
||||
#define STD_RESPONSE_CONTENT "the server did not create a response"
|
||||
#define STD_RESPONSE_CONTENT_LEN (sizeof(STD_RESPONSE_CONTENT) - 1)
|
||||
|
||||
void
|
||||
handler_connection(struct mg_connection *c, int ev, void *p)
|
||||
{
|
||||
if (ev == MG_EV_HTTP_REQUEST)
|
||||
{
|
||||
struct http_message *hm = (struct http_message *) p;
|
||||
LOG_DEBUG("new http %.*s request for %.*s\n", hm->method.len, hm->method.p, hm->uri.len, hm->uri.p);
|
||||
|
||||
endpoint_t *endpoint = router_find_endpoint(hm->uri.p, hm->uri.len, &hm->method);
|
||||
|
||||
if(endpoint)
|
||||
{
|
||||
if(endpoint->func)
|
||||
{
|
||||
endpoint_response_t response;
|
||||
response.status_code = 500;
|
||||
response.content_type = "text/plain";
|
||||
response.content_length = STD_RESPONSE_CONTENT_LEN;
|
||||
response.content = STD_RESPONSE_CONTENT;
|
||||
response.alloced_content = false;
|
||||
|
||||
endpoint->func(p, endpoint->args, &response);
|
||||
|
||||
char *response_headers = malloc(sizeof(char) * (HEADERS_FMT_LEN + strlen(response.content_type) + 1));
|
||||
sprintf(response_headers, HEADERS_FMT, response.content_type);
|
||||
|
||||
mg_send_head(c, response.status_code, response.content_length, response_headers);
|
||||
mg_printf(c, "%s", response.content);
|
||||
|
||||
free(response_headers);
|
||||
|
||||
if(response.alloced_content)
|
||||
{
|
||||
free((char*)response.content);
|
||||
}
|
||||
|
||||
for(int i = 0; i < endpoint->args_count; ++i)
|
||||
{
|
||||
if(endpoint->args[i].type == ENDPOINT_ARG_TYPE_STR)
|
||||
{
|
||||
free((char*)endpoint->args[i].value.v_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(endpoint->method == HTTP_METHOD_OPTIONS)
|
||||
{
|
||||
char options_header[256]; // TODO make more generic
|
||||
sprintf(options_header, STD_HEADERS "Allow: OPTIONS%s%s%s%s",
|
||||
endpoint->options & HTTP_METHOD_GET ? ", GET" : "",
|
||||
endpoint->options & HTTP_METHOD_POST ? ", POST" : "",
|
||||
endpoint->options & HTTP_METHOD_PUT ? ", PUT" : "",
|
||||
endpoint->options & HTTP_METHOD_DELETE ? ", DELETE" : ""
|
||||
);
|
||||
mg_send_head(c, 204, 0, options_header);
|
||||
}
|
||||
else
|
||||
{
|
||||
mg_send_head(c, 501, 0, "Content-Type: text/plain");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mg_send_head(c, 500, 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);
|
||||
}
|
||||
}
|
47
helpers.h
47
helpers.h
|
@ -1,47 +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, void *message, size_t length);
|
||||
|
||||
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, int bind_func_param, const char *logic);
|
||||
|
||||
const char *col_name;
|
||||
const void *value;
|
||||
intptr_t bind_func;
|
||||
int bind_func_param;
|
||||
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();
|
||||
|
||||
int
|
||||
get_day_of_week();
|
||||
}
|
||||
|
||||
#endif //EMGAUWA_CORE_HELPERS_H
|
|
@ -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,62 +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*)filter->value));
|
||||
}
|
||||
if(filter->bind_func == (intptr_t)&sqlite3_bind_text)
|
||||
{
|
||||
sqlite3_bind_text(stmt, i + 1, (char*)filter->value, filter->bind_func_param, SQLITE_STATIC);
|
||||
}
|
||||
if(filter->bind_func == (intptr_t)&sqlite3_bind_blob)
|
||||
{
|
||||
sqlite3_bind_blob(stmt, i + 1, filter->value, filter->bind_func_param, SQLITE_STATIC);
|
||||
}
|
||||
}
|
||||
|
||||
return stmt;
|
||||
}
|
||||
|
||||
helpers::sql_filter_builder::sql_filter_builder(const char *col_name, const void *value, intptr_t bind_func, int bind_func_param, const char *logic)
|
||||
{
|
||||
this->col_name = col_name;
|
||||
this->value = value;
|
||||
this->bind_func = bind_func;
|
||||
this->bind_func_param = bind_func_param;
|
||||
this->logic = logic;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#include <netdb.h>
|
||||
#include <helpers.h>
|
||||
|
||||
int
|
||||
helpers::get_day_of_week()
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
struct tm *now = localtime(&t);
|
||||
int wday_sun_sat = now->tm_wday;
|
||||
int wday_mon_sun = (wday_sun_sat + 6) % 7;
|
||||
return wday_mon_sun;
|
||||
}
|
|
@ -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;
|
||||
}
|
11
helpers/get_weekday.c
Normal file
11
helpers/get_weekday.c
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <time.h>
|
||||
|
||||
#include <helpers.h>
|
||||
|
||||
int
|
||||
helper_get_weekday(const struct tm *time_struct)
|
||||
{
|
||||
int wday_sun_sat = time_struct->tm_wday;
|
||||
int wday_mon_sun = (wday_sun_sat + 6) % 7;
|
||||
return wday_mon_sun;
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
#include <helpers.h>
|
||||
#include <globals.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <drogon/trantor/trantor/utils/Logger.h>
|
||||
#include <drogon/utils/Utilities.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:
|
||||
LOG_INFO << "Migrating LEVEL 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;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#include <helpers.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <string.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 " << strerror(errno);
|
||||
freeaddrinfo(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
|
||||
return s;
|
||||
}
|
54
helpers/parse_cli.c
Normal file
54
helpers/parse_cli.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
#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[] = {
|
||||
"core [options] [[--] args]",
|
||||
"core [options]",
|
||||
NULL,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
config->run_type = RUN_TYPE_INVALID;
|
||||
if(strcmp(argv[0], "start") == 0)
|
||||
{
|
||||
config->run_type = RUN_TYPE_START;
|
||||
return;
|
||||
}
|
||||
LOG_FATAL("bad action '%s' given ('start')\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_FATAL("no action given ('start')\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, void *message, size_t length)
|
||||
{
|
||||
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, length, 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
|
16
include/command.h
Normal file
16
include/command.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef CORE_COMMAND_H
|
||||
#define CORE_COMMAND_H
|
||||
|
||||
#include <models/controller.h>
|
||||
#include <models/relay.h>
|
||||
|
||||
int
|
||||
command_set_relay_schedule(relay_t *relay);
|
||||
|
||||
int
|
||||
command_set_controller_name(controller_t *controller);
|
||||
|
||||
int
|
||||
command_send(controller_t *controller, int command_code, char *payload, uint32_t payload_size);
|
||||
|
||||
#endif /* CORE_COMMAND_H */
|
39
include/config.h
Normal file
39
include/config.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
#ifndef CORE_CONFIG_H
|
||||
#define CORE_CONFIG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <confini.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RUN_TYPE_START,
|
||||
RUN_TYPE_INVALID,
|
||||
} 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;
|
||||
|
||||
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 */
|
30
include/endpoints/api_v1_controllers.h
Normal file
30
include/endpoints/api_v1_controllers.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef CORE_ENDPOINTS_API_V1_CONTROLLERS_H
|
||||
#define CORE_ENDPOINTS_API_V1_CONTROLLERS_H
|
||||
|
||||
#include <router.h>
|
||||
|
||||
void
|
||||
api_v1_controllers_discover_POST(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_controllers_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_PUT(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_DELETE(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_relays_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_relays_INT_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_controllers_STR_relays_INT_PUT(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
#endif /* CORE_ENDPOINTS_API_V1_CONTROLLERS_H */
|
12
include/endpoints/api_v1_relays.h
Normal file
12
include/endpoints/api_v1_relays.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef CORE_ENDPOINTS_API_V1_RELAYS_H
|
||||
#define CORE_ENDPOINTS_API_V1_RELAYS_H
|
||||
|
||||
#include <router.h>
|
||||
|
||||
void
|
||||
api_v1_relays_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_relays_tag_STR_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
#endif /* CORE_ENDPOINTS_API_V1_RELAYS_H */
|
24
include/endpoints/api_v1_schedules.h
Normal file
24
include/endpoints/api_v1_schedules.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef CORE_ENDPOINTS_API_V1_SCHEDULES_H
|
||||
#define CORE_ENDPOINTS_API_V1_SCHEDULES_H
|
||||
|
||||
#include <router.h>
|
||||
|
||||
void
|
||||
api_v1_schedules_POST(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_schedules_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_schedules_STR_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_schedules_STR_PUT(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_schedules_STR_DELETE(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
void
|
||||
api_v1_schedules_tag_STR_GET(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
#endif /* CORE_ENDPOINTS_API_V1_SCHEDULES_H */
|
40
include/enums.h
Normal file
40
include/enums.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef CORE_ENUMS_H
|
||||
#define CORE_ENUMS_H
|
||||
|
||||
typedef enum
|
||||
{
|
||||
POLL_FDS_DISCOVERY,
|
||||
POLL_FDS_COMMAND
|
||||
} poll_fds_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;
|
||||
|
||||
#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 */
|
17
include/helpers.h
Normal file
17
include/helpers.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef CORE_HELPERS_H
|
||||
#define CORE_HELPERS_H
|
||||
|
||||
#include <time.h>
|
||||
#include <config.h>
|
||||
#include <confini.h>
|
||||
|
||||
int
|
||||
helper_connect_tcp_server(char* host, uint16_t port);
|
||||
|
||||
void
|
||||
helper_parse_cli(int argc, const char **argv, config_t *config);
|
||||
|
||||
int
|
||||
helper_get_weekday(const struct tm *time_struct);
|
||||
|
||||
#endif /* CORE_HELPERS_H */
|
26
include/logger.h
Normal file
26
include/logger.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#ifndef CORE_LOGGER_H
|
||||
#define CORE_LOGGER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <colors.h>
|
||||
#include <config.h>
|
||||
|
||||
#ifndef SOURCE_PATH_SIZE
|
||||
#define SOURCE_PATH_SIZE 0
|
||||
#endif
|
||||
|
||||
#define __FILENAME__ (__FILE__ + SOURCE_PATH_SIZE)
|
||||
|
||||
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
|
1
include/macros.h
Normal file
1
include/macros.h
Normal file
|
@ -0,0 +1 @@
|
|||
#define STRLEN(s) ((sizeof(s)/sizeof(s[0])) - sizeof(s[0]))
|
0
include/migrations/.gitkeep
Normal file
0
include/migrations/.gitkeep
Normal file
53
include/models/controller.h
Normal file
53
include/models/controller.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#ifndef CORE_MODELS_CONTROLLER_H
|
||||
#define CORE_MODELS_CONTROLLER_H
|
||||
|
||||
#include <uuid/uuid.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <constants.h>
|
||||
#include <cJSON.h>
|
||||
#include <helpers.h>
|
||||
#include <models/relay.h>
|
||||
|
||||
#define IP_LENGTH 16
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
uuid_t uid;
|
||||
char name[MAX_NAME_LENGTH + 1];
|
||||
char ip[IP_LENGTH + 1];
|
||||
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);
|
||||
|
||||
cJSON*
|
||||
controller_to_json(controller_t* contoller);
|
||||
|
||||
controller_t*
|
||||
controller_get_by_id(int id);
|
||||
|
||||
controller_t*
|
||||
controller_get_by_uid(uuid_t uid);
|
||||
|
||||
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 */
|
22
include/models/junction_relay_schedule.h
Normal file
22
include/models/junction_relay_schedule.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#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_get_relays_ids(int schedule_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 */
|
60
include/models/relay.h
Normal file
60
include/models/relay.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef CORE_RELAY_H
|
||||
#define CORE_RELAY_H
|
||||
|
||||
#include <string.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include <constants.h>
|
||||
#include <cJSON.h>
|
||||
#include <helpers.h>
|
||||
#include <database.h>
|
||||
#include <models/schedule.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
char name[MAX_NAME_LENGTH + 1];
|
||||
int number;
|
||||
int controller_id;
|
||||
int active_schedule_id;
|
||||
schedule_t *active_schedule;
|
||||
schedule_t *schedules[7];
|
||||
} relay_t;
|
||||
|
||||
int
|
||||
relay_save();
|
||||
|
||||
int
|
||||
relay_remove();
|
||||
|
||||
void
|
||||
relay_reload_active_schedule(relay_t *relay);
|
||||
|
||||
cJSON*
|
||||
relay_to_json();
|
||||
|
||||
void
|
||||
relay_free(relay_t *relay);
|
||||
|
||||
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_for_controller(int controller_id, int relay_num);
|
||||
|
||||
relay_t**
|
||||
relay_get_with_schedule(int schedule_id);
|
||||
|
||||
relay_t**
|
||||
relay_get_all();
|
||||
|
||||
relay_t**
|
||||
relay_get_by_controller_id(int controller_id);
|
||||
|
||||
#endif /* CORE_RELAY_H */
|
67
include/models/schedule.h
Normal file
67
include/models/schedule.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
#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);
|
||||
|
||||
int
|
||||
schedule_is_protected(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_id_or_off(int id);
|
||||
|
||||
schedule_t*
|
||||
schedule_get_by_id(int id);
|
||||
|
||||
schedule_t*
|
||||
schedule_get_by_uid_or_off(uuid_t uid);
|
||||
|
||||
schedule_t*
|
||||
schedule_get_by_uid(uuid_t uid);
|
||||
|
||||
schedule_t**
|
||||
schedule_get_relay_weekdays(int relay_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 */
|
73
include/router.h
Normal file
73
include/router.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
#ifndef CORE_ROUTER_H
|
||||
#define CORE_ROUTER_H
|
||||
|
||||
#include <mongoose.h>
|
||||
|
||||
#define ENDPOINTS_MAX_COUNT 128
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ENDPOINT_ARG_TYPE_INT,
|
||||
ENDPOINT_ARG_TYPE_STR
|
||||
} endpoint_arg_type_e;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
HTTP_METHOD_GET = (1 << 0),
|
||||
HTTP_METHOD_POST = (1 << 1),
|
||||
HTTP_METHOD_PUT = (1 << 2),
|
||||
HTTP_METHOD_DELETE = (1 << 3),
|
||||
HTTP_METHOD_OPTIONS = (1 << 4)
|
||||
} http_method_e;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
endpoint_arg_type_e type;
|
||||
union
|
||||
{
|
||||
int v_int;
|
||||
const char *v_str;
|
||||
} value;
|
||||
} endpoint_args_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int status_code;
|
||||
const char *content_type;
|
||||
size_t content_length;
|
||||
const char *content;
|
||||
int alloced_content;
|
||||
} endpoint_response_t;
|
||||
|
||||
typedef void (*endpoint_func_f)(struct http_message *hm, endpoint_args_t *args, endpoint_response_t *response);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *full_route;
|
||||
char **route;
|
||||
char *route_keeper;
|
||||
int method;
|
||||
int options;
|
||||
endpoint_func_f func;
|
||||
int trailing_slash;
|
||||
|
||||
int args_count;
|
||||
endpoint_args_t *args;
|
||||
|
||||
int possible_route;
|
||||
int args_found;
|
||||
} endpoint_t;
|
||||
|
||||
void
|
||||
router_init();
|
||||
|
||||
endpoint_t*
|
||||
router_register_endpoint(const char *route, int method, endpoint_func_f func);
|
||||
|
||||
endpoint_t*
|
||||
router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method_str);
|
||||
|
||||
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);
|
||||
}
|
109
main.c
Normal file
109
main.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
#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);
|
||||
}
|
||||
|
||||
sqlite3_exec(global_database, "PRAGMA foreign_keys = ON", 0, 0, 0);
|
||||
|
||||
|
||||
/******************** 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);
|
||||
}
|
74
main.cc
74
main.cc
|
@ -1,74 +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(int argc, char** argv)
|
||||
{
|
||||
signal(SIGINT, terminate);
|
||||
signal(SIGABRT, terminate);
|
||||
signal(SIGTERM, terminate);
|
||||
|
||||
const char* config_file_name = "config.json";
|
||||
|
||||
if(argc == 2)
|
||||
{
|
||||
config_file_name = argv[1];
|
||||
}
|
||||
|
||||
//Load config file
|
||||
drogon::app().loadConfigFile(config_file_name);
|
||||
|
||||
const char* db_name = drogon::app().instance().getCustomConfig()["db_name"].asCString();
|
||||
|
||||
/* Open database */
|
||||
int rc = sqlite3_open(db_name, &globals::db);
|
||||
|
||||
if(rc) {
|
||||
LOG_FATAL << "Can't open database: " << sqlite3_errmsg(globals::db);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(helpers::migrate_sql())
|
||||
{
|
||||
terminate(1);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
293
models/controller.c
Normal file
293
models/controller.c
Normal file
|
@ -0,0 +1,293 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <cJSON.h>
|
||||
#include <logger.h>
|
||||
#include <database.h>
|
||||
#include <models/controller.h>
|
||||
#include <models/junction_tag.h>
|
||||
#include <models/tag.h>
|
||||
|
||||
static int
|
||||
db_update_insert(controller_t *controller, sqlite3_stmt *stmt)
|
||||
{
|
||||
int rc;
|
||||
|
||||
sqlite3_bind_int(stmt, 1, controller->id);
|
||||
sqlite3_bind_blob(stmt, 2, controller->uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 3, controller->name, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 4, controller->ip, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_int(stmt, 5, controller->active);
|
||||
sqlite3_bind_int(stmt, 6, controller->port);
|
||||
sqlite3_bind_int(stmt, 7, controller->relay_count);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc != SQLITE_DONE;
|
||||
}
|
||||
static controller_t*
|
||||
controller_db_select_mapper(sqlite3_stmt *stmt)
|
||||
{
|
||||
controller_t *new_controller = malloc(sizeof(controller_t));
|
||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||
{
|
||||
const char *name = sqlite3_column_name(stmt, i);
|
||||
switch(name[0])
|
||||
{
|
||||
case 'a': // active
|
||||
new_controller->active = (bool)sqlite3_column_int(stmt, i);
|
||||
break;
|
||||
case 'i':
|
||||
switch(name[1])
|
||||
{
|
||||
case 'd': // id
|
||||
new_controller->id = sqlite3_column_int(stmt, i);
|
||||
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;
|
||||
case 'u': // uid
|
||||
uuid_copy(new_controller->uid, (const unsigned char*)sqlite3_column_blob(stmt, i));
|
||||
break;
|
||||
default: // ignore columns not implemented
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_controller->relays = relay_get_by_controller_id(new_controller->id);
|
||||
return new_controller;
|
||||
}
|
||||
|
||||
static controller_t**
|
||||
controller_db_select(sqlite3_stmt *stmt)
|
||||
{
|
||||
controller_t **all_controllers = malloc(sizeof(controller_t*));
|
||||
|
||||
int row = 0;
|
||||
|
||||
while(true)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
controller_t *new_controller = controller_db_select_mapper(stmt);
|
||||
row++;
|
||||
|
||||
all_controllers = (controller_t**)realloc(all_controllers, sizeof(controller_t*) * (row + 1));
|
||||
all_controllers[row - 1] = new_controller;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(s == SQLITE_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("error selecting controllers from database: %s\n", sqlite3_errstr(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
all_controllers[row] = NULL;
|
||||
return all_controllers;
|
||||
}
|
||||
|
||||
int
|
||||
controller_save(controller_t *controller)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
if(controller->id)
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "UPDATE controllers set uid = ?2, name = ?3, ip = ?4, active = ?5, port = ?6, relay_count = ?7 WHERE id = ?1;", -1, &stmt, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "INSERT INTO controllers(uid, name, ip, active, port, relay_count) values (?2, ?3, ?4, ?5, ?6, ?7);", -1, &stmt, NULL);
|
||||
}
|
||||
|
||||
int result = db_update_insert(controller, stmt);
|
||||
|
||||
if(result)
|
||||
{
|
||||
if(controller->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
|
||||
{
|
||||
if(!controller->id)
|
||||
{
|
||||
controller->id = sqlite3_last_insert_rowid(global_database);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
controller_remove(controller_t *controller)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
if(!controller->id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3_prepare_v2(global_database, "DELETE FROM controllers WHERE id=?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, controller->id);
|
||||
|
||||
int rc = sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc != SQLITE_DONE;
|
||||
}
|
||||
|
||||
void
|
||||
controller_free(controller_t *controller)
|
||||
{
|
||||
relay_free_list(controller->relays);
|
||||
free(controller);
|
||||
}
|
||||
|
||||
void
|
||||
controller_free_list(controller_t **controllers)
|
||||
{
|
||||
for(int i = 0; controllers[i] != NULL; ++i)
|
||||
{
|
||||
controller_free(controllers[i]);
|
||||
}
|
||||
free(controllers);
|
||||
}
|
||||
|
||||
cJSON*
|
||||
controller_to_json(controller_t *controller)
|
||||
{
|
||||
cJSON *json = cJSON_CreateObject();
|
||||
|
||||
cJSON *json_name = cJSON_CreateString(controller->name);
|
||||
if(json_name == NULL)
|
||||
{
|
||||
cJSON_Delete(json);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(json, "name", json_name);
|
||||
|
||||
char uuid_str[UUID_STR_LEN];
|
||||
uuid_unparse(controller->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_ip = cJSON_CreateString(controller->ip);
|
||||
if(json_ip == NULL)
|
||||
{
|
||||
cJSON_Delete(json);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(json, "ip", json_ip);
|
||||
|
||||
cJSON *json_port = cJSON_CreateNumber(controller->port);
|
||||
if(json_port == NULL)
|
||||
{
|
||||
cJSON_Delete(json);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(json, "port", json_port);
|
||||
|
||||
cJSON *json_relay_count = cJSON_CreateNumber(controller->relay_count);
|
||||
if(json_relay_count == NULL)
|
||||
{
|
||||
cJSON_Delete(json);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(json, "relay_count", json_relay_count);
|
||||
|
||||
cJSON *json_active = cJSON_CreateBool(controller->active);
|
||||
if(json_active == NULL)
|
||||
{
|
||||
cJSON_Delete(json);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(json, "active", json_active);
|
||||
|
||||
relay_t **relays = relay_get_by_controller_id(controller->id);
|
||||
cJSON *json_relays = cJSON_CreateArray();
|
||||
for(int i = 0; relays[i] != NULL; ++i)
|
||||
{
|
||||
cJSON_AddItemToArray(json_relays, relay_to_json(relays[i]));
|
||||
}
|
||||
cJSON_AddItemToObject(json, "relays", json_relays);
|
||||
relay_free_list(relays);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
controller_t*
|
||||
controller_get_by_id(int id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM controllers WHERE id = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, id);
|
||||
|
||||
controller_t **sql_result = controller_db_select(stmt);
|
||||
|
||||
controller_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
controller_t*
|
||||
controller_get_by_uid(uuid_t uid)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM controllers WHERE uid = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||
|
||||
controller_t **sql_result = controller_db_select(stmt);
|
||||
|
||||
controller_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
controller_t**
|
||||
controller_get_all()
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM controllers;", -1, &stmt, NULL);
|
||||
|
||||
return controller_db_select(stmt);
|
||||
}
|
|
@ -1,263 +0,0 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <helpers.h>
|
||||
#include <mpack.h>
|
||||
#include <enums.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_blob(stmt, 1, controller->id, sizeof(uuid_t), 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
|
||||
uuid_copy(new_controller->id, (const unsigned char*)sqlite3_column_blob(stmt, i));
|
||||
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_blob, sizeof(uuid_t));
|
||||
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_blob(stmt, 1, this->id, sizeof(uuid_t), SQLITE_STATIC);
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc == SQLITE_DONE;
|
||||
|
||||
}
|
||||
|
||||
Json::Value
|
||||
controller_dbo::to_json()
|
||||
{
|
||||
char id_str[37];
|
||||
uuid_unparse(this->id, id_str);
|
||||
Json::Value controller_json;
|
||||
controller_json["name"] = this->name;
|
||||
controller_json["id"] = id_str;
|
||||
controller_json["ip"] = this->ip;
|
||||
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, int bind_func_param)
|
||||
{
|
||||
helpers::sql_filter_builder *filters[1];
|
||||
helpers::sql_filter_builder filter
|
||||
{
|
||||
key,
|
||||
value,
|
||||
bind_func,
|
||||
bind_func_param,
|
||||
";"
|
||||
};
|
||||
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, char *payload, uint32_t payload_size)
|
||||
{
|
||||
LOG_DEBUG << "Commanding " << command_code;
|
||||
|
||||
int bytes_transferred;
|
||||
char port_str[6];
|
||||
sprintf(port_str, "%d", this->port);
|
||||
|
||||
int fd_controller = helpers::open_tcp_connection(this->ip, port_str);
|
||||
|
||||
if(fd_controller == -1)
|
||||
{
|
||||
LOG_ERROR << "Can't open command socket " << this->ip << ":" << port_str;
|
||||
return false;
|
||||
}
|
||||
|
||||
if((bytes_transferred = send(fd_controller, &payload_size, sizeof(payload_size), 0)) <= 0)
|
||||
{
|
||||
LOG_ERROR << "error during sending size";
|
||||
return false;
|
||||
}
|
||||
if((bytes_transferred = send(fd_controller, payload, payload_size, 0)) <= 0)
|
||||
{
|
||||
LOG_ERROR << "error during sending";
|
||||
return false;
|
||||
}
|
||||
|
||||
close(fd_controller);
|
||||
|
||||
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,54 +0,0 @@
|
|||
#ifndef EMGAUWA_CORE_controller_DBO_H
|
||||
#define EMGAUWA_CORE_controller_DBO_H
|
||||
|
||||
#include <string>
|
||||
#include <uuid/uuid.h>
|
||||
#include <sqlite3.h>
|
||||
#include <json/value.h>
|
||||
#include <helpers.h>
|
||||
#include "relay_dbo.h"
|
||||
|
||||
class controller_dbo
|
||||
{
|
||||
public:
|
||||
|
||||
uuid_t id;
|
||||
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, int bind_func_param);
|
||||
|
||||
static controller_dbo**
|
||||
get_by(helpers::sql_filter_builder **filters);
|
||||
|
||||
static controller_dbo**
|
||||
get_all();
|
||||
|
||||
bool
|
||||
command(int command_code, char *payload, uint32_t payload_size);
|
||||
|
||||
static void
|
||||
free_list(controller_dbo **controllers_list);
|
||||
};
|
||||
|
||||
|
||||
#endif //EMGAUWA_CORE_controller_DBO_H
|
145
models/junction_relay_schedule.c
Normal file
145
models/junction_relay_schedule.c
Normal file
|
@ -0,0 +1,145 @@
|
|||
#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;
|
||||
}
|
||||
|
||||
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 junction ids from database: %s\n", sqlite3_errstr(s));
|
||||
sqlite3_finalize(stmt);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
ids[row] = 0;
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
int
|
||||
junction_relay_schedule_get_schedule_id(uint8_t weekday, int relay_id)
|
||||
{
|
||||
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);
|
||||
|
||||
int *id_list = get_ids(stmt);
|
||||
int result = id_list[0];
|
||||
|
||||
free(id_list);
|
||||
return result;
|
||||
}
|
||||
|
||||
int*
|
||||
junction_relay_schedule_get_relays_ids(int schedule_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT relay_id FROM junction_relay_schedule WHERE schedule_id=?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||
|
||||
return get_ids(stmt);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <helpers.h>
|
||||
#include "junction_relay_schedule_dbo.h"
|
||||
#include "globals.h"
|
||||
|
||||
bool
|
||||
junction_relay_schedule_dbo::insert(uint8_t weekday, int relay_id, int schedule_id)
|
||||
{
|
||||
int rc;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "INSERT INTO junction_relay_schedule(weekday, schedule_id, relay_id) values (?1, ?2, ?3);", -1, &stmt, nullptr);
|
||||
|
||||
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)
|
||||
{
|
||||
printf("ERROR inserting data: %s\n", sqlite3_errmsg(globals::db));
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
junction_relay_schedule_dbo::get_schedule_id(uint8_t weekday, int relay_id)
|
||||
{
|
||||
int result = 0;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "SELECT schedule_id FROM junction_relay_schedule WHERE weekday=?1 AND relay_id=?2 LIMIT 1;", -1, &stmt, nullptr);
|
||||
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 Selecting relays from database: " << sqlite3_errstr(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
junction_relay_schedule_dbo::remove(uint8_t weekday, int relay_id, int schedule_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "DELETE FROM junction_relay_schedule WHERE weekday=?1 AND schedule_id=?2 AND relay_id=?3;", -1, &stmt, nullptr);
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
junction_relay_schedule_dbo::remove_for_relay(int relay_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "DELETE FROM junction_relay_schedule WHERE relay_id=?1;", -1, &stmt, nullptr);
|
||||
sqlite3_bind_int(stmt, 1, relay_id);
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc == SQLITE_DONE;
|
||||
}
|
||||
|
||||
bool
|
||||
junction_relay_schedule_dbo::remove_for_schedule(int schedule_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "DELETE FROM junction_relay_schedule WHERE schedule_id=?1;", -1, &stmt, nullptr);
|
||||
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc == SQLITE_DONE;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
#ifndef EMGAUWA_CORE_JUNCTION_RELAY_SCHEDULE_DBO_H
|
||||
#define EMGAUWA_CORE_JUNCTION_RELAY_SCHEDULE_DBO_H
|
||||
|
||||
#include <string>
|
||||
#include <sqlite3.h>
|
||||
#include <json/value.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <helpers.h>
|
||||
|
||||
namespace junction_relay_schedule_dbo
|
||||
{
|
||||
int
|
||||
get_schedule_id(uint8_t weekday, int relay_id);
|
||||
|
||||
bool
|
||||
insert(uint8_t weekday, int relay_id, int schedule_id);
|
||||
|
||||
bool
|
||||
remove(uint8_t weekday, int relay_id, int schedule_id);
|
||||
|
||||
bool
|
||||
remove_for_relay(int relay_id);
|
||||
|
||||
bool
|
||||
remove_for_schedule(int schedule_id);
|
||||
};
|
||||
|
||||
|
||||
#endif //EMGAUWA_CORE_JUNCTION_RELAY_SCHEDULE_DBO_H
|
194
models/junction_tag.c
Normal file
194
models/junction_tag.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
#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);
|
||||
|
||||
if(schedule_id)
|
||||
{
|
||||
sqlite3_bind_int(stmt, 2, schedule_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_bind_null(stmt, 2);
|
||||
}
|
||||
|
||||
if(relay_id)
|
||||
{
|
||||
sqlite3_bind_int(stmt, 3, relay_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_bind_null(stmt, 3);
|
||||
}
|
||||
|
||||
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);
|
||||
if(new_id != 0) // found row for other target (relay <> schedule)
|
||||
{
|
||||
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;
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <helpers.h>
|
||||
#include "junction_tag_dbo.h"
|
||||
#include "globals.h"
|
||||
|
||||
bool
|
||||
junction_tag_dbo::insert(int tag_id, int relay_id, int schedule_id)
|
||||
{
|
||||
int rc;
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "INSERT INTO junction_tag(tag_id, schedule_id, relay_id) values (?1, ?2, ?3);", -1, &stmt, nullptr);
|
||||
|
||||
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(globals::db));
|
||||
return false;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int*
|
||||
get_ids(sqlite3_stmt *stmt)
|
||||
{
|
||||
auto *ids = (int*)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: " << sqlite3_errstr(s);
|
||||
sqlite3_finalize(stmt);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
ids[row] = 0;
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
int*
|
||||
junction_tag_dbo::get_relays_for_tag_id(int tag_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "SELECT relay_id FROM junction_tag WHERE tag_id=?1;", -1, &stmt, nullptr);
|
||||
sqlite3_bind_int(stmt, 1, tag_id);
|
||||
|
||||
return get_ids(stmt);
|
||||
}
|
||||
|
||||
int*
|
||||
junction_tag_dbo::get_schedules_for_tag_id(int tag_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "SELECT schedule_id FROM junction_tag WHERE tag_id=?1;", -1, &stmt, nullptr);
|
||||
sqlite3_bind_int(stmt, 1, tag_id);
|
||||
|
||||
return get_ids(stmt);
|
||||
}
|
||||
|
||||
int*
|
||||
junction_tag_dbo::get_tags_for_relay_id(int relay_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "SELECT tag_id FROM junction_tag WHERE relay_id=?1;", -1, &stmt, nullptr);
|
||||
sqlite3_bind_int(stmt, 1, relay_id);
|
||||
|
||||
return get_ids(stmt);
|
||||
}
|
||||
|
||||
int*
|
||||
junction_tag_dbo::get_tags_for_schedule_id(int schedule_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "SELECT tag_id FROM junction_tag WHERE schedule_id=?1;", -1, &stmt, nullptr);
|
||||
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||
|
||||
return get_ids(stmt);
|
||||
}
|
||||
|
||||
bool
|
||||
junction_tag_dbo::remove(int tag_id, int relay_id, int schedule_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "DELETE FROM junction_tag WHERE tag_id=?1 AND schedule_id=?2 AND relay_id=?3;", -1, &stmt, nullptr);
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
junction_tag_dbo::remove_for_tag(int tag_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "DELETE FROM junction_tag WHERE tag_id=?1;", -1, &stmt, nullptr);
|
||||
sqlite3_bind_int(stmt, 1, tag_id);
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc == SQLITE_DONE;
|
||||
}
|
||||
|
||||
bool
|
||||
junction_tag_dbo::remove_for_relay(int relay_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "DELETE FROM junction_tag WHERE relay_id=?1;", -1, &stmt, nullptr);
|
||||
sqlite3_bind_int(stmt, 1, relay_id);
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc == SQLITE_DONE;
|
||||
}
|
||||
|
||||
bool
|
||||
junction_tag_dbo::remove_for_schedule(int schedule_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
sqlite3_prepare_v2(globals::db, "DELETE FROM junction_tag WHERE schedule_id=?1;", -1, &stmt, nullptr);
|
||||
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc == SQLITE_DONE;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
#ifndef EMGAUWA_CORE_JUNCTION_TAG_DBO_H
|
||||
#define EMGAUWA_CORE_JUNCTION_TAG_DBO_H
|
||||
|
||||
#include <string>
|
||||
#include <sqlite3.h>
|
||||
#include <json/value.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <helpers.h>
|
||||
|
||||
namespace junction_tag_dbo
|
||||
{
|
||||
int*
|
||||
get_relays_for_tag_id(int tag_id);
|
||||
|
||||
int*
|
||||
get_schedules_for_tag_id(int tag_id);
|
||||
|
||||
int*
|
||||
get_tags_for_relay_id(int relay_id);
|
||||
|
||||
int*
|
||||
get_tags_for_schedule_id(int schedule_id);
|
||||
|
||||
bool
|
||||
insert(int tag_id, int relay_id, int schedule_id);
|
||||
|
||||
bool
|
||||
remove(int tag_id, int relay_id, int schedule_id);
|
||||
|
||||
bool
|
||||
remove_for_tag(int tag_id);
|
||||
|
||||
bool
|
||||
remove_for_relay(int relay_id);
|
||||
|
||||
bool
|
||||
remove_for_schedule(int schedule_id);
|
||||
};
|
||||
|
||||
|
||||
#endif //EMGAUWA_CORE_JUNCTION_TAG_DBO_H
|
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_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_blob();
|
||||
};
|
||||
|
||||
#endif //EMGAUWA_CORE_PERIOD_LIST_H
|
376
models/relay.c
Normal file
376
models/relay.c
Normal file
|
@ -0,0 +1,376 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <cJSON.h>
|
||||
#include <logger.h>
|
||||
#include <database.h>
|
||||
#include <models/relay.h>
|
||||
#include <models/controller.h>
|
||||
#include <models/schedule.h>
|
||||
#include <models/junction_tag.h>
|
||||
#include <models/junction_relay_schedule.h>
|
||||
#include <models/tag.h>
|
||||
|
||||
static int
|
||||
db_update_insert(relay_t *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_int(stmt, 4, relay->controller_id);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc != SQLITE_DONE;
|
||||
}
|
||||
static relay_t*
|
||||
relay_db_select_mapper(sqlite3_stmt *stmt)
|
||||
{
|
||||
relay_t *new_relay = malloc(sizeof(relay_t));
|
||||
for(int i = 0; i < sqlite3_column_count(stmt); i++)
|
||||
{
|
||||
const char *name = sqlite3_column_name(stmt, i);
|
||||
switch(name[0])
|
||||
{
|
||||
case 'c': // controller_id
|
||||
new_relay->controller_id = sqlite3_column_int(stmt, i);
|
||||
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;
|
||||
}
|
||||
break;
|
||||
default: // ignore columns not implemented
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
schedule_t **schedules = schedule_get_relay_weekdays(new_relay->id);
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
if(schedules[i] == NULL)
|
||||
{
|
||||
LOG_ERROR("got only %d/7 schedules for relay_id %d\n", i, new_relay->id);
|
||||
relay_free(new_relay);
|
||||
free(schedules);
|
||||
return NULL;
|
||||
}
|
||||
new_relay->schedules[i] = schedules[i];
|
||||
}
|
||||
free(schedules); // don't free list, because contents are kept in relay->schedules
|
||||
|
||||
relay_reload_active_schedule(new_relay);
|
||||
|
||||
return new_relay;
|
||||
}
|
||||
|
||||
static relay_t**
|
||||
relay_db_select(sqlite3_stmt *stmt)
|
||||
{
|
||||
relay_t **all_relays = malloc(sizeof(relay_t*));
|
||||
|
||||
int row = 0;
|
||||
|
||||
while(true)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = sqlite3_step(stmt);
|
||||
if (s == SQLITE_ROW)
|
||||
{
|
||||
relay_t *new_relay = relay_db_select_mapper(stmt);
|
||||
row++;
|
||||
|
||||
all_relays = (relay_t**)realloc(all_relays, sizeof(relay_t*) * (row + 1));
|
||||
all_relays[row - 1] = new_relay;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(s == SQLITE_DONE)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("error selecting relays from database: %s\n", sqlite3_errstr(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
all_relays[row] = NULL;
|
||||
return all_relays;
|
||||
}
|
||||
|
||||
int
|
||||
relay_save(relay_t *relay)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
if(relay->id)
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "UPDATE relays set number = ?2, name = ?3, controller_id = ?4 WHERE id = ?1;", -1, &stmt, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_prepare_v2(global_database, "INSERT INTO relays(number, name, controller_id) values (?2, ?3, ?4);", -1, &stmt, NULL);
|
||||
}
|
||||
|
||||
int result = db_update_insert(relay, stmt);
|
||||
|
||||
if(result)
|
||||
{
|
||||
if(relay->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
|
||||
{
|
||||
if(relay->id == 0)
|
||||
{
|
||||
relay->id = sqlite3_last_insert_rowid(global_database);
|
||||
}
|
||||
}
|
||||
|
||||
junction_relay_schedule_remove_for_relay(relay->id);
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
junction_relay_schedule_insert(i, relay->id, relay->schedules[i]->id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
relay_remove(relay_t *relay)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
if(!relay->id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3_prepare_v2(global_database, "DELETE FROM relays WHERE id=?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, relay->id);
|
||||
|
||||
int rc = sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc != SQLITE_DONE;
|
||||
}
|
||||
|
||||
void
|
||||
relay_reload_active_schedule(relay_t *relay)
|
||||
{
|
||||
time_t timestamp = time(NULL);
|
||||
struct tm *time_struct = localtime(×tamp);
|
||||
relay->active_schedule = relay->schedules[helper_get_weekday(time_struct)];
|
||||
}
|
||||
|
||||
void
|
||||
relay_free(relay_t *relay)
|
||||
{
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
schedule_free(relay->schedules[i]);
|
||||
}
|
||||
free(relay);
|
||||
}
|
||||
|
||||
void
|
||||
relay_free_list(relay_t **relays)
|
||||
{
|
||||
for(int i = 0; relays[i] != NULL; ++i)
|
||||
{
|
||||
relay_free(relays[i]);
|
||||
}
|
||||
free(relays);
|
||||
}
|
||||
|
||||
cJSON*
|
||||
relay_to_json(relay_t *relay)
|
||||
{
|
||||
relay_reload_active_schedule(relay);
|
||||
|
||||
cJSON *json = cJSON_CreateObject();
|
||||
|
||||
cJSON *json_number = cJSON_CreateNumber(relay->number);
|
||||
if(json_number == NULL)
|
||||
{
|
||||
LOG_DEBUG("failed to make number\n");
|
||||
cJSON_Delete(json);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(json, "number", json_number);
|
||||
|
||||
cJSON *json_name = cJSON_CreateString(relay->name);
|
||||
if(json_name == NULL)
|
||||
{
|
||||
LOG_DEBUG("failed to make name\n");
|
||||
cJSON_Delete(json);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(json, "name", json_name);
|
||||
|
||||
controller_t *controller = controller_get_by_id(relay->controller_id);
|
||||
if(!controller)
|
||||
{
|
||||
LOG_WARN("failed to get controller\n");
|
||||
cJSON_Delete(json);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char uuid_str[UUID_STR_LEN];
|
||||
uuid_unparse(controller->uid, uuid_str);
|
||||
cJSON *json_controller_id = cJSON_CreateString(uuid_str);
|
||||
if(json_controller_id == NULL)
|
||||
{
|
||||
LOG_DEBUG("failed to make controller id\n");
|
||||
cJSON_Delete(json);
|
||||
return NULL;
|
||||
}
|
||||
cJSON_AddItemToObject(json, "controller_id", json_controller_id);
|
||||
|
||||
controller_free(controller);
|
||||
|
||||
cJSON_AddItemToObject(json, "active_schedule", schedule_to_json(relay->active_schedule));
|
||||
|
||||
cJSON *json_schedules = cJSON_CreateArray();
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
cJSON_AddItemToArray(json_schedules, schedule_to_json(relay->schedules[i]));
|
||||
}
|
||||
cJSON_AddItemToObject(json, "schedules", json_schedules);
|
||||
|
||||
cJSON *json_tags = cJSON_CreateArray();
|
||||
int *tags_ids = junction_tag_get_tags_for_relay_id(relay->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;
|
||||
}
|
||||
|
||||
relay_t*
|
||||
relay_get_by_id(int id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM relays WHERE id = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, id);
|
||||
|
||||
relay_t **sql_result = relay_db_select(stmt);
|
||||
|
||||
relay_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
relay_t*
|
||||
relay_get_by_uid(uuid_t uid)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM relays WHERE uid = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||
|
||||
relay_t **sql_result = relay_db_select(stmt);
|
||||
|
||||
relay_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
relay_t**
|
||||
relay_get_all()
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM relays;", -1, &stmt, NULL);
|
||||
|
||||
return relay_db_select(stmt);
|
||||
}
|
||||
|
||||
relay_t**
|
||||
relay_get_with_schedule(int schedule_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT DISTINCT relays.* FROM relays INNER JOIN junction_relay_schedule ON relays.id == junction_relay_schedule.relay_id WHERE junction_relay_schedule.schedule_id = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, schedule_id);
|
||||
|
||||
return relay_db_select(stmt);
|
||||
}
|
||||
|
||||
relay_t*
|
||||
relay_get_for_controller(int controller_id, int relay_num)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM relays WHERE controller_id = ?1 AND number = ?2;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, controller_id);
|
||||
sqlite3_bind_int(stmt, 2, relay_num);
|
||||
|
||||
relay_t **sql_result = relay_db_select(stmt);
|
||||
|
||||
relay_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
relay_t**
|
||||
relay_get_by_controller_id(int controller_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM relays WHERE controller_id = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, controller_id);
|
||||
|
||||
return relay_db_select(stmt);
|
||||
|
||||
}
|
|
@ -1,290 +0,0 @@
|
|||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <trantor/utils/Logger.h>
|
||||
#include <helpers.h>
|
||||
#include "relay_dbo.h"
|
||||
#include "globals.h"
|
||||
#include "junction_relay_schedule_dbo.h"
|
||||
#include "junction_tag_dbo.h"
|
||||
#include "tag_dbo.h"
|
||||
#include "controller_dbo.h"
|
||||
#include "schedule_dbo.h"
|
||||
|
||||
static bool relay_db_update_insert(relay_dbo *relay, sqlite3_stmt *stmt)
|
||||
{
|
||||
int rc;
|
||||
|
||||
junction_relay_schedule_dbo::remove_for_relay(relay->id);
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
junction_relay_schedule_dbo::insert(i, relay->id, relay->schedules[i]->id);
|
||||
}
|
||||
|
||||
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_blob(stmt, 4, relay->controller_id, sizeof(uuid_t), 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 'c': // controller_id
|
||||
uuid_copy(new_relay->controller_id, (const unsigned char*)sqlite3_column_blob(stmt, i));
|
||||
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;
|
||||
}
|
||||
break;
|
||||
default: // ignore columns not implemented
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
int schedule_id = junction_relay_schedule_dbo::get_schedule_id(i, new_relay->id);
|
||||
new_relay->schedules[i] = schedule_dbo::get_by_id_or_off(schedule_id);
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
relay_dbo::save()
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
if(this->id)
|
||||
{
|
||||
sqlite3_prepare_v2(globals::db, "UPDATE relays set number = ?2, name = ?3, controller_id = ?4 WHERE id = ?1;", -1, &stmt, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_prepare_v2(globals::db, "INSERT INTO relays(number, name, controller_id) values (?2, ?3, ?4);", -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()
|
||||
{
|
||||
this->active_schedule = this->schedules[helpers::get_day_of_week()];
|
||||
|
||||
char controller_id_str[37];
|
||||
uuid_unparse(this->controller_id, controller_id_str);
|
||||
char active_schedule_uid_str[37];
|
||||
schedule_dbo::unparse_uid(this->active_schedule->uid, active_schedule_uid_str);
|
||||
|
||||
Json::Value schedules_json(Json::arrayValue);
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
schedules_json.append(this->schedules[i]->to_json());
|
||||
}
|
||||
|
||||
Json::Value relay_json;
|
||||
relay_json["name"] = this->name;
|
||||
relay_json["number"] = this->number;
|
||||
relay_json["active_schedule_id"] = active_schedule_uid_str;
|
||||
relay_json["controller_id"] = controller_id_str;
|
||||
relay_json["active_schedule"] = this->active_schedule->to_json();
|
||||
relay_json["schedules"] = schedules_json;
|
||||
|
||||
Json::Value tags_json(Json::arrayValue);
|
||||
int *tags_ids = junction_tag_dbo::get_tags_for_relay_id(this->id);
|
||||
if(tags_ids != nullptr)
|
||||
{
|
||||
|
||||
int tags_count;
|
||||
for(tags_count = 0; tags_ids[tags_count] != 0; ++tags_count);
|
||||
|
||||
char **tags = (char**)malloc(sizeof(char*) * tags_count);
|
||||
|
||||
for(int i = 0; i < tags_count; ++i)
|
||||
{
|
||||
tags[i] = tag_dbo::get_tag(tags_ids[i]);
|
||||
tags_json[i] = tags[i];
|
||||
}
|
||||
}
|
||||
relay_json["tags"] = tags_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, int bind_func_param)
|
||||
{
|
||||
helpers::sql_filter_builder *filters[1];
|
||||
helpers::sql_filter_builder filter
|
||||
{
|
||||
key,
|
||||
value,
|
||||
bind_func,
|
||||
bind_func_param,
|
||||
";"
|
||||
};
|
||||
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_by_id(int id)
|
||||
{
|
||||
relay_dbo **relays = relay_dbo::get_by_simple("id", &id, (intptr_t)&sqlite3_bind_int, 0);
|
||||
relay_dbo *result = relays[0];
|
||||
|
||||
free(relays);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
relay_dbo*
|
||||
relay_dbo::get_relay_for_controller(uuid_t controller_id, int relay_num)
|
||||
{
|
||||
helpers::sql_filter_builder *filters[2];
|
||||
helpers::sql_filter_builder filter(
|
||||
"number",
|
||||
&relay_num,
|
||||
(intptr_t)&sqlite3_bind_int,
|
||||
0,
|
||||
"AND"
|
||||
);
|
||||
helpers::sql_filter_builder filter2(
|
||||
"controller_id",
|
||||
(void*)controller_id,
|
||||
(intptr_t)sqlite3_bind_blob,
|
||||
sizeof(uuid_t),
|
||||
";"
|
||||
);
|
||||
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(uuid_t search_controller_id, int relay_num)
|
||||
{
|
||||
controller_dbo **controllers = controller_dbo::get_by_simple("id", search_controller_id, (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
|
||||
|
||||
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,58 +0,0 @@
|
|||
#ifndef EMGAUWA_CORE_RELAY_DBO_H
|
||||
#define EMGAUWA_CORE_RELAY_DBO_H
|
||||
|
||||
#include <string>
|
||||
#include <sqlite3.h>
|
||||
#include <json/value.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <helpers.h>
|
||||
#include "schedule_dbo.h"
|
||||
|
||||
class relay_dbo
|
||||
{
|
||||
public:
|
||||
|
||||
int id;
|
||||
char name[128];
|
||||
int number;
|
||||
uuid_t controller_id;
|
||||
int active_schedule_id;
|
||||
schedule_dbo *active_schedule;
|
||||
schedule_dbo *schedules[7];
|
||||
|
||||
void
|
||||
reload_active_schedule();
|
||||
|
||||
bool
|
||||
save();
|
||||
|
||||
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, int bind_func_param);
|
||||
|
||||
static relay_dbo*
|
||||
get_by_id(int id);
|
||||
|
||||
static relay_dbo**
|
||||
get_by(helpers::sql_filter_builder **filters);
|
||||
|
||||
static relay_dbo*
|
||||
get_relay_for_controller(uuid_t controller_id, int relay_num);
|
||||
|
||||
static bool
|
||||
valid_num_for_controller(uuid_t search_controller_id, int relay_num);
|
||||
|
||||
static relay_dbo**
|
||||
get_all();
|
||||
};
|
||||
|
||||
|
||||
#endif //EMGAUWA_CORE_RELAY_DBO_H
|
461
models/schedule.c
Normal file
461
models/schedule.c
Normal file
|
@ -0,0 +1,461 @@
|
|||
#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("error selecting schedules from database: %s\n", sqlite3_errstr(s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
all_schedules[row] = NULL;
|
||||
return all_schedules;
|
||||
}
|
||||
|
||||
int
|
||||
schedule_save(schedule_t *schedule)
|
||||
{
|
||||
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
|
||||
{
|
||||
if(!schedule->id)
|
||||
{
|
||||
schedule->id = sqlite3_last_insert_rowid(global_database);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
schedule_remove(schedule_t *schedule)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
if(!schedule->id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
sqlite3_prepare_v2(global_database, "DELETE FROM schedules WHERE id=?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, schedule->id);
|
||||
|
||||
int rc = sqlite3_step(stmt);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return rc != SQLITE_DONE;
|
||||
}
|
||||
|
||||
int
|
||||
schedule_is_protected(schedule_t *schedule)
|
||||
{
|
||||
uuid_t tmp_uuid;
|
||||
|
||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||
memcpy(tmp_uuid, "off", 3);
|
||||
if(uuid_compare(schedule->uid, tmp_uuid) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||
memcpy(tmp_uuid, "on", 2);
|
||||
if(uuid_compare(schedule->uid, tmp_uuid) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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_by_id_or_off(int id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules WHERE id = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, id);
|
||||
|
||||
schedule_t **sql_result = schedule_db_select(stmt);
|
||||
|
||||
schedule_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
if(result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
uuid_t tmp_uuid;
|
||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||
memcpy(tmp_uuid, "off", 3);
|
||||
|
||||
return schedule_get_by_uid(tmp_uuid);
|
||||
}
|
||||
|
||||
schedule_t*
|
||||
schedule_get_by_id(int id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules WHERE id = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, id);
|
||||
|
||||
schedule_t **sql_result = schedule_db_select(stmt);
|
||||
|
||||
schedule_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
schedule_t*
|
||||
schedule_get_by_uid_or_off(uuid_t uid)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules WHERE uid = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||
|
||||
schedule_t **sql_result = schedule_db_select(stmt);
|
||||
|
||||
schedule_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
if(result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
uuid_t tmp_uuid;
|
||||
memset(tmp_uuid, 0, sizeof(uuid_t));
|
||||
memcpy(tmp_uuid, "off", 3);
|
||||
|
||||
return schedule_get_by_uid(tmp_uuid);
|
||||
}
|
||||
|
||||
schedule_t*
|
||||
schedule_get_by_uid(uuid_t uid)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT * FROM schedules WHERE uid = ?1;", -1, &stmt, NULL);
|
||||
sqlite3_bind_blob(stmt, 1, uid, sizeof(uuid_t), SQLITE_STATIC);
|
||||
|
||||
schedule_t **sql_result = schedule_db_select(stmt);
|
||||
|
||||
schedule_t *result = sql_result[0];
|
||||
free(sql_result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
schedule_t**
|
||||
schedule_get_relay_weekdays(int relay_id)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
|
||||
sqlite3_prepare_v2(global_database, "SELECT schedules.* FROM schedules INNER JOIN junction_relay_schedule ON schedules.id == junction_relay_schedule.schedule_id WHERE junction_relay_schedule.relay_id = ?1 ORDER BY junction_relay_schedule.weekday ASC", -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, relay_id);
|
||||
|
||||
return schedule_db_select(stmt);
|
||||
}
|
||||
|
||||
schedule_t**
|
||||
schedule_get_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);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue