#include <stdlib.h> #include <string.h> #include <sqlite3.h> #include <cache.h> #include <cJSON.h> #include <logger.h> #include <database.h> #include <status.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) { LOGGER_DEBUG("saving relay '%s' into database (id: %d)\n", relay->name, relay->id); 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)); new_relay->is_on = 0; 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) { LOGGER_ERR("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 { LOGGER_ERR("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) { LOGGER_ERR("error inserting data: %s\n", sqlite3_errmsg(global_database)); } else { LOGGER_ERR("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); } cache_invalidate_relay(relay->id); status_reload_entry(relay->id); return result; } 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) { cJSON *json; char *cached = cache_get_json_relay(relay->id); if(cached) { json = cJSON_CreateRaw(cached); free(cached); return json; } controller_t *controller = controller_get_by_id(relay->controller_id); if(!controller) { LOGGER_WARNING("failed to get controller\n"); return NULL; } char uuid_str[UUID_STR_LEN]; uuid_unparse(controller->uid, uuid_str); LOGGER_DEBUG("JSONifying relay %s:%d\n", uuid_str, relay->number); relay_reload_active_schedule(relay); json = cJSON_CreateObject(); cJSON *json_number = cJSON_CreateNumber(relay->number); if(json_number == NULL) { LOGGER_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) { LOGGER_DEBUG("failed to make name\n"); cJSON_Delete(json); return NULL; } cJSON_AddItemToObject(json, "name", json_name); cJSON *json_controller_id = cJSON_CreateString(uuid_str); if(json_controller_id == NULL) { LOGGER_DEBUG("failed to make controller id\n"); cJSON_Delete(json); return NULL; } cJSON_AddItemToObject(json, "controller_id", json_controller_id); controller_free(controller); cJSON *json_is_on; switch(relay->is_on) { case 0: json_is_on = cJSON_CreateFalse(); break; case 1: json_is_on = cJSON_CreateTrue(); break; default: json_is_on = cJSON_CreateNull(); break; } cJSON_AddItemToObject(json, "is_on", json_is_on); cJSON *json_active_schedule = schedule_to_json(relay->active_schedule); cJSON_AddItemToObject(json, "active_schedule", json_active_schedule); cJSON *json_schedules = cJSON_CreateArray(); for(int i = 0; i < 7; ++i) { cJSON *json_schedule = schedule_to_json(relay->schedules[i]); cJSON_AddItemToArray(json_schedules, json_schedule); } 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) { 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_relay(relay->id, json_str); cJSON_Delete(json); json = cJSON_CreateRaw(json_str); free(json_str); return json; } cJSON* relay_list_to_json(relay_t **relays) { cJSON *json = cJSON_CreateArray(); for(int i = 0; relays[i] != NULL; ++i) { cJSON *json_relay = relay_to_json(relays[i]); cJSON_AddItemToArray(json, json_relay); } 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() { LOGGER_DEBUG("getting all relays from database\n"); 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) { LOGGER_DEBUG("getting relays [schedule_id=%d] from database\n", 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) { LOGGER_DEBUG("getting relay [controller_id=%d, relay_num=%d] from database\n", controller_id, 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) { LOGGER_DEBUG("getting relays [controller_id=%d] from database\n", 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); }