add: weekly schedule support

This commit is contained in:
Tobias Reisinger 2020-04-23 17:00:12 +02:00
parent b3f75f4004
commit 44a83cd2c5
11 changed files with 220 additions and 79 deletions

View file

@ -14,7 +14,8 @@ endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -g -latomic") set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -Wall -Wextra -Werror -g -latomic")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
########## ##########
# If you include the drogon source code locally in your project, use this method to add drogon # If you include the drogon source code locally in your project, use this method to add drogon
@ -76,6 +77,16 @@ add_custom_target(run
DEPENDS core DEPENDS core
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR} WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
) )
add_custom_target(debug
COMMAND valgrind ./core
DEPENDS core
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
)
add_custom_target(debug-full
COMMAND valgrind --leak-check=full --show-leak-kinds=all ./core
DEPENDS core
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
)
add_custom_target(test 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 ${CMAKE_SOURCE_DIR}/config.testing.json

View file

@ -93,7 +93,7 @@ controllers::put_relays_one_by_id_and_num(const HttpRequestPtr &req,
Json::Value body = *req->getJsonObject(); Json::Value body = *req->getJsonObject();
uuid_t active_schedule_id; uuid_t active_schedule_id;
if(schedule_dbo::parse_uid(body["active_schedule"].asCString(), active_schedule_id)) if(schedule_dbo::parse_uid(body["active_schedule"]["id"].asCString(), active_schedule_id))
{ {
LOG_DEBUG << "bad active_schedule uuid"; LOG_DEBUG << "bad active_schedule uuid";
auto resp = HttpResponse::newHttpResponse(); auto resp = HttpResponse::newHttpResponse();
@ -102,18 +102,48 @@ controllers::put_relays_one_by_id_and_num(const HttpRequestPtr &req,
return; return;
} }
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;
}
}
relay_dbo *relay = relay_dbo::get_relay_for_controller(controller_id, relay_num); relay_dbo *relay = relay_dbo::get_relay_for_controller(controller_id, relay_num);
schedule_dbo **schedule = schedule_dbo::get_by_simple("uid", active_schedule_id, (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
schedule_dbo **schedule_list = schedule_dbo::get_by_simple("uid", active_schedule_id, (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
schedule_dbo *active_schedule = schedule_list[0];
free(schedule_list);
schedule_dbo *schedules[7];
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);
}
bool db_action_result; bool db_action_result;
if(relay) if(relay)
{ {
strncpy(relay->name, body["name"].asCString(), 127); strncpy(relay->name, body["name"].asCString(), 127);
relay->active_schedule_id = schedule[0]->id;
uuid_copy(relay->controller_id, controller_id); uuid_copy(relay->controller_id, controller_id);
relay->reload_active_schedule(); for(int i = 0; i < 7; ++i)
{
relay->schedules[i] = schedules[i];
}
relay->schedules[helpers::get_day_of_week()] = active_schedule;
relay->active_schedule = active_schedule;
db_action_result = relay->update(); db_action_result = relay->update();
} }
@ -123,10 +153,15 @@ controllers::put_relays_one_by_id_and_num(const HttpRequestPtr &req,
relay->number = relay_num; relay->number = relay_num;
strncpy(relay->name, body["name"].asCString(), 127); strncpy(relay->name, body["name"].asCString(), 127);
relay->active_schedule_id = schedule[0]->id; for(int i = 0; i < 7; ++i)
uuid_copy(relay->controller_id, controller_id); {
relay->schedules[i] = schedules[i];
}
relay->reload_active_schedule(); relay->schedules[helpers::get_day_of_week()] = active_schedule;
relay->active_schedule = active_schedule;
uuid_copy(relay->controller_id, controller_id);
db_action_result = relay->insert(); db_action_result = relay->insert();
} }

View file

@ -18,33 +18,87 @@ void valid_json::doFilter(const HttpRequestPtr &req,
bool is_valid = true; bool is_valid = true;
// level 1
is_valid &= body["name"].type() == Json::ValueType::stringValue; is_valid &= body["name"].type() == Json::ValueType::stringValue;
is_valid &= body["active_schedule"].type() == Json::ValueType::stringValue; is_valid &= body["active_schedule"].type() == Json::ValueType::objectValue;
is_valid &= body["schedules"].type() == Json::ValueType::arrayValue;
uuid_t active_schedule_id; // level 2
if(schedule_dbo::parse_uid(body["active_schedule"].asCString(), active_schedule_id)) if(is_valid)
{ {
LOG_DEBUG << "parse_uid failed"; is_valid &= body["active_schedule"]["id"].type() == Json::ValueType::stringValue;
for(int i = 0; i < 7; ++i)
{
is_valid &= body["schedules"][i].type() == Json::ValueType::objectValue;
}
}
// level 3
if(is_valid)
{
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(); auto res = drogon::HttpResponse::newHttpResponse();
res->setStatusCode(k400BadRequest); res->setStatusCode(k400BadRequest);
fcb(res); fcb(res);
return;
} }
if(is_valid) uuid_t active_schedule_id;
if(schedule_dbo::parse_uid(body["active_schedule"]["id"].asCString(), active_schedule_id))
{ {
schedule_dbo **schedules = schedule_dbo::get_by_simple("uid", active_schedule_id, (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t)); 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;
}
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;
}
schedules = schedule_dbo::get_by_simple("uid", schedules_ids[i], (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
bool schedule_found = schedules[0] != nullptr; bool schedule_found = schedules[0] != nullptr;
schedule_dbo::free_list(schedules); schedule_dbo::free_list(schedules);
if(schedule_found) if(!schedule_found)
{ {
//Passed LOG_DEBUG << "could not find schedule " << i;
fccb(); auto res = drogon::HttpResponse::newHttpResponse();
res->setStatusCode(k400BadRequest);
fcb(res);
return; return;
} }
} }
//Check failed
LOG_DEBUG << "schedule not found"; //Passed
auto res = drogon::HttpResponse::newHttpResponse(); fccb();
res->setStatusCode(k400BadRequest); return;
fcb(res);
} }

View file

@ -39,6 +39,9 @@ namespace helpers
int int
migrate_sql(); migrate_sql();
int
get_day_of_week();
} }
#endif //EMGAUWA_CORE_HELPERS_H #endif //EMGAUWA_CORE_HELPERS_H

View file

@ -0,0 +1,12 @@
#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;
}

View file

@ -34,7 +34,6 @@ main(int argc, char** argv)
signal(SIGINT, terminate); signal(SIGINT, terminate);
signal(SIGABRT, terminate); signal(SIGABRT, terminate);
signal(SIGTERM, terminate); signal(SIGTERM, terminate);
signal(SIGKILL, terminate);
const char* config_file_name = "config.json"; const char* config_file_name = "config.json";

View file

@ -11,10 +11,16 @@ static bool relay_db_update_insert(relay_dbo *relay, sqlite3_stmt *stmt)
{ {
int rc; int rc;
uint32_t schedules_ids[7];
for(int i = 0; i < 7; ++i)
{
schedules_ids[i] = relay->schedules[i]->id;
}
sqlite3_bind_int(stmt, 1, relay->id); sqlite3_bind_int(stmt, 1, relay->id);
sqlite3_bind_int(stmt, 2, relay->number); sqlite3_bind_int(stmt, 2, relay->number);
sqlite3_bind_text(stmt, 3, relay->name, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 3, relay->name, -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 4, relay->active_schedule_id); sqlite3_bind_blob(stmt, 4, schedules_ids, sizeof(uint32_t) * 7, SQLITE_STATIC);
sqlite3_bind_blob(stmt, 5, relay->controller_id, sizeof(uuid_t), SQLITE_STATIC); sqlite3_bind_blob(stmt, 5, relay->controller_id, sizeof(uuid_t), SQLITE_STATIC);
sqlite3_bind_text(stmt, 6, relay->tag, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 6, relay->tag, -1, SQLITE_STATIC);
@ -34,15 +40,13 @@ static relay_dbo*
relay_db_select_mapper(sqlite3_stmt *stmt) relay_db_select_mapper(sqlite3_stmt *stmt)
{ {
auto *new_relay = new relay_dbo(); auto *new_relay = new relay_dbo();
const char* new_tag; const char *new_tag;
const uint32_t *new_relays_ids;
for(int i = 0; i < sqlite3_column_count(stmt); i++) for(int i = 0; i < sqlite3_column_count(stmt); i++)
{ {
const char *name = sqlite3_column_name(stmt, i); const char *name = sqlite3_column_name(stmt, i);
switch(name[0]) switch(name[0])
{ {
case 'a': // active_schedule_dbid
new_relay->active_schedule_id = sqlite3_column_int(stmt, i);
break;
case 'c': // controller_id case 'c': // controller_id
uuid_copy(new_relay->controller_id, (const unsigned char*)sqlite3_column_blob(stmt, i)); uuid_copy(new_relay->controller_id, (const unsigned char*)sqlite3_column_blob(stmt, i));
break; break;
@ -62,6 +66,13 @@ relay_db_select_mapper(sqlite3_stmt *stmt)
break; break;
} }
break; break;
case 's': // schedules_ids
new_relays_ids = (const uint32_t*)sqlite3_column_blob(stmt, i);
for(int i = 0; i < 7; ++i)
{
new_relay->schedules[i] = schedule_dbo::get_by_id_or_off(new_relays_ids[i]);
}
break;
case 't': // tag case 't': // tag
new_tag = (const char*)sqlite3_column_text(stmt, i); new_tag = (const char*)sqlite3_column_text(stmt, i);
new_relay->tag[0] = '\0'; new_relay->tag[0] = '\0';
@ -93,8 +104,6 @@ relay_db_select(sqlite3_stmt *stmt)
{ {
relay_dbo *new_relay = relay_db_select_mapper(stmt); relay_dbo *new_relay = relay_db_select_mapper(stmt);
new_relay->reload_active_schedule();
row++; row++;
all_relays = (relay_dbo**)realloc(all_relays, sizeof(relay_dbo*) * (row + 1)); all_relays = (relay_dbo**)realloc(all_relays, sizeof(relay_dbo*) * (row + 1));
@ -120,35 +129,12 @@ relay_db_select(sqlite3_stmt *stmt)
return all_relays; return all_relays;
} }
void
relay_dbo::reload_active_schedule()
{
schedule_dbo **schedules = schedule_dbo::get_by_simple("id", &this->active_schedule_id, (intptr_t)&sqlite3_bind_int, 0);
if(!schedules[0])
{
free(schedules);
uuid_t off_uuid;
memset(off_uuid, 0, sizeof(uuid_t));
memcpy(off_uuid, "off", 3);
schedules = schedule_dbo::get_by_simple("uid", off_uuid, (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
if(schedules[0])
{
this->active_schedule_id = schedules[0]->id;
}
}
this->active_schedule = schedules[0];
free(schedules);
}
bool bool
relay_dbo::update() relay_dbo::update()
{ {
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
sqlite3_prepare_v2(globals::db, "UPDATE relays set number = ?2, name = ?3, active_schedule_id = ?4, controller_id = ?5, tag = ?6 WHERE id = ?1;", -1, &stmt, nullptr); sqlite3_prepare_v2(globals::db, "UPDATE relays set number = ?2, name = ?3, schedules_ids = ?4, controller_id = ?5, tag = ?6 WHERE id = ?1;", -1, &stmt, nullptr);
return relay_db_update_insert(this, stmt); return relay_db_update_insert(this, stmt);
} }
@ -158,7 +144,7 @@ relay_dbo::insert()
{ {
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
sqlite3_prepare_v2(globals::db, "INSERT INTO relays(number, name, active_schedule_id, controller_id, tag) values (?2, ?3, ?4, ?5, ?6);", -1, &stmt, nullptr); sqlite3_prepare_v2(globals::db, "INSERT INTO relays(number, name, schedules_ids, controller_id, tag) values (?2, ?3, ?4, ?5, ?6);", -1, &stmt, nullptr);
return relay_db_update_insert(this, stmt); return relay_db_update_insert(this, stmt);
} }
@ -181,16 +167,26 @@ relay_dbo::remove()
Json::Value Json::Value
relay_dbo::to_json() relay_dbo::to_json()
{ {
this->active_schedule = this->schedules[helpers::get_day_of_week()];
char controller_id_str[37]; char controller_id_str[37];
uuid_unparse(this->controller_id, controller_id_str); uuid_unparse(this->controller_id, controller_id_str);
char active_schedule_uid_str[37]; char active_schedule_uid_str[37];
schedule_dbo::unparse_uid(this->active_schedule->uid, active_schedule_uid_str); 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; Json::Value relay_json;
relay_json["name"] = this->name; relay_json["name"] = this->name;
relay_json["number"] = this->number; relay_json["number"] = this->number;
relay_json["active_schedule_id"] = active_schedule_uid_str; relay_json["active_schedule_id"] = active_schedule_uid_str;
relay_json["controller_id"] = controller_id_str; relay_json["controller_id"] = controller_id_str;
relay_json["active_schedule"] = this->active_schedule->to_json(); relay_json["active_schedule"] = this->active_schedule->to_json();
relay_json["schedules"] = schedules_json;
relay_json["tag"] = this->tag; relay_json["tag"] = this->tag;
return relay_json; return relay_json;

View file

@ -19,6 +19,7 @@ public:
int active_schedule_id; int active_schedule_id;
char tag[64]; char tag[64];
schedule_dbo *active_schedule; schedule_dbo *active_schedule;
schedule_dbo *schedules[7];
void void
reload_active_schedule(); reload_active_schedule();
@ -41,6 +42,9 @@ public:
static relay_dbo** static relay_dbo**
get_by_simple(const char *key, const void *value, intptr_t bind_func, int bind_func_param); get_by_simple(const char *key, const void *value, intptr_t bind_func, int bind_func_param);
static relay_dbo*
get_by_id_or_off(const int id);
static relay_dbo** static relay_dbo**
get_by(helpers::sql_filter_builder **filters); get_by(helpers::sql_filter_builder **filters);

View file

@ -183,6 +183,29 @@ schedule_dbo::get_by_simple(const char *key, const void *value, intptr_t bind_fu
return schedule_db_select(stmt); return schedule_db_select(stmt);
} }
schedule_dbo*
schedule_dbo::get_by_id_or_off(const int id)
{
schedule_dbo **schedules = schedule_dbo::get_by_simple("id", &id, (intptr_t)&sqlite3_bind_int, 0);
if(!schedules[0])
{
free(schedules);
uuid_t off_uuid;
memset(off_uuid, 0, sizeof(uuid_t));
memcpy(off_uuid, "off", 3);
schedules = schedule_dbo::get_by_simple("uid", off_uuid, (intptr_t)&sqlite3_bind_blob, sizeof(uuid_t));
if(!schedules[0])
{
LOG_FATAL << "schedule with uid 'off' not found";
exit(1);
}
}
schedule_dbo *schedule = schedules[0];
free(schedules);
return schedule;
}
schedule_dbo** schedule_dbo**
schedule_dbo::get_by(helpers::sql_filter_builder **filters) schedule_dbo::get_by(helpers::sql_filter_builder **filters)
{ {

View file

@ -38,6 +38,9 @@ public:
static schedule_dbo** static schedule_dbo**
get_by_simple(const char *key, const void *value, intptr_t bind_func, int bind_func_param); get_by_simple(const char *key, const void *value, intptr_t bind_func, int bind_func_param);
static schedule_dbo*
get_by_id_or_off(const int id);
static schedule_dbo** static schedule_dbo**
get_by(helpers::sql_filter_builder **filters); get_by(helpers::sql_filter_builder **filters);

View file

@ -1,51 +1,52 @@
create table meta create table meta
( (
version_num int not null version_num INTEGER
NOT NULL
); );
create table controllers create table controllers
( (
id VARCHAR(33) not null id VARCHAR(64)
primary key NOT NULL
unique, PRIMARY KEY
UNIQUE,
name VARCHAR(128), name VARCHAR(128),
ip VARCHAR(16), ip VARCHAR(16),
port INTEGER, port INTEGER,
relay_count INTEGER, relay_count INTEGER,
tag varchar(64), tag varchar(64),
active BOOLEAN not null active BOOLEAN
NOT NULL
); );
create table relays create table relays
( (
id INTEGER id INTEGER
not null PRIMARY KEY
primary key AUTOINCREMENT,
unique, name VARCHAR(128),
name VARCHAR(128), number INTEGER
number INTEGER NOT NULL,
not null, controller_id VARCHAR(33)
controller_id VARCHAR(33) NOT NULL
not null REFERENCES CONTROLLERS (ID),
references controllers (id), schedules_ids INTEGER
active_schedule_id int REFERENCES SCHEDULES (ID),
references schedules (id), tag vARCHAR(64)
tag vARCHAR(64)
); );
create table schedules create table schedules
( (
id INTEGER id INTEGER
not null PRIMARY KEY
primary key AUTOINCREMENT,
unique,
uid VARCHAR(33) uid VARCHAR(33)
not null NOT NULL
unique, UNIQUE,
name VARCHAR(128), name VARCHAR(128),
periods BLOB, periods BLOB,
tag vARCHAR(64) tag vARCHAR(64)
); );
INSERT INTO schedules (uid, name, periods) VALUES (x'6f666600000000000000000000000000', 'off', x'00'); INSERT INTO schedules (uid, name, periods) VALUES (x'6f666600000000000000000000000000', 'off', x'00');
INSERT INTO schedules (uid, name, periods) VALUES (x'6f6e0000000000000000000000000000', 'on', x'010000009F05'); INSERT INTO schedules (uid, name, periods) VALUES (x'6f6e0000000000000000000000000000', 'on', x'010000009F05');