diff --git a/controller.ini b/controller.ini index f6a83ae..95e2198 100644 --- a/controller.ini +++ b/controller.ini @@ -3,7 +3,7 @@ name = new emgauwa device discovery-port = 4421 relay-count = 10 database = controller_db.lmdb -log-level = debug +log-level = info [relay-0] driver = piface diff --git a/handlers/command.c b/handlers/command.c index 1935d32..f3ed3d1 100644 --- a/handlers/command.c +++ b/handlers/command.c @@ -17,25 +17,39 @@ #include #include +static void +handler_command_set_name(mpack_node_t map, controller_t *controller) +{ + char name_buffer[MAX_NAME_LENGTH + 1]; + mpack_node_copy_cstr(mpack_node_map_uint(map, COMMAND_MAPPING_NAME), name_buffer, MAX_NAME_LENGTH + 1); + controller_set_name(controller, name_buffer); +} + static void handler_command_set_schedule(mpack_node_t map, controller_t *controller) { uint8_t relay_num = mpack_node_u8(mpack_node_map_uint(map, COMMAND_MAPPING_RELAY_NUM)); - int uid_size = sizeof(uuid_t); - uuid_t schedule_id; - memcpy(schedule_id, mpack_node_data(mpack_node_map_uint(map, COMMAND_MAPPING_SCHEDULE_ID)), uid_size); - - uint16_t periods_count = mpack_node_u16(mpack_node_map_uint(map, COMMAND_MAPPING_PERIODS_COUNT)); - uint16_t *periods = (uint16_t*)mpack_node_bin_data(mpack_node_map_uint(map, COMMAND_MAPPING_PERIODS_BLOB)); - relay_t *target_relay = controller->relays[relay_num]; - if(target_relay->schedule) + mpack_node_t schedules_array = mpack_node_map_uint(map, COMMAND_MAPPING_SCHEDULES_ARRAY); + + for(int i = 0; i < 7; ++i) { - schedule_free(target_relay->schedule); + mpack_node_t schedules_map = mpack_node_array_at(schedules_array, i); + + uuid_t schedule_id; + memcpy(schedule_id, mpack_node_data(mpack_node_map_uint(schedules_map, COMMAND_MAPPING_SCHEDULE_ID)), sizeof(uuid_t)); + + uint16_t periods_count = mpack_node_u16(mpack_node_map_uint(schedules_map, COMMAND_MAPPING_PERIODS_COUNT)); + uint16_t *periods = (uint16_t*)mpack_node_bin_data(mpack_node_map_uint(schedules_map, COMMAND_MAPPING_PERIODS_BLOB)); + + if(target_relay->schedules[i]) + { + schedule_free(target_relay->schedules[i]); + } + target_relay->schedules[i] = schedule_create(schedule_id, i, periods_count, periods); } - target_relay->schedule = schedule_create(schedule_id, periods_count, periods); relay_debug(target_relay); } @@ -101,7 +115,7 @@ handler_command(int fd, controller_t *controller) case COMMAND_CODE_GET_ID: break; case COMMAND_CODE_SET_NAME: - controller_set_name(controller, mpack_node_str(mpack_node_map_uint(root, COMMAND_MAPPING_NAME))); + handler_command_set_name(root, controller); break; case COMMAND_CODE_GET_NAME: break; diff --git a/handlers/discovery.c b/handlers/discovery.c index 7ed817c..c684a38 100644 --- a/handlers/discovery.c +++ b/handlers/discovery.c @@ -42,14 +42,14 @@ handler_discovery(int fd, controller_t *controller) mpack_writer_t writer; mpack_writer_init_growable(&writer, &payload, &payload_size); - mpack_start_map(&writer, 2); + mpack_start_map(&writer, 4); mpack_write_uint(&writer, DISCOVERY_MAPPING_ID); mpack_write_bin(&writer, (char*)controller->id, sizeof(uuid_t)); mpack_write_uint(&writer, DISCOVERY_MAPPING_COMMAND_PORT); mpack_write_u16(&writer, controller->command_port); mpack_write_uint(&writer, DISCOVERY_MAPPING_RELAY_COUNT); mpack_write_u8(&writer, controller->relay_count); - mpack_write_uint(&writer, COMMAND_MAPPING_PERIODS_COUNT); + mpack_write_uint(&writer, DISCOVERY_MAPPING_NAME); mpack_write_cstr(&writer, controller->name); mpack_finish_map(&writer); diff --git a/helpers/get_day_of_week.c b/helpers/get_day_of_week.c new file mode 100644 index 0000000..0214f63 --- /dev/null +++ b/helpers/get_day_of_week.c @@ -0,0 +1,12 @@ +#include + +#include + +int +helper_get_weekday(const time_t timestamp_now) +{ + struct tm *now = localtime(×tamp_now); + int wday_sun_sat = now->tm_wday; + int wday_mon_sun = (wday_sun_sat + 6) % 7; + return wday_mon_sun; +} diff --git a/helpers/parse_cli.c b/helpers/parse_cli.c index 38f5ff4..f9fac01 100644 --- a/helpers/parse_cli.c +++ b/helpers/parse_cli.c @@ -18,7 +18,7 @@ static const char *const usage[] = { #define PERM_EXEC (1<<2) void -helpers_parse_cli(int argc, const char **argv, config_t *config) +helper_parse_cli(int argc, const char **argv, config_t *config) { struct argparse_option options[] = { diff --git a/include/enums.h b/include/enums.h index 80fc0af..a2c0709 100644 --- a/include/enums.h +++ b/include/enums.h @@ -20,9 +20,10 @@ typedef enum COMMAND_MAPPING_CODE = 0, COMMAND_MAPPING_NAME = 1, COMMAND_MAPPING_RELAY_NUM = 2, - COMMAND_MAPPING_SCHEDULE_ID = 3, - COMMAND_MAPPING_PERIODS_COUNT = 4, - COMMAND_MAPPING_PERIODS_BLOB = 5, + 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 diff --git a/include/helpers.h b/include/helpers.h index 49d2a6f..4d59c84 100644 --- a/include/helpers.h +++ b/include/helpers.h @@ -29,6 +29,9 @@ int helper_load_config(IniDispatch *disp, void *config_void); void -helpers_parse_cli(int argc, const char **argv, config_t *config); +helper_parse_cli(int argc, const char **argv, config_t *config); + +int +helper_get_weekday(const time_t timestamp_now); #endif /* CONTROLLER_HELPERS_H */ diff --git a/include/models/controller.h b/include/models/controller.h index 3931fc7..41015ff 100644 --- a/include/models/controller.h +++ b/include/models/controller.h @@ -48,7 +48,6 @@ typedef enum DB_KEY_CONTROLLER_NAME = 1, DB_KEY_CONTROLLER_COMMAND_PORT = 2, DB_KEY_CONTROLLER_DISCOVERY_PORT = 3, - DB_KEY_CONTROLLER_RELAYS = 4, } db_key_controller_e; /** diff --git a/include/models/relay.h b/include/models/relay.h index cb4a6fe..34f08ba 100644 --- a/include/models/relay.h +++ b/include/models/relay.h @@ -12,7 +12,7 @@ typedef struct { uint8_t number; char name[MAX_NAME_LENGTH + 1]; - schedule_t *schedule; + schedule_t *schedules[7]; } relay_t; /** @@ -21,8 +21,6 @@ typedef struct typedef enum { DB_KEY_RELAY_NAME = 0, - DB_KEY_RELAY_SCHEDULE_ID = 1, - DB_KEY_RELAY_SCHEDULE_PERIODS = 2, } db_key_relay_e; relay_t* diff --git a/include/models/schedule.h b/include/models/schedule.h index 15274d4..69f6d81 100644 --- a/include/models/schedule.h +++ b/include/models/schedule.h @@ -3,18 +3,35 @@ #include #include +#include #include typedef struct { uuid_t id; + uint8_t weekday; uint16_t length; period_t **periods; } schedule_t; +/** + * @brief Key to save schedule information in database + */ +typedef enum +{ + DB_KEY_SCHEDULE_ID = 0, + DB_KEY_SCHEDULE_PERIODS = 1, +} db_key_schedule_e; + schedule_t* -schedule_create(uuid_t id, uint16_t length, uint16_t *periods_blob); +schedule_create(uuid_t id, uint8_t weekday, uint16_t length, uint16_t *periods_blob); + +schedule_t* +schedule_load(MDB_env *mdb_env, uint8_t relay_num, uint8_t weekday); + +int +schedule_save(schedule_t *schedule, uint8_t relay_num, MDB_env *mdb_env); uint16_t* schedule_periods_to_blob(schedule_t *schedule); diff --git a/main.c b/main.c index d81c7a5..12ad87f 100644 --- a/main.c +++ b/main.c @@ -43,6 +43,7 @@ terminate(int signum) controller_free(this_controller); + free(global_config.database); free(global_config.relay_configs); exit(signum); @@ -69,10 +70,9 @@ main(int argc, const char** argv) /******************** LOAD CONFIG ********************/ global_config.file = "controller.ini"; - global_config.database = "controller_db.lmdb"; global_config.log_level = LOG_LEVEL_INFO; - helpers_parse_cli(argc, argv, &global_config); + helper_parse_cli(argc, argv, &global_config); FILE * const ini_file = fopen(global_config.file, "rb"); if(ini_file == NULL) diff --git a/models/controller.c b/models/controller.c index 764cf2b..ace9507 100644 --- a/models/controller.c +++ b/models/controller.c @@ -65,7 +65,7 @@ controller_debug(controller_t *controller) LOG_DEBUG("(3/5) command_port: %5d discovery_port: %5d\n", controller->command_port, controller->discovery_port); LOG_DEBUG("(4/5) relay count: %3d\n", controller->relay_count); LOG_DEBUG("(5/5) relays @ %p:\n", (void*)controller->relays); - for(uint8_t i = 0; i < controller->relay_count; ++i) + for(int i = 0; i < controller->relay_count; ++i) { relay_debug(controller->relays[i]); } diff --git a/models/relay.c b/models/relay.c index 5610c20..59474ba 100644 --- a/models/relay.c +++ b/models/relay.c @@ -3,6 +3,7 @@ #include #include +#include #include relay_t* @@ -17,7 +18,10 @@ relay_create(uint8_t number) memset(off_id, 0, sizeof(uuid_t)); memcpy(off_id, "off", 3); - new_relay->schedule = schedule_create(off_id, 0, NULL); + for(int i = 0; i < 7; ++i) + { + new_relay->schedules[i] = schedule_create(off_id, i, 0, NULL); + } return new_relay; } @@ -32,7 +36,8 @@ relay_set_name(relay_t *relay, const char *name) int relay_is_active(relay_t *relay, time_t timestamp_now) { - if(relay->schedule->length == 0) + schedule_t *schedule = relay->schedules[helper_get_weekday(timestamp_now)]; + if(schedule->length == 0) { return 0; } @@ -42,9 +47,9 @@ relay_is_active(relay_t *relay, time_t timestamp_now) // finally remove seconds timestamp_now /= SECONDS_PER_MINUTE; - for(uint16_t i = 0; i < relay->schedule->length; ++i) + for(uint16_t i = 0; i < schedule->length; ++i) { - if(period_includes_time(relay->schedule->periods[i], timestamp_now)) + if(period_includes_time(schedule->periods[i], timestamp_now)) { return 1; } @@ -62,16 +67,19 @@ relay_debug(relay_t *relay) } LOG_DEBUG("(1/3) %d @ %p\n", relay->number, (void*)relay); LOG_DEBUG("(2/3) name: %s\n", relay->name); - LOG_DEBUG("(3/3) schedule:\n"); - schedule_debug(relay->schedule); + LOG_DEBUG("(3/3) schedules @ %p:\n", (void*)relay->schedules); + for(int i = 0; i < 7; ++i) + { + schedule_debug(relay->schedules[i]); + } } void relay_free(relay_t *relay) { - if(relay->schedule) + for(int i = 0; i < 7; ++i) { - schedule_free(relay->schedule); + schedule_free(relay->schedules[i]); } free(relay); } diff --git a/models/relay_load.c b/models/relay_load.c index 7dd9456..209c2cd 100644 --- a/models/relay_load.c +++ b/models/relay_load.c @@ -8,14 +8,16 @@ #include static int -relay_load_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_relay_e key_relay, uint8_t num, MDB_val *value) +relay_load_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_relay_e key_relay, uint8_t relay_num, MDB_val *value) { int err; size_t key_size = sizeof(db_key_relay_e) + sizeof(uint8_t); - void *key_data = malloc(key_size); - memmove((uint8_t*)key_data, &key_relay, sizeof(db_key_relay_e)); - memmove((uint8_t*)key_data + sizeof(db_key_relay_e), &num, sizeof(uint8_t)); + uint8_t *key_data = malloc(key_size); + uint8_t *key_data_writer = key_data; + memmove(key_data_writer, &relay_num, sizeof(uint8_t)); + key_data_writer += sizeof(uint8_t); + memmove(key_data_writer, &key_relay, sizeof(db_key_relay_e)); MDB_val key; key.mv_size = key_size; @@ -75,26 +77,12 @@ relay_load(MDB_env *mdb_env, uint8_t num) strncpy(new_relay->name, (char*)value.mv_data, MAX_NAME_LENGTH); new_relay->name[MAX_NAME_LENGTH] = '\0'; - if((err = relay_load_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_SCHEDULE_ID, num, &value)) != 0) - { - LOG_INFO("no relay for num %d found in db. returning new one\n", num); - mdb_txn_abort(mdb_txn); // transaction is read only - return relay_create(num); - } - uuid_t *schedule_id = (uuid_t*)value.mv_data; - - if((err = relay_load_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_SCHEDULE_PERIODS, num, &value)) != 0) - { - LOG_INFO("no relay for num %d found in db. returning new one\n", num); - mdb_txn_abort(mdb_txn); // transaction is read only - return relay_create(num); - } - uint16_t schedule_periods_length = ((uint16_t*)value.mv_data)[0]; - uint16_t *schedule_periods = ((uint16_t*)value.mv_data) + 1; - - new_relay->schedule = schedule_create(*schedule_id, schedule_periods_length, schedule_periods); - mdb_txn_abort(mdb_txn); // transaction is read only + + for(int i = 0; i < 7; ++i) + { + new_relay->schedules[i] = schedule_load(mdb_env, num, i); + } return new_relay; } diff --git a/models/relay_save.c b/models/relay_save.c index 28b6a92..e2b8656 100644 --- a/models/relay_save.c +++ b/models/relay_save.c @@ -8,14 +8,16 @@ #include static int -relay_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_relay_e key_relay, uint8_t num, MDB_val value) +relay_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_relay_e key_relay, uint8_t relay_num, MDB_val value) { int err; size_t key_size = sizeof(db_key_relay_e) + sizeof(uint8_t); - void *key_data = malloc(key_size); - memmove((uint8_t*)key_data, &key_relay, sizeof(db_key_relay_e)); - memmove((uint8_t*)key_data + sizeof(db_key_relay_e), &num, sizeof(uint8_t)); + uint8_t *key_data = malloc(key_size); + uint8_t *key_data_writer = key_data; + memmove(key_data_writer, &relay_num, sizeof(uint8_t)); + key_data_writer += sizeof(uint8_t); + memmove(key_data_writer, &key_relay, sizeof(db_key_relay_e)); MDB_val key; key.mv_size = key_size; @@ -62,27 +64,12 @@ relay_save(relay_t *relay, MDB_env *mdb_env) return 1; } - value.mv_size = sizeof(uuid_t); - value.mv_data = relay->schedule->id; - if(relay_save_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_SCHEDULE_ID, relay->number, value)) - { - LOG_ERROR("failed to save ID\n"); - return 1; - } - - // save periods blob - uint16_t *periods_blob = schedule_periods_to_blob(relay->schedule); - value.mv_size = sizeof(uint16_t) * ((periods_blob[0] * 2) + 1); - value.mv_data = periods_blob; - if(relay_save_single(mdb_txn, mdb_dbi, DB_KEY_RELAY_SCHEDULE_PERIODS, relay->number, value)) - { - free(periods_blob); - LOG_ERROR("failed to save periods\n"); - return 1; - } - free(periods_blob); - mdb_txn_commit(mdb_txn); + for(int i = 0; i < 7; ++i) + { + schedule_save(relay->schedules[i], relay->number, mdb_env); + } + return 0; } diff --git a/models/schedule.c b/models/schedule.c index 894d86f..9545ee2 100644 --- a/models/schedule.c +++ b/models/schedule.c @@ -5,12 +5,14 @@ #include schedule_t* -schedule_create(uuid_t id, uint16_t length, uint16_t *periods_blob) +schedule_create(uuid_t id, uint8_t weekday, uint16_t length, uint16_t *periods_blob) { schedule_t *new_schedule = malloc(sizeof(schedule_t)); memmove(new_schedule->id, id, sizeof(uuid_t)); + new_schedule->weekday = weekday; + new_schedule->length = length; new_schedule->periods = NULL; @@ -64,10 +66,11 @@ schedule_debug(schedule_t *schedule) LOG_DEBUG("schedule is NULL\n"); return; } - char uuid_str[37]; + char uuid_str[UUID_STR_LEN]; uuid_unparse(schedule->id, uuid_str); LOG_DEBUG("(1/3) %s @ %p\n", uuid_str, (void*)schedule); - LOG_DEBUG("(2/3) period count: %3d\n", schedule->length); + LOG_DEBUG("(2/4) period count: %3d\n", schedule->length); + LOG_DEBUG("(3/4) weekday: %3d\n", schedule->weekday); // one block: "HH:MM-HH:MM, " --> size: 13 (14 with '\0') char *periods_debug_str = malloc(sizeof(char) * ((schedule->length * 13) + 1)); @@ -85,5 +88,7 @@ schedule_debug(schedule_t *schedule) ); } - LOG_DEBUG("(3/3) periods: %s\n", periods_debug_str); + LOG_DEBUG("(4/4) periods: %s\n", periods_debug_str); + + free(periods_debug_str); } diff --git a/models/schedule_load.c b/models/schedule_load.c new file mode 100644 index 0000000..ffc77e1 --- /dev/null +++ b/models/schedule_load.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include + +#include +#include + +static int +schedule_load_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_schedule_e key_schedule, uint8_t relay_num, uint8_t weekday, MDB_val *value) +{ + int err; + + size_t key_size = sizeof(db_key_schedule_e) + (2 * sizeof(uint8_t)); + uint8_t *key_data = malloc(key_size); + uint8_t *key_data_writer = key_data; + memmove(key_data_writer, &relay_num, sizeof(uint8_t)); + key_data_writer += sizeof(uint8_t); + memmove(key_data_writer, &weekday, sizeof(uint8_t)); + key_data_writer += sizeof(uint8_t); + memmove(key_data_writer, &key_schedule, sizeof(db_key_schedule_e)); + + MDB_val key; + key.mv_size = key_size; + key.mv_data = key_data; + + if((err = mdb_get(mdb_txn, mdb_dbi, &key, value)) != 0) + { + LOG_ERROR("mdb_get error %s\n", mdb_strerror(err)); + mdb_txn_abort(mdb_txn); + free(key_data); + return 1; + } + free(key_data); + return 0; +} + +schedule_t* +schedule_load(MDB_env *mdb_env, uint8_t relay_num, uint8_t weekday) +{ + int err; + MDB_txn *mdb_txn; + MDB_dbi mdb_dbi; + + schedule_t *new_schedule; + + uuid_t off_id; + memset(off_id, 0, sizeof(uuid_t)); + memcpy(off_id, "off", 3); + + if((err = mdb_txn_begin(mdb_env, NULL, MDB_RDONLY, &mdb_txn)) != 0) + { + LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err)); + return schedule_create(off_id, weekday, 0, NULL); + } + + if((err = mdb_dbi_open(mdb_txn, "schedules", 0, &mdb_dbi)) != 0) + { + switch(err) + { + case MDB_NOTFOUND: + LOG_INFO("no schedule db found in db. returning new one (no schedules db)\n"); + mdb_txn_abort(mdb_txn); + break; + default: + LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err)); + } + return schedule_create(off_id, weekday, 0, NULL); + } + + MDB_val value; + + if((err = schedule_load_single(mdb_txn, mdb_dbi, DB_KEY_SCHEDULE_ID, relay_num, weekday, &value)) != 0) + { + LOG_INFO("no schedule for relay %d and weekday %d found in db. returning new one\n", relay_num, weekday); + mdb_txn_abort(mdb_txn); // transaction is read only + return schedule_create(off_id, weekday, 0, NULL); + } + uuid_t *schedule_id = (uuid_t*)value.mv_data; + + if((err = schedule_load_single(mdb_txn, mdb_dbi, DB_KEY_SCHEDULE_PERIODS, relay_num, weekday, &value)) != 0) + { + LOG_INFO("no schedule for relay %d and weekday %d found in db. returning new one\n", relay_num, weekday); + mdb_txn_abort(mdb_txn); // transaction is read only + return schedule_create(off_id, weekday, 0, NULL); + } + uint16_t schedule_periods_length = ((uint16_t*)value.mv_data)[0]; + uint16_t *schedule_periods = ((uint16_t*)value.mv_data) + 1; + + new_schedule = schedule_create(*schedule_id, weekday, schedule_periods_length, schedule_periods); + + mdb_txn_abort(mdb_txn); // transaction is read only + + return new_schedule; +} diff --git a/models/schedule_save.c b/models/schedule_save.c new file mode 100644 index 0000000..e53cdd7 --- /dev/null +++ b/models/schedule_save.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +static int +schedule_save_single(MDB_txn *mdb_txn, MDB_dbi mdb_dbi, db_key_schedule_e key_schedule, uint8_t relay_num, uint8_t weekday, MDB_val value) +{ + int err; + + size_t key_size = sizeof(db_key_schedule_e) + (2 * sizeof(uint8_t)); + uint8_t *key_data = malloc(key_size); + uint8_t *key_data_writer = key_data; + memmove(key_data_writer, &relay_num, sizeof(uint8_t)); + key_data_writer += sizeof(uint8_t); + memmove(key_data_writer, &weekday, sizeof(uint8_t)); + key_data_writer += sizeof(uint8_t); + memmove(key_data_writer, &key_schedule, sizeof(db_key_schedule_e)); + + MDB_val key; + key.mv_size = key_size; + key.mv_data = key_data; + + if((err = mdb_put(mdb_txn, mdb_dbi, &key, &value, 0)) != 0) + { + LOG_ERROR("mdb_put error %s\n", mdb_strerror(err)); + mdb_txn_abort(mdb_txn); + free(key_data); + return 1; + } + free(key_data); + return 0; +} + +int +schedule_save(schedule_t *schedule, uint8_t relay_num, MDB_env *mdb_env) +{ + char uuid_str[37]; + uuid_unparse(schedule->id, uuid_str); + LOG_TRACE("saving schedule %s @ %p\n", uuid_str, schedule); + int err; + + MDB_txn *mdb_txn; + MDB_dbi mdb_dbi; + MDB_val value; + + if((err = mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn)) != 0) + { + LOG_ERROR("mdb_txn_begin error %s\n", mdb_strerror(err)); + exit(1); + } + + if((err = mdb_dbi_open(mdb_txn, "schedules", MDB_CREATE, &mdb_dbi)) != 0) + { + LOG_ERROR("mdb_dbi_open error %s\n", mdb_strerror(err)); + exit(1); + } + + value.mv_size = sizeof(uuid_t); + value.mv_data = schedule->id; + if(schedule_save_single(mdb_txn, mdb_dbi, DB_KEY_SCHEDULE_ID, relay_num, schedule->weekday, value)) + { + LOG_ERROR("failed to save ID\n"); + return 1; + } + + // save periods blob + uint16_t *periods_blob = schedule_periods_to_blob(schedule); + value.mv_size = sizeof(uint16_t) * ((periods_blob[0] * 2) + 1); + value.mv_data = periods_blob; + if(schedule_save_single(mdb_txn, mdb_dbi, DB_KEY_SCHEDULE_PERIODS, relay_num, schedule->weekday, value)) + { + free(periods_blob); + LOG_ERROR("failed to save periods\n"); + return 1; + } + free(periods_blob); + + mdb_txn_commit(mdb_txn); + + return 0; +}