From 0103b0b2ff6bb5ab57a3325f8d0d3f8cd8f43d67 Mon Sep 17 00:00:00 2001 From: Tobias Reisinger Date: Sat, 29 Aug 2020 23:51:45 +0200 Subject: [PATCH] add: more basic macro stuff add: more efficient relay loading (only ids) --- include/cache.h | 11 +- include/models/junction_relay_schedule.h | 3 + include/models/macro.h | 45 +++ src/cache.c | 37 +- src/models/junction_relay_schedule.c | 11 + src/models/macro.c | 451 +++++++++++++++++++++++ src/models/schedule.c | 4 +- 7 files changed, 555 insertions(+), 7 deletions(-) create mode 100644 include/models/macro.h create mode 100644 src/models/macro.c diff --git a/include/cache.h b/include/cache.h index 8fde368..3e2ff7c 100644 --- a/include/cache.h +++ b/include/cache.h @@ -36,7 +36,6 @@ void cache_invalidate_relay(int relay_id, int status_relay); - void cache_put_json_controller(int controller_id, char *controller_json); @@ -47,6 +46,16 @@ void cache_invalidate_controller(int controller_id); +void +cache_put_json_macro(int macro_id, char *macro_json); + +char* +cache_get_json_macro(int macro_id); + +void +cache_invalidate_macro(int macro_id); + + void cache_invalidate_tagged(int tag_id); diff --git a/include/models/junction_relay_schedule.h b/include/models/junction_relay_schedule.h index d8ef168..e3c81ac 100644 --- a/include/models/junction_relay_schedule.h +++ b/include/models/junction_relay_schedule.h @@ -10,4 +10,7 @@ junction_relay_schedule_remove_for_relay(int relay_id); int junction_relay_schedule_insert_weekdays(int relay_id, int *schedule_ids); +int* +junction_relay_schedule_get_relay_ids_with_schedule(int schedule_id); + #endif /* CORE_MODELS_JUNCTION_RELAY_SCHEDULE_H */ diff --git a/include/models/macro.h b/include/models/macro.h new file mode 100644 index 0000000..b59dd23 --- /dev/null +++ b/include/models/macro.h @@ -0,0 +1,45 @@ +#ifndef CORE_MACRO_H +#define CORE_MACRO_H + +#include + +#include + +#include +#include + +typedef struct +{ + int id; + uuid_t uid; + char name[MAX_NAME_LENGTH + 1]; +} macro_t; + +int +macro_save(macro_t *macro); + +int +macro_remove(macro_t *macro); + +void +macro_free(macro_t *macro); + +void +macro_free_list(macro_t **macro); + +cJSON* +macro_to_json(macro_t *macro); + +void +macro_free_list(macro_t **macros_list); + +macro_t* +macro_get_by_id(int id); + +macro_t* +macro_get_by_uid(uuid_t uid); + +macro_t** +macro_get_all(); + +#endif /* CORE_MACRO_H */ diff --git a/src/cache.c b/src/cache.c index 2636ead..cc1d716 100644 --- a/src/cache.c +++ b/src/cache.c @@ -2,6 +2,7 @@ #include #include #include +#include sqlite3 *cache_database; @@ -134,14 +135,14 @@ cache_invalidate_schedule(int schedule_id) sprintf(key, "schedule_json:%d", schedule_id); cache_invalidate(key); - relay_t **relays = relay_get_with_schedule(schedule_id); + int *relay_ids = junction_relay_schedule_get_relay_ids_with_schedule(schedule_id); - for(int i = 0; relays[i] != NULL; ++i) + for(int i = 0; relay_ids[i] != 0; ++i) { - cache_invalidate_relay(relays[i]->id, -1); + cache_invalidate_relay(relay_ids[i], -1); } - relay_free_list(relays); + free(relay_ids); } @@ -208,6 +209,34 @@ cache_invalidate_controller(int controller_id) cache_invalidate(key); } + + +void +cache_put_json_macro(int macro_id, char *macro_json) +{ + char key[32]; + sprintf(key, "macro_json:%d", macro_id); + cache_insert_value(key, macro_json); +} + +char* +cache_get_json_macro(int macro_id) +{ + char key[32]; + sprintf(key, "macro_json:%d", macro_id); + return cache_get_value(key); +} + +void +cache_invalidate_macro(int macro_id) +{ + char key[32]; + sprintf(key, "macro_json:%d", macro_id); + cache_invalidate(key); +} + + + void cache_invalidate_tagged(int tag_id) { diff --git a/src/models/junction_relay_schedule.c b/src/models/junction_relay_schedule.c index e2b2309..9ceed85 100644 --- a/src/models/junction_relay_schedule.c +++ b/src/models/junction_relay_schedule.c @@ -87,3 +87,14 @@ junction_relay_schedule_remove_for_relay(int relay_id) return rc == SQLITE_DONE; } + +int* +junction_relay_schedule_get_relay_ids_with_schedule(int schedule_id) +{ + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(global_database, "SELECT relay_id FROM junction_relay_schedule WHERE schedule_id=?1;", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, schedule_id); + + return database_helper_get_ids(stmt); +} diff --git a/src/models/macro.c b/src/models/macro.c new file mode 100644 index 0000000..1aa234e --- /dev/null +++ b/src/models/macro.c @@ -0,0 +1,451 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static int +db_update_insert(macro_t *macro, sqlite3_stmt *stmt) +{ + LOGGER_DEBUG("saving macro '%s' into database (id: %d)\n", macro->name, macro->id); + + int rc; + + sqlite3_bind_int(stmt, 1, macro->id); + sqlite3_bind_blob(stmt, 2, macro->uid, sizeof(uuid_t), SQLITE_STATIC); + sqlite3_bind_text(stmt, 3, macro->name, -1, SQLITE_STATIC); + + rc = sqlite3_step(stmt); + + sqlite3_finalize(stmt); + + free(periods_blob); + + return rc != SQLITE_DONE; +} +static macro_t* +macro_db_select_mapper(sqlite3_stmt *stmt) +{ + const uint16_t *periods_blob; + macro_t *new_macro = malloc(sizeof(macro_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_macro->id = sqlite3_column_int(stmt, i); + break; + case 'n': // name + strncpy(new_macro->name, (const char*)sqlite3_column_text(stmt, i), MAX_NAME_LENGTH); + new_macro->name[MAX_NAME_LENGTH] = '\0'; + break; + case 'u': // uid + uuid_copy(new_macro->uid, (const unsigned char*)sqlite3_column_blob(stmt, i)); + break; + default: // ignore columns not implemented + break; + } + } + return new_macro; +} + +static macro_t** +macro_db_select(sqlite3_stmt *stmt) +{ + macro_t **all_macros = malloc(sizeof(macro_t*)); + + int row = 0; + + for(;;) + { + int s; + + s = sqlite3_step(stmt); + if (s == SQLITE_ROW) + { + macro_t *new_macro = macro_db_select_mapper(stmt); + row++; + + all_macros = (macro_t**)realloc(all_macros, sizeof(macro_t*) * (row + 1)); + all_macros[row - 1] = new_macro; + } + else + { + if(s == SQLITE_DONE) + { + break; + } + else + { + LOGGER_ERR("error selecting macros from database: %s\n", sqlite3_errstr(s)); + break; + } + } + } + sqlite3_finalize(stmt); + all_macros[row] = NULL; + return all_macros; +} + +int +macro_save(macro_t *macro) +{ + int opened_transaction = database_transaction_begin(); + + sqlite3_stmt *stmt; + if(macro->id) + { + sqlite3_prepare_v2(global_database, "UPDATE macros SET uid = ?2, name = ?3, periods = ?4 WHERE id=?1;", -1, &stmt, NULL); + } + else + { + sqlite3_prepare_v2(global_database, "INSERT INTO macros(uid, name, periods) values (?2, ?3, ?4);", -1, &stmt, NULL); + } + + int result = db_update_insert(macro, stmt); + + if(result) + { + if(macro->id) + { + LOGGER_ERR("error inserting data: %s\n", sqlite3_errmsg(global_database)); + } + else + { + LOGGER_ERR("error updating data: %s\n", sqlite3_errmsg(global_database)); + } + + if(opened_transaction) + { + database_transaction_rollback(); + } + } + else + { + if(!macro->id) + { + macro->id = sqlite3_last_insert_rowid(global_database); + } + + if(opened_transaction) + { + database_transaction_commit(); + } + } + + cache_invalidate_macro(macro->id); + + return result; +} + +int +macro_remove(macro_t *macro) +{ + sqlite3_stmt *stmt; + if(!macro->id) + { + return 0; + } + + sqlite3_prepare_v2(global_database, "DELETE FROM macros WHERE id=?1;", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, macro->id); + + int rc = sqlite3_step(stmt); + + sqlite3_finalize(stmt); + + return rc != SQLITE_DONE; +} + +int +macro_is_protected(macro_t *macro) +{ + uuid_t tmp_uuid; + + memset(tmp_uuid, 0, sizeof(uuid_t)); + memcpy(tmp_uuid, "off", 3); + if(uuid_compare(macro->uid, tmp_uuid) == 0) + { + return 1; + } + + memset(tmp_uuid, 0, sizeof(uuid_t)); + memcpy(tmp_uuid, "on", 2); + if(uuid_compare(macro->uid, tmp_uuid) == 0) + { + return 1; + } + + return 0; +} + +void +macro_free(macro_t *macro) +{ + free(macro->periods); + free(macro); +} + +void +macro_free_list(macro_t **macros) +{ + for(int i = 0; macros[i] != NULL; ++i) + { + macro_free(macros[i]); + } + free(macros); +} + +uint16_t* +macro_periods_to_blob(macro_t *macro) +{ + uint16_t *blob = malloc(sizeof(uint16_t) * ((macro->periods_count * 2) + 1)); + + blob[0] = macro->periods_count; + + for(int i = 0; i < macro->periods_count; i++) + { + blob[(i * 2) + 1] = macro->periods[i].start; + blob[(i * 2) + 2] = macro->periods[i].end; + } + return blob; +} + +cJSON* +macro_to_json(macro_t *macro) +{ + cJSON *json; + + char *cached = cache_get_json_macro(macro->id); + if(cached) + { + json = cJSON_CreateRaw(cached); + free(cached); + return json; + } + + char uuid_str[UUID_STR_LEN]; + macro_uid_unparse(macro->uid, uuid_str); + + LOGGER_DEBUG("JSONifying macro %s\n", uuid_str); + + json = cJSON_CreateObject(); + + cJSON *json_name = cJSON_CreateString(macro->name); + if(json_name == NULL) + { + cJSON_Delete(json); + return NULL; + } + cJSON_AddItemToObject(json, "name", json_name); + + cJSON *json_id = cJSON_CreateString(uuid_str); + if(json_name == NULL) + { + cJSON_Delete(json); + return NULL; + } + cJSON_AddItemToObject(json, "id", json_id); + + cJSON *json_tags = cJSON_CreateArray(); + int *tags_ids = junction_tag_get_tags_for_macro_id(macro->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) + { + LOGGER_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); + + char *json_str = cJSON_Print(json); + cache_put_json_macro(macro->id, json_str); + cJSON_Delete(json); + + json = cJSON_CreateRaw(json_str); + free(json_str); + return json; +} + +macro_t* +macro_get_by_id_or_off(int id) +{ + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(global_database, "SELECT * FROM macros WHERE id = ?1;", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, id); + + macro_t **sql_result = macro_db_select(stmt); + + macro_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 macro_get_by_uid(tmp_uuid); +} + +macro_t* +macro_get_by_id(int id) +{ + LOGGER_DEBUG("getting macro [id=%d] from database\n", id); + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(global_database, "SELECT * FROM macros WHERE id = ?1;", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, id); + + macro_t **sql_result = macro_db_select(stmt); + + macro_t *result = sql_result[0]; + free(sql_result); + + return result; +} + +macro_t* +macro_get_by_uid_or_off(uuid_t uid) +{ + char uuid_str[UUID_STR_LEN]; + macro_uid_unparse(uid, uuid_str); + LOGGER_DEBUG("getting macro [uid=%s] or off from database\n", uuid_str); + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(global_database, "SELECT * FROM macros WHERE uid = ?1;", -1, &stmt, NULL); + sqlite3_bind_blob(stmt, 1, uid, sizeof(uuid_t), SQLITE_STATIC); + + macro_t **sql_result = macro_db_select(stmt); + + macro_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 macro_get_by_uid(tmp_uuid); +} + +macro_t* +macro_get_by_uid(uuid_t uid) +{ + char uuid_str[UUID_STR_LEN]; + macro_uid_unparse(uid, uuid_str); + LOGGER_DEBUG("getting macro [uid=%s] from database\n", uuid_str); + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(global_database, "SELECT * FROM macros WHERE uid = ?1;", -1, &stmt, NULL); + sqlite3_bind_blob(stmt, 1, uid, sizeof(uuid_t), SQLITE_STATIC); + + macro_t **sql_result = macro_db_select(stmt); + + macro_t *result = sql_result[0]; + free(sql_result); + + return result; +} + +macro_t** +macro_get_relay_weekdays(int relay_id) +{ + LOGGER_DEBUG("getting macros [relay_id=%d] from database\n", relay_id); + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(global_database, "SELECT macros.* FROM macros INNER JOIN junction_relay_macro ON macros.id == junction_relay_macro.macro_id WHERE junction_relay_macro.relay_id = ?1 ORDER BY junction_relay_macro.weekday ASC", -1, &stmt, NULL); + sqlite3_bind_int(stmt, 1, relay_id); + + return macro_db_select(stmt); +} + +macro_t** +macro_get_all() +{ + LOGGER_DEBUG("getting all macros from database\n"); + sqlite3_stmt *stmt; + + sqlite3_prepare_v2(global_database, "SELECT * FROM macros;", -1, &stmt, NULL); + + return macro_db_select(stmt); + +} + +int +macro_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 +macro_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); +} diff --git a/src/models/schedule.c b/src/models/schedule.c index abb6850..9225007 100644 --- a/src/models/schedule.c +++ b/src/models/schedule.c @@ -46,8 +46,8 @@ schedule_db_select_mapper(sqlite3_stmt *stmt) 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'; + strncpy(new_schedule->name, (const char*)sqlite3_column_text(stmt, i), MAX_NAME_LENGTH); + new_schedule->name[MAX_NAME_LENGTH] = '\0'; break; case 'p': // periods periods_blob = sqlite3_column_blob(stmt, i);