
#include "accident.h"
#include <pthread.h>
#include <sched.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syslog.h>
#include <time.h>
#include <unistd.h>
#include <sys/statvfs.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <ftw.h>
#include "../proto_forward.h"
#include "define.h"

#define OPC_ACCIDENT OPC_HISTORY
#define ACCIDENT_DATA "accident_data"
#define DATA_ACCURACY "%.5f" // 处理精度()

int accident_dispose_en = 0;    // 初始化完成后置1 否则对外API无效

pthread_rwlock_t accident_dispose_rwlock;           // Accident_dispose的锁

struct Accident_dispose_t *Accident_dispose = NULL; // 已触发的事故队列
struct Accident_data_info **Accident_data_tab = NULL; // 数据表缓冲区二维队列
int dev_num = 0;

#if EN_TDENGINE
TAOS *accident_conn;    // taos链接
TAOS *accident_history;    // taos链接
#else 
void *accident_conn;    // taos链接
void *accident_history;    // taos链接
#endif
void *accident_dispose_loop(void *arg);
int accident_dispose_del(struct Accident_dispose_t *tmp);
struct Accident_data_info *accident_data_taos_get(struct Accident_data_info *tmp, char *time_str_start, char *time_str_end);
struct Accident_dispose_t *accident_info_init(int code_id, int front_time, int after_time, time_t time_now);
int accident_dispose_add(struct Accident_dispose_t *tmp);
#ifdef EN_TDENGINE

int accident_list_init()
{
    Accident_dispose = calloc(1, sizeof(struct Accident_dispose_t));
    if(NULL == Accident_dispose)
    {
        ems_syslog(LOG_ERR, "<accident_list_init>:calloc failed for Accident_dispose (size=%zu)", sizeof(struct Accident_dispose_t));
        return 1;
    }
    INIT_LIST_HEAD(&Accident_dispose->Accident_queue);

    TAOS_RES *res = NULL;
    res = taos_query(accident_conn, "SELECT * FROM "ACCIDENT_DATA";");
    if (NULL == res || taos_errno(res) != 0) {
        if(NULL != res) 
        {
            ems_syslog(LOG_ERR, "<accident_list_init>:Query failed: %s", taos_errstr(res) );
        }
    }
    else 
    {
        TAOS_ROW row;
        while ((row = taos_fetch_row(res))) {
            
            if(*(int *)row[6] == CLOUD_DEL) // 跳过已经结束的事故记录 
            {
                continue;
            }

            struct Accident_dispose_t *tmp = accident_info_init(*(int*)row[1], *(int*)row[2], *(int*)row[3], *(time_t *)row[4]);
            
            tmp->taos_timeid = *(time_t *)row[0];
            tmp->pack = *(int *)row[5];            
            tmp->cloud = *(int *)row[6];
            if(tmp->cloud == CLOUD_RUN)
            {
                tmp->cloud = CLOUD_UP;
            }
            strcpy(tmp->md5, (char *)row[7]);
            tmp->md5[MD5_DIGEST_LENGTH*2 ] = 0;
            tmp->md5[MD5_DIGEST_LENGTH*2 + 1] = 0;
            accident_dispose_add(tmp); 
        }
    }
    
    if(NULL != res) taos_free_result(res);
    return 0;

}

int accident_conn_taos_init()
{
    
    TAOS_RES *res = NULL;
        // 连接taos数据库
    accident_history = tdengine_connect(NULL, 0, NULL, NULL, OPC_HISTORY);
    if(NULL == accident_history)
    {
        return 1;
    }

    accident_conn = tdengine_connect(NULL, 0, NULL, NULL, OPC_ACCIDENT);

    if (accident_conn == NULL) {
        // 第一次连接失败，尝试创建数据库
        char create_db_sql[128];
        snprintf(create_db_sql, sizeof(create_db_sql), "CREATE DATABASE IF NOT EXISTS %s", OPC_ACCIDENT);
        TAOS *temp_conn = tdengine_connect(NULL, 0, NULL, NULL, NULL);
        if (temp_conn) {
            res = taos_query(temp_conn, create_db_sql);
            if (NULL == res || taos_errno(res) != 0) {
                if(NULL != res) 
                {
                    ems_syslog(LOG_ERR, "<accident_conn_taos_init>:Query failed: %s: %s ", create_db_sql, taos_errstr(res) );
                }
            }
            if(NULL != res) taos_free_result(res);
            taos_close(temp_conn);
            
            // 再次尝试连接
            accident_conn = tdengine_connect(NULL, 0, NULL, NULL, OPC_ACCIDENT);
            if (accident_conn == NULL) {
                ems_syslog(LOG_ERR, "<accident_conn_taos_init>:failed to connect to taos after creating database: %s", taos_errstr(accident_conn));
                return 1;
            }
        } else {
            ems_syslog(LOG_ERR, "<accident_conn_taos_init>:failed to connect to taos to create database: %s", taos_errstr(temp_conn));
            return 2;
        }
    }
    // 检查表是否存在
    const char *create_table_sql = "CREATE TABLE IF NOT EXISTS "ACCIDENT_DATA" ("
                                  "time TIMESTAMP, "
                                  "code_id INT, "
                                  "front_time INT, "
                                  "after_time INT, "
                                  "Occurrence_time BIGINT, "
                                  "pack INT, "
                                  "cloud INT, "
                                  "md5 VARCHAR(34));";
    res = taos_query(accident_conn, create_table_sql);
    if (NULL == res || taos_errno(res) != 0) {
        if(NULL != res) 
        {
            ems_syslog(LOG_ERR, "<accident_conn_taos_init>:Query failed: %s: %s ", create_table_sql, taos_errstr(res) );
        }
    }
    if(NULL != res) taos_free_result(res);

    return 0;

    
}

/** 初始化
 * @description: 
 * @return {*}
 */
int accident_dispose_init()
{
    
    
    if (pthread_rwlock_init(&accident_dispose_rwlock, NULL) != 0) {
        perror("<accident_dispose_init>:rwlock init failed");
        return 2;
    }

    /* 暂时屏蔽 开发环境不检查
    struct statvfs buf;
    if (statvfs("/mnt/ssd/", &buf) != 0) {
        ems_syslog(LOG_ERR, "/mnt/ssd/ is not mounted");
        return -1;
    }
    */

    /*等待数据库连接上 数据列表初始化*/
    if(accident_conn_taos_init() || accident_list_init())
    {
        ems_syslog(LOG_ERR,"<accident_dispose_init>:Error accident dispose Init faild\n"); 
        return 1;
    }

    pthread_t accident_pid; 
    pthread_create(&accident_pid, NULL, accident_dispose_loop, NULL);
    if (pthread_setname_np(accident_pid, "accident")) 
    { 
        ems_syslog(LOG_ERR,"<accident_dispose_init>:Error setting thread name\n"); 
    }

    return 0;
}


/**
 * @description: 事故队列新增
 * @param {Accident_dispose_t} *tmp
 * @return {*}
 */
int accident_dispose_add(struct Accident_dispose_t *tmp)
{
    if(NULL == Accident_dispose)
    {
        ems_syslog(LOG_ERR, "<accident_dispose_add>:Accident_dispose is NULL");
        return -1;
    }

    if(NULL == tmp)
    {
        ems_syslog(LOG_ERR, "<accident_dispose_add>:the accident is NULL");
        return -1;
    }

    pthread_rwlock_wrlock(&accident_dispose_rwlock);
    list_add(&tmp->Accident_queue, &Accident_dispose->Accident_queue);
    pthread_rwlock_unlock(&accident_dispose_rwlock);

    return 0;
}


/**
 * @description: 基于事故当前不同的状态信息，将事故状态数据更新进数据库
 * @param {Accident_dispose_t} *tmp
 * @return {*}
 */
int Accident_flush_taos_info(struct Accident_dispose_t *tmp)
{
    if(NULL == tmp )
    {
        ems_syslog(LOG_ERR, "<Accident_flush_taos_info>: tmp is NULL !!!");
        return -1;
    }

    ems_syslog(LOG_ERR, "事故信息刷新: code:%d taos_timeid:%ld  pack%d  cloud:%d", tmp->code_id, tmp->taos_timeid, tmp->pack, tmp->cloud);

    // if(CLOUD_DEL == tmp->cloud && tmp->taos_timeid != 0) // 在数据库中检索并删除该数据
    // {


    //     char delete_sql[128];
    //     snprintf(delete_sql, sizeof(delete_sql),
    //             "DELETE FROM "ACCIDENT_DATA" WHERE time = %ld", 
    //             tmp->taos_timeid);
    //     pthread_rwlock_wrlock(&accident_dispose_rwlock);
    //     TAOS_RES *tmp_res = taos_query(accident_conn, delete_sql);
    //     if (NULL == tmp_res || taos_errno(tmp_res) != 0) {
    //         ems_syslog(LOG_ERR, "<Accident_flush_taos_info>:failed to delete data: %s", taos_errstr(tmp_res));
    //         return -1;
    //     }
    //     char cmd[512];
    //     snprintf(cmd, sizeof(cmd), "rm %s/%ld_%d.tar.gz", ACCIDENT_ZIP_PATH, tmp->taos_timeid, tmp->code_id);
    //     system(cmd);
    //     goto AFTI_END;

    // }


    // 检查数据是否存在并执行覆盖写入或创建
    pthread_rwlock_wrlock(&accident_dispose_rwlock);
    char check_sql[128];
    snprintf(check_sql, sizeof(check_sql),
            "SELECT COUNT(1) FROM "ACCIDENT_DATA" WHERE time = %ld",
            tmp->taos_timeid);

    TAOS_RES* res = taos_query(accident_conn, check_sql);
    

    if (res && taos_errno(res) == 0) {

        TAOS_ROW row = taos_fetch_row(res);
        if (row && *(int32_t*)row[0] > 0) {

            if(res) taos_free_result(res);
            res = NULL;

            char update_sql[512];
            snprintf(update_sql, sizeof(update_sql),
                    "INSERT INTO "ACCIDENT_DATA" (time, code_id, front_time, after_time, occurrence_time, pack, cloud, md5) VALUES ("
                    "%ld, %d, %d, %d, %ld, %d, %d, \'%s\');",
                    tmp->taos_timeid, tmp->code_id, tmp->front_time, tmp->after_time,
                    tmp->Occurrence_time, tmp->pack, tmp->cloud, tmp->md5
                    );
            // accident_conn = tdengine_connect(NULL, 0, NULL, NULL, OPC_ACCIDENT);
            if(NULL == accident_conn )
            {
                ems_syslog(LOG_ERR, "<Accident_flush_taos_info>: %s 连接失败 !!!", OPC_ACCIDENT);
                return -1;
            }
            TAOS_RES* update_res = taos_query(accident_conn, update_sql);

            
            if (update_res) {
                if (taos_errno(update_res) != 0) {
                    ems_syslog(LOG_ERR, "<Accident_flush_taos_info>:failed to update data: %s", taos_errstr(update_res));
                }
                taos_free_result(update_res);
                goto AFTI_END;
            }
        }
    }

    if(res) 
    {   if(taos_errno(res))
        {
            ems_syslog(LOG_ERR, "<Accident_flush_taos_info>:failed to check data existence: %s", taos_errstr(res));
        }
        taos_free_result(res);

        res = NULL;

    }

    char insert_sql[512];
    snprintf(insert_sql, sizeof(insert_sql),
            "INSERT INTO "ACCIDENT_DATA" VALUES (now, %d, %d, %d, %ld, %d, %d, '%s')",
            tmp->code_id, tmp->front_time, tmp->after_time,
            tmp->Occurrence_time, tmp->pack, tmp->cloud, tmp->md5);
    
    // accident_conn = tdengine_connect(NULL, 0, NULL, NULL, OPC_ACCIDENT);
    if(NULL == accident_conn )
    {
        ems_syslog(LOG_ERR, "<Accident_flush_taos_info>: %s 连接失败 !!!", OPC_ACCIDENT);
        return -1;
    }
    TAOS_RES* insert_res = taos_query(accident_conn, insert_sql);

    if (insert_res) {

        if (taos_errno(insert_res) != 0) {
            ems_syslog(LOG_ERR, "<Accident_flush_taos_info>:failed to insert data: %s", taos_errstr(insert_res));
            taos_free_result(insert_res);
            goto AFTI_END;
        }
        taos_free_result(insert_res);

        // accident_conn = tdengine_connect(NULL, 0, NULL, NULL, OPC_ACCIDENT);
        if(NULL == accident_conn )
        {
            ems_syslog(LOG_ERR, "<Accident_flush_taos_info>: %s 连接失败 !!!", OPC_ACCIDENT);
            return -1;
        }
        res = taos_query(accident_conn, "SELECT LAST(time) FROM "ACCIDENT_DATA"");

        if (res && taos_errno(res) == 0) {
            TAOS_ROW row = taos_fetch_row(res);
            if (row) {
                tmp->taos_timeid = *(time_t*)row[0];
            }
            taos_free_result(res);
            res = NULL;
        }
        
    }
AFTI_END:

    pthread_rwlock_unlock(&accident_dispose_rwlock);
    return 0;
}

/**
 * @description: 事故记录队 设置出队标志 但不直接出队 出队行为交给loop执行以确保链表安全
 * @param {int} code_id
 * @param {int} start_time
 * @param {int} end_time
 * @return {*}
 */
int accident_dispose_del(struct Accident_dispose_t *tmp)
{
    if(NULL == Accident_dispose)
    {
        ems_syslog(LOG_ERR, "<accident_dispose_del>:Accident_dispose is NULL");
        return -1;
    }

    pthread_rwlock_wrlock(&accident_dispose_rwlock);
    list_del(&tmp->Accident_queue);
    
    char gz_path[256] = "";
    sprintf(gz_path, ACCIDENT_ZIP_PATH"/%ld_%d.tar.gz", tmp->taos_timeid, tmp->code_id);
    remove(gz_path);
    
    ems_syslog(LOG_ERR, "事故上报完毕，删除本地文件记录: code:%d taos_timeid:%ld  pack%d  cloud:%d  remove(%s)", tmp->code_id, tmp->taos_timeid, tmp->pack, tmp->cloud, gz_path);
    memset(tmp, 0, sizeof(struct Accident_dispose_t));  // 清0再free
    free(tmp);

    pthread_rwlock_unlock(&accident_dispose_rwlock);

    return 0;
}
static int accident_str_replace_chr(char *line, int len)
{
    for(int i = 0; line[i] != '\0';i++)
    {
        if(!( (line[i] >= '0' && line[i] <= '9') || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') || line[i] == '\0'|| line[i] == '_'))
        {
            line[i] = '_';
        }
    }
    return 0;
}



/**
 * @description: 创建单个表的信息缓冲区
 * @param {char} *tag_name
 * @param {char} *no
 * @return {*}
 */
struct Accident_data_info *accident_data_info_creat(char *tag_name, char *no)
{
    if (tag_name == NULL || no == NULL) {
        ems_syslog(LOG_ERR, "<accident_data_info_creat>: NULL parameter (tag_name=%p, no=%p)", tag_name, no);
        return NULL;
    }

    struct Accident_data_info *tmp = calloc(1, sizeof(struct Accident_data_info));
    if (tmp == NULL) {
        ems_syslog(LOG_ERR, "<accident_data_info_creat>: calloc failed (size=%zu)", sizeof(struct Accident_data_info));
        return NULL;
    }

    tmp->mode_type = 0;
    tmp->res = NULL;
    tmp->value = 0;
    tmp->tagname = tag_name;
    
    snprintf(tmp->tabname, sizeof(tmp->tabname) - 1, "%s_%s", no, tag_name);    // 表名称需要组合故启用临时空间缓冲
    accident_str_replace_chr(tmp->tabname, sizeof(tmp->tabname));

    return tmp;
}

/**
 * @description: 获取整个tag表并按照设备隔离data信息表。
 * @return {*}
 */
int accident_data_list_init()
{
    proto_forward_t *dev_var = get_proto_forward_var();
    if (dev_var == NULL) {
        ems_syslog(LOG_ERR, "<accident_data_list_init>:get_proto_forward_var failed");
        return -1;
    }

    dev_num = 0;
    for (int i = 0; i < dev_var->channels_size; i++) {
        dev_num += dev_var->channels[i]->devs_size;
    }


    Accident_data_tab = calloc(dev_num, sizeof(struct Accident_data_info *));
    if (Accident_data_tab == NULL) {
        ems_syslog(LOG_ERR, "<accident_data_list_init>:calloc failed for Accident_data_tab");
        return -1;
    }

    // 遍历所有设备和标签
    for (int i = 0, m = 0; i < dev_var->channels_size; i++) {
        channel_t *channel = dev_var->channels[i];
        ems_syslog(LOG_DEBUG, "channel:%s", channel->channel);

        for (int j = 0; j < channel->devs_size; j++, m++) {
            device_t *dev = channel->devs[j];
            if (dev == NULL) continue;

            // 初始化设备列表头
            Accident_data_tab[m] = calloc(1, sizeof(struct Accident_data_info));
            if (Accident_data_tab[m] == NULL) {
                ems_syslog(LOG_ERR, "<accident_data_list_init>:calloc failed for device %s", dev->no);
                continue;
            }
            INIT_LIST_HEAD(&Accident_data_tab[m]->list);
            strcpy(Accident_data_tab[m]->tabname, dev->no);  // 表头记录设备no

            // 处理读写标签
            for (int k = 0; k < dev->tags.rw_tags_size; k++) {
                tag_t *tag = dev->tags.rw_tags[k];
                if(tag && HISTORY_TYPE_OFF != tag->history_type)
                {
                    struct Accident_data_info *info = accident_data_info_creat(tag->name, dev->no);
                    if (info) {
                        list_add_tail(&info->list, &Accident_data_tab[m]->list);
                    }
                }
                
            }

            // 处理只读标签
            for (int k = 0; k < dev->tags.ro_tags_size; k++) {
                tag_t *tag = dev->tags.ro_tags[k];
                if(tag && HISTORY_TYPE_OFF != tag->history_type)
                {
                    struct Accident_data_info *info = accident_data_info_creat(tag->name, dev->no);
                    if (info) {
                        list_add_tail(&info->list, &Accident_data_tab[m]->list);
                    }
                }

                
            }

            // 处理系统信息标签
            for (int k = 0; k < dev->tags.sys_info_tags_size; k++) {
                tag_t *tag = dev->tags.sys_info_tags[k];
                if(tag && HISTORY_TYPE_OFF != tag->history_type)
                {
                    struct Accident_data_info *info = accident_data_info_creat(tag->name, dev->no);
                    if (info) {
                        list_add_tail(&info->list, &Accident_data_tab[m]->list);
                    }
                }
            }
        }
    }

    return 0;
}

/**
 * @description: 创建对象并初始化一些参数
 * @param {int} code_id
 * @param {int} front_time
 * @param {int} after_time
 * @return {*}
 */
struct Accident_dispose_t *accident_info_init(int code_id, int front_time, int after_time, time_t time_now)
{
    struct Accident_dispose_t *tmp = (struct Accident_dispose_t *)calloc(1, sizeof(struct Accident_dispose_t));
    if(NULL == tmp)
    {
        ems_syslog(LOG_ERR, "<accident_info_init>:calloc failed for Accident_dispose_t tmp");
        return NULL;
    }

    tmp->code_id = code_id;
    tmp->front_time = front_time;
    tmp->after_time = after_time;
    tmp->start_time = time_now - (front_time * 60);  
    tmp->Occurrence_time = time_now;  
    tmp->end_time = time_now + (after_time * 60);    
    tmp->pack = 0;
    tmp->cloud = 0;
    tmp->pack_cnt = 0;
    return tmp;
}  

/**
 * @description: 事故涌入的操作API  多线程安全
 * @param {int} code_id
 * @param {int} front_time
 * @param {int} after_time
 * @return {*}
 */
int accident_record_api(int code_id, int front_time, int after_time)
{
    time_t time_now = time(NULL);   // 第一时间记录触发时间
    if(0 == accident_dispose_en)
    {
        ems_syslog(LOG_DEBUG, "<accident_record_api>:事故反演初始化未完成 or 失败 api无法支持");
        return -1;
    }

    struct Accident_dispose_t *tmp = accident_info_init(code_id, front_time, after_time, time_now);
    char insert_sql[512];
    snprintf(insert_sql, sizeof(insert_sql),
             "INSERT INTO "ACCIDENT_DATA" VALUES (NOW, %d, %d, %d, %ld, %d, %d, '%s')",
             tmp->code_id, tmp->front_time, tmp->after_time, 
             tmp->Occurrence_time, tmp->pack, tmp->cloud, tmp->md5);

    Accident_flush_taos_info(tmp);
    accident_dispose_add(tmp);

    return 0;
}


/**
 * @description: 事故完成上云 清理列表、数据库记录、本地数据包;
 * @param {Accident_dispose_t} *tmp
 * @return {*}
 */
int accident_del_api(char *md5)
{
    if(0 == accident_dispose_en)
    {
        ems_syslog(LOG_DEBUG, "<accident_get_clould_api>:事故反演初始化未完成 or 失败 api无法支持");
        return -1;
    }

    if(NULL == md5)
    {
        return -1;
    }
    int found = 0; 
    pthread_rwlock_wrlock(&accident_dispose_rwlock);

    struct list_head *pos, *n;

    list_for_each_safe(pos, n, &Accident_dispose->Accident_queue) 
    {
        struct Accident_dispose_t *tmp = list_entry(pos, struct Accident_dispose_t, Accident_queue);

        if(strncmp(tmp->md5, md5, MD5_DIGEST_LENGTH*2 + 2) == 0)
        {
            found = 1;
            if(tmp->cloud != 0)
            {
                tmp->cloud = CLOUD_DEL;
            }
            break;
        }
    }

    pthread_rwlock_unlock(&accident_dispose_rwlock);

    if(!found)
    {
        ems_syslog(LOG_DEBUG, "<accident_del_api>: 未找到匹配的md5[%s]", md5);
        return -1;
    }
    
    return 0;
}


/**
 * @description: 校验获取可以上云的数据包(多线程异步安全)
 * @param {Accident_dispose_t} *tmp
 * @return {*}      NULL:返回空/失败 !NULL:成功
 */
struct Accident_dispose_t * accident_get_clould_api()
{
    if(0 == accident_dispose_en)
    {
        ems_syslog(LOG_ERR, "<accident_get_clould_api>:事故反演初始化未完成 or 失败 api无法支持");  // 可能周期打印设高log等级防止打印频繁
        return NULL;
    }

    struct list_head *pos, *n;
    
    pthread_rwlock_rdlock(&accident_dispose_rwlock);
    list_for_each_safe(pos, n, &Accident_dispose->Accident_queue) 
    {
        struct Accident_dispose_t *tmp = list_entry(pos, struct Accident_dispose_t, Accident_queue);

        if(0 != tmp->taos_timeid && tmp->pack == PACK_END && CLOUD_UP == tmp->cloud)
        {
            pthread_rwlock_unlock(&accident_dispose_rwlock);
            pthread_rwlock_wrlock(&accident_dispose_rwlock);
            tmp->cloud = CLOUD_RUN;
            pthread_rwlock_unlock(&accident_dispose_rwlock);
            Accident_flush_taos_info(tmp);
            return tmp;
        }
        if(tmp->cloud == CLOUD_RUN)
        {
            tmp->cloud = CLOUD_UP;
        }
    }
    pthread_rwlock_unlock(&accident_dispose_rwlock);
    return NULL;
}

/**
 * @description:  上云失败了执行该api(多线程异步安全)
 * @param {Accident_dispose_t} *tmp
 * @return {*}  0:执行成功  1:未通过校验 不予更改 -1:api无法支持功能
 */
int accident_cloud_df_api(struct Accident_dispose_t * tmp)
{
    if(0 == accident_dispose_en)
    {
        ems_syslog(LOG_DEBUG, "<accident_cloud_df_api>:事故反演初始化未完成 or 失败 api无法支持");
        return -1;
    }
    pthread_rwlock_wrlock(&accident_dispose_rwlock);
    if(NULL != tmp && 0 != tmp->taos_timeid && PACK_NON != tmp->pack && CLOUD_RUN == tmp->cloud)
    {
        if(CLOUD_RUN == tmp->cloud)
            tmp->cloud = CLOUD_UP;
        pthread_rwlock_unlock(&accident_dispose_rwlock);
        Accident_flush_taos_info(tmp);
        return 0;
    }
    pthread_rwlock_unlock(&accident_dispose_rwlock);
    
    return 1;
}


/**
 * @description: 查该id的info信息
 * @param {int} id
 * @return {*}
 */
char *get_accident_info(int id)
{
    
    for (int i = 0; i < accident_control.accident_rule_count; i++) 
    {
        accident_rule_t* rule = &accident_control.accident_rule[i];

        if(rule->id == id)
        {
            return rule->name;
        }
    }
        
    return "";
}

/**
 * @description: 创建事故信息accident.json文件
 * @param {Accident_dispose_t} *accident_pack
 * @param {char *} subdir_path
 * @return {*}
 */
int pack_accident_info(struct Accident_dispose_t *accident_pack, const char * subdir_path)
{
    
    // 创建并写入accident.json文件
    char file_path[256 + 50];
    snprintf(file_path, sizeof(file_path), "%s/accident.json", subdir_path);

    char start_time_str[64], happen_time_str[64], end_time_str[64];
    struct tm *tm_info;
    
    struct tm target_time = {};  
    localtime_r(&accident_pack->start_time, &target_time);
    tm_info = &target_time;
    strftime(start_time_str, sizeof(start_time_str), "%Y-%m-%d %H:%M:%S", tm_info);
    
    // 格式化发生时间 (使用当前时间作为发生时间)
    localtime_r(&accident_pack->Occurrence_time, &target_time);
    strftime(happen_time_str, sizeof(happen_time_str), "%Y-%m-%d %H:%M:%S", tm_info);
    
    // 格式化结束时间
    localtime_r(&accident_pack->end_time, &target_time);

    strftime(end_time_str, sizeof(end_time_str), "%Y-%m-%d %H:%M:%S", tm_info);

    // 创建JSON内容
    char json_content[512];
    
    snprintf(json_content, sizeof(json_content),
        "{\n"
        "    \"id\":%d,\n"
        "    \"info\":\"%s\",\n"
        "    \"granularity\":%d,\n"
        "    \"time\":{\n"
        "        \"startTime\": \"%s\",\n"
        "        \"triggerTime\": \"%s\",\n"
        "        \"endTime\": \"%s\"\n"
        "    }\n"
        "}",
        accident_pack->code_id, get_accident_info(accident_pack->code_id), time_granularity, start_time_str, happen_time_str, end_time_str);

    // 写入文件
    FILE *fp = fopen(file_path, "w");
    if (!fp) {
        ems_syslog(LOG_ERR, "<pack_accident_info>:Failed to create accident.json: %s", strerror(errno));
        return -4;
    }
    fprintf(fp, "%s", json_content);
    fclose(fp);

    return 0;
}




/**
 * @description: 从数据库打包历史数据
 * @return {*}
 */
int pack_history_data(struct Accident_dispose_t *accident_pack, const char * subdir_path)
{
    char time_str_start[64], time_str_end[64];
    struct lnxall_buff lbuf;
    int tg_tmp = time_granularity;

    tg_tmp = tg_tmp < 1 ? 1 : tg_tmp;
    tg_tmp = tg_tmp > 30 ? 30 : tg_tmp; // 默认限制

    lbuff_init(&lbuf, 200);
    str_time_get(accident_pack->start_time, time_str_start);
    str_time_get(accident_pack->end_time, time_str_end);
    for (int i = 0; i < dev_num; i++)   // 所有表数据解析
    {

        if (Accident_data_tab[i] == NULL) {
            continue;
        }
        memset(lbuf.bufptr, 0, lbuf.curlen);    // 清空
        lbuf.curlen = 0;

        struct list_head *pos, *n;
        lbuff_sprintf(&lbuf, "collectTime");
        list_for_each_safe(pos, n, &Accident_data_tab[i]->list) 
        {
            struct Accident_data_info *tag_info = list_entry(pos, struct Accident_data_info, list);
            // 获取标签数据
            struct Accident_data_info *data = accident_data_taos_get(tag_info, time_str_start, time_str_end);
            if (data == NULL) {
                ems_syslog(LOG_ERR, "<pack_history_data>:Failed to get data for tag %s", tag_info->tagname);
            }

            /*拼接第一行表信息*/
            lbuff_append(&lbuf, ",", 1);
            lbuff_append(&lbuf, tag_info->tagname, strlen(tag_info->tagname));
        }
        char *no = Accident_data_tab[i]->tabname;
        
        // 基于no与subdir_path创建.csv文件
        char value_tmp[256];
        if(strcmp(no, "EMS"))
            snprintf(value_tmp, sizeof(value_tmp), "%s/%s.csv", subdir_path, no);
        else
        {
            char sn[MAX_BASE_SHORT_LEN] = "";
            get_board_sn(sn);
            snprintf(value_tmp, sizeof(value_tmp), "%s/%s.csv", subdir_path, sn);
        }

            
            
        FILE *csv_fp = fopen(value_tmp, "w");
        if (!csv_fp) {
            ems_syslog(LOG_ERR, "<pack_history_data>:Failed to create csv file %s: %s", value_tmp, strerror(errno));
            continue;
        }
        fprintf(csv_fp, "%s\n", lbuf.bufptr);


        TAOS_ROW t;
        /*遍历覆写每一行*/
        for (int j = 0, j_max = accident_pack->end_time - accident_pack->start_time; j <= j_max; j+=tg_tmp) 
        {
            memset(lbuf.bufptr, 0, lbuf.curlen);
            lbuf.curlen = 0;
            memset(value_tmp, 0,sizeof(value_tmp));

            str_time_get(accident_pack->start_time + j, value_tmp);
            lbuff_append(&lbuf, value_tmp, strlen(value_tmp));

            memset(value_tmp, 0,sizeof(value_tmp));
            list_for_each_safe(pos, n, &Accident_data_tab[i]->list)
            {
                struct Accident_data_info *tag_info = list_entry(pos, struct Accident_data_info, list);
START_SWITCH:                
                switch (tag_info->mode_type)
                {
                    
                    case 0: /*正常数据列表*/
                    t = NULL;
                    if(tag_info->res != NULL)   t = taos_fetch_row(tag_info->res);
                    
                    
/*此 if(NULL == t){...} 为shit代码,因taos c/c++连接器的api中,无法在不执行taos_fetch_row()情况下,得知query其结果实际行数 
若执行taos_fetch_row 该迭代器将导致第一行数据再也无法访问,将导致数据帧错位
故将补救解析在此处再次迭代处理
本意在 accident_data_taos_get 一次性解决
或许你会说怎么不使用 taos_fetch_block(res, &row) ？ 那就请你解决 

{ (1h数据) taos_fetch_block(res, &row) = 3065 or 3571 or 3755} 后
还有数据存在 res 里 的未能完全获取全量数据还需要迭代器迭代 进而导致数据对齐 
结构体内需要创建TAOS_ROW、row_num TAOS_ROW与taos_fetch_row 交叉使用
 迭代器的特殊处理   
 
 等等的问题吧. 我不想围着他这机制慢慢转 时间紧任务重
*/
                    if(NULL == t)   
                    {
                        taos_free_result(tag_info->res);
                        tag_info->res = NULL;

                        char query[512] = "";
                        sprintf(query, "SELECT LAST(result) FROM %s WHERE time <= \'%s\'", tag_info->tabname, time_str_start);
                        tag_info->res = taos_query(accident_history, query);    // 查询时刻前最近一次记录的数据
                        
                        if (NULL == tag_info->res || taos_errno(tag_info->res) != 0) {
                            if(NULL != tag_info->res)
                            {
                                ems_syslog(LOG_ERR, "<pack_history_data>:Query failed: %s: %s", query, taos_errstr(tag_info->res));
                            }
                        }
                        else 
                        {
                            TAOS_ROW temp_row = taos_fetch_row(tag_info->res);
                            if (temp_row) {
                                TAOS_FIELD *fields = taos_fetch_fields(tag_info->res);
                                if (NULL != fields && fields[0].type == TSDB_DATA_TYPE_VARCHAR) {
                                    char *str_val = (char *)temp_row[0];
                                    
                                    tag_info->value = atof(str_val);
                                    tag_info->mode_type = 1; 

                                    taos_free_result(tag_info->res);
                                    tag_info->res = NULL;
                                    goto START_SWITCH;  // 重来一遍
                                    }
                            }
                        }

                        if(tag_info->res) taos_free_result(tag_info->res);             // 清理
                        tag_info->res = NULL;
                        tag_info->mode_type = 2;
                        goto START_SWITCH;  // 重来一遍

                    }
                    lbuff_append(&lbuf, ",", 1);

                    // 数据写入lbuf中
                    TAOS_FIELD *fields = taos_fetch_fields(tag_info->res);
                    if (fields[1].type == TSDB_DATA_TYPE_VARCHAR) {
                        char *str_val = (char *)t[1];
                        if(NULL == str_val)
                        {
                            lbuff_append(&lbuf, "NULL", 4);
                        }
                        else 
                        {
                            double val = atof(str_val);
                            lbuff_sprintf(&lbuf, DATA_ACCURACY, val);
                        }
                    } else {
                        lbuff_sprintf(&lbuf, DATA_ACCURACY, *(double *)t[1]);
                    }

                    break;
                    case 1:/* 1:无数据序列但前置时间内有数据执行全量覆盖写入*/
                    
                    snprintf(value_tmp, sizeof(value_tmp), DATA_ACCURACY, tag_info->value);
                    lbuff_append(&lbuf, ",", 1);
                    lbuff_append(&lbuf, value_tmp, strlen(value_tmp));

                    break;
                    case 2:/*空数据*/
                    lbuff_append(&lbuf, ",NULL", 5);

                    break;
                }
            }
            // 写入一次文件
            fprintf(csv_fp, "%s\n", lbuf.bufptr);
            fflush(csv_fp);
        }
        fclose(csv_fp);
        list_for_each_safe(pos, n, &Accident_data_tab[i]->list)
        {
            struct Accident_data_info *tag_info = list_entry(pos, struct Accident_data_info, list);
            if(tag_info->res) taos_free_result(tag_info->res);
            tag_info->res = NULL;

        }

    }
    return 0;
}
// malloc(): invalid size (unsorted)
/**
 * @description: 检查SSD挂载并创建事故打包文件
 * @param {struct Accident_dispose_t} *accident_pack 事故数据
 * @return {int} 0成功，其他失败
 */
int accident_top_pack(struct Accident_dispose_t *accident_pack)
{
    /* 暂时屏蔽 开发环境不检查
    struct statvfs buf;
    if (statvfs("/mnt/ssd/", &buf) != 0) {
        ems_syslog(LOG_ERR, "/mnt/ssd/ is not mounted");
        return -1;
    }
    */
    time_granularity = dev_get_dev_tag_int(DEV_NO_EMS, ACCIDENT_TGI);   // 打包时更新参数缓存

    if(accident_pack->pack_cnt >= 3)
    {
        return 5;
    }
    accident_pack->pack_cnt++;

    ems_syslog(LOG_ERR, "事故:%d, time_id:%ld  Occurence_time:%ld, ", accident_pack->code_id, accident_pack->taos_timeid, accident_pack->Occurrence_time);

    int ret = 0, buf_max = 1024;
    accident_pack->pack = PACK_RUN;
    if (mkdir(ACCIDENT_ZIP_PATH, 0755) != 0 && errno != EEXIST) {
        ems_syslog(LOG_ERR, "<accident_top_pack>:Failed to create accidnet directory: %s", strerror(errno));
        return 1;
    }

    char *buf = calloc(1, buf_max);
    char *tar_name = calloc(1, buf_max);

    char *subdir_path = ACCIDENT_DATA_TMP_PATH;

    if (mkdir(subdir_path, 0755) != 0) {
        if (errno == EEXIST) {            // 目录已存在，清空内容   确保下一数据包内容干净
            memset(buf, 0, strlen(buf));
            char *cmd = buf;
            snprintf(cmd, buf_max, "rm -rf \"%s\"/*", subdir_path);
            
            system(cmd);
        } else {
            ems_syslog(LOG_ERR, "<accident_top_pack>:Failed to create accident subdirectory: %s", strerror(errno));
            ret = -3;
            goto ATP_END;
        }
    }

    if(pack_accident_info(accident_pack, subdir_path) || pack_history_data(accident_pack, subdir_path))
    {
        // 清空目录
        memset(buf, 0, strlen(buf));
        char *cmd = buf;
        snprintf(cmd, buf_max, "rm -rf \"%s\"/*", subdir_path);
        system(cmd);
        ret = -1;
        goto ATP_END;
    }

    // 创建压缩包
    snprintf(tar_name, buf_max, "%s/%ld_%d.tar.gz", ACCIDENT_ZIP_PATH, accident_pack->taos_timeid, accident_pack->code_id);
    
    // 打包目录
    memset(buf, 0, strlen(buf));
    snprintf(buf, buf_max, "tar -czf \"%s\" -C \"%s\" .", tar_name, subdir_path);
    if (system(buf) != 0) {
        ems_syslog(LOG_ERR, "<accident_top_pack>:Failed to create tar.gz package");
        ret = -2;
        goto ATP_END;
    }

    get_file_md5(tar_name, accident_pack->md5);


    // 清理临时目录
    snprintf(buf, buf_max, "rm -rf \"%s\"", subdir_path);
    system(buf);

    accident_pack->pack = 2;
    accident_pack->cloud = 1;
ATP_END:
    free(buf);
    free(tar_name);
    return ret;
}



/**
 * @description: 将time_t时间转换为"年-月-日 时:分:秒"格式字符串
 * @param {time_t} time_now - 时间值
 * @param {char*} buf - 输出缓冲区(至少20字节)
 * @return {int} 成功返回0，失败返回-1
 */
int str_time_get(time_t time_now, char *buf)
{
    if (buf == NULL) {
        return -1;
    }
    struct tm target_time = {};  
    localtime_r(&time_now, &target_time);
    struct tm *tm_info = &target_time;
    if (tm_info == NULL) {
        return -1;
    }

    snprintf(buf, 73, "%04d-%02d-%02d %02d:%02d:%02d",
             tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
             tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec);

    return 0;
}

/**
 * @description: 将"年-月-日 时:分:秒"格式字符串转换为time_t
 * @param {const char*} time_str - 时间字符串(格式: "YYYY-MM-DD HH:MM:SS")
 * @param {time_t*} time_out - 输出time_t值
 * @return {int} 成功返回0，失败返回-1
 */
int str_time_parse(const char *time_str, time_t *time_out)
{
    if (time_str == NULL || time_out == NULL) {
        return -1;
    }

    struct tm tm_info = {0};
    char *ret = strptime(time_str, "%Y-%m-%d %H:%M:%S", &tm_info);
    if (ret == NULL || *ret != '\0') {
        return -1;
    }

    *time_out = mktime(&tm_info);
    if (*time_out == -1) {
        return -1;
    }

    return 0;
}






/**
 * @description: 获取数据函数，针对一个表，将其taos数据基于几种可能的处理机制解析  
 * @param {Accident_data_info} *tmp
 * @param {char} *time_str_start
 * @param {char} *time_str_end
 * @return {*}
 */
struct Accident_data_info *accident_data_taos_get(struct Accident_data_info *tmp, char *time_str_start, char *time_str_end)
{
    char query[1024] = "";
    tmp->mode_type = 0;
    sprintf(query, "SELECT t0.interval_start, t0.%s FROM ("
                       "SELECT _wstart AS interval_start, LAST(result) AS %s "
                       "FROM %s WHERE time >= \'%s\' "
                       "AND time <= \'%s\' "
                       "INTERVAL(1s) SLIDING(1s) FILL(PREV)) t0", tmp->tabname, tmp->tabname, tmp->tabname,  
                       time_str_start, 
                       time_str_end);

    tmp->res = taos_query(accident_history, query);        
    
    if (NULL == tmp->res || taos_errno(tmp->res) != 0) {
        if(NULL != tmp->res)
        {
            ems_syslog(LOG_DEBUG, "Query failed: %s: %s %d %d", query, taos_errstr(tmp->res), taos_errno(tmp->res), taos_affected_rows(tmp->res));
            taos_free_result(tmp->res);         // 清理
            tmp->res = NULL;
        }
    }
    else// 数据获取没问题便退出
    {
        tmp->mode_type = 0; 
        goto ADTG_END;
        
    }

    sprintf(query, "SELECT LAST(result) FROM %s WHERE time <= \'%s\'", tmp->tabname, time_str_start);
    tmp->res = taos_query(accident_history, query);    // 查询时刻前最近一次记录的数据
    if (NULL == tmp->res || taos_errno(tmp->res) != 0) {
        if(NULL != tmp->res)
        {
            ems_syslog(LOG_DEBUG, "<accident_data_taos_get>:Query failed: %s: %s", query, taos_errstr(tmp->res));
        }
    }
    else 
    {
        TAOS_ROW temp_row = taos_fetch_row(tmp->res);
        if (temp_row) {
            TAOS_FIELD *fields = taos_fetch_fields(tmp->res);
            if (fields[0].type == TSDB_DATA_TYPE_VARCHAR) {
                char *str_val = (char *)temp_row[0];
                tmp->value = atof(str_val);
                tmp->mode_type = 1; 
                
                taos_free_result(tmp->res);
                tmp->res = NULL;
                
                goto ADTG_END;

                }
        }
    }

    if(tmp->res) taos_free_result(tmp->res);             // 清理

    tmp->res = NULL;
    tmp->mode_type = 2;
ADTG_END:

    return tmp;
}


void accident_test()
{
    accident_record_api(1101, 2, 0);
    sleep(3);
    accident_record_api(1102, 2, 0);
    
    // struct Accident_dispose_t *tmp = accident_info_init(1001, 10, 10, time(NULL));
    // struct Accident_data_info tmps = 
    // {
    //     .tabname = "bms_soc",
    //     .tagname= "soc",
    //     .value = 0

    // };
    // char *time_start = "2025-06-13 17:00:00";
    // char *time_end = "2025-06-13 17:30:00";
    // accident_data_taos_get(&tmps, time_start, time_end);
    // tmp->taos_timeid = 1749869551151;
    // Accident_flush_taos_info(tmp);

    // return;
    // struct Accident_dispose_t accident_pack = {0};
    // accident_pack.start_time = 1749351600;
    // accident_pack.end_time = accident_pack.start_time + 3600;
    // accident_pack.code_id = 1001;
    // accident_pack.pack = 0;
    // accident_pack.cloud = 0;
    // accident_pack.front_time = accident_pack.start_time + 1800;
    // time_t time_now = time(NULL);
    // accident_top_pack(&accident_pack);
    // ems_syslog(LOG_ERR, "test to success kill the proto_forward 打包持续时间:%ld", (time(NULL) - time_now));

}


/**
 * @description: 打包、上报事件处理线程
 * @param {void} *arg
 * @return {*}
 */
void *accident_dispose_loop(void *arg)
{
    struct Accident_dispose_t *tmp = NULL;
    struct list_head *pos, *n;
    time_t current_time;
    accident_dispose_en = 1;
    while (1) {
        sleep(1);
        
        current_time = time(NULL);
        pthread_rwlock_rdlock(&accident_dispose_rwlock);
        list_for_each_safe(pos, n, &Accident_dispose->Accident_queue) 
        {
            tmp = list_entry(pos, struct Accident_dispose_t, Accident_queue);
            pthread_rwlock_unlock(&accident_dispose_rwlock);
            if(CLOUD_DEL == tmp->cloud)
            {
                Accident_flush_taos_info(tmp);
                accident_dispose_del(tmp);

            }
            if(0 == dev_get_dev_tag_int(DEV_NO_EMS,ACCIDENT_EN))
            {
                pthread_rwlock_rdlock(&accident_dispose_rwlock);
                continue;
            }
            
            if(tmp->pack < PACK_UP && current_time > tmp->end_time)   // 更新pack
            {
                pthread_rwlock_wrlock(&accident_dispose_rwlock);
                tmp->pack = PACK_UP;
                pthread_rwlock_unlock(&accident_dispose_rwlock);
            }

            if (tmp->pack == PACK_UP && tmp->pack_cnt < 3)
            {
                int ret = 0;
                time_t time_cnt = time(NULL);
                ret = accident_top_pack(tmp);
                pthread_rwlock_wrlock(&accident_dispose_rwlock);
                if(ret)
                {
                    ems_syslog(LOG_ERR, "事故:%d打包失败", tmp->code_id);
                    tmp->pack = PACK_ERR;
                }
                else 
                {
                    tmp->pack = PACK_END;
                    tmp->cloud = CLOUD_UP; // 更新cloud
                    ems_syslog(LOG_ERR, "事故:%d打包成功 耗时: %lds", tmp->code_id, time(NULL) - time_cnt);

                }
                pthread_rwlock_unlock(&accident_dispose_rwlock);
                Accident_flush_taos_info(tmp);
            }
            pthread_rwlock_rdlock(&accident_dispose_rwlock);

        }
        pthread_rwlock_unlock(&accident_dispose_rwlock);
    }
    
    return NULL;
}


#else
int accident_list_init()
{return 0;}
int accident_conn_taos_init()
{return 0;}
int accident_dispose_init()
{return 0;}
int accident_dispose_add(struct Accident_dispose_t *tmp)
{return 0;}
int Accident_flush_taos_info(struct Accident_dispose_t *tmp)
{return 0;}
int accident_dispose_del(struct Accident_dispose_t *tmp)
{return 0;}
struct Accident_data_info *accident_data_info_creat(char *tag_name, char *no)
{return NULL;}
int accident_data_list_init()
{return 0;}
struct Accident_dispose_t *accident_info_init(int code_id, int front_time, int after_time, time_t time_now)
{return NULL;}
int accident_record_api(int code_id, int front_time, int after_time)
{return 0;}
int accident_del_api(char *md5)
{return 0;}
struct Accident_dispose_t * accident_get_clould_api()
{return NULL;}
int accident_cloud_df_api(struct Accident_dispose_t * tmp)
{return 0;}
char *get_accident_info(int id)
{return NULL;}
int pack_accident_info(struct Accident_dispose_t *accident_pack, const char * subdir_path)
{return 0;}
int pack_history_data(struct Accident_dispose_t *accident_pack, const char * subdir_path)
{return 0;}
int accident_top_pack(struct Accident_dispose_t *accident_pack)
{return 0;}
int str_time_get(time_t time_now, char *buf)
{return 0;}
int str_time_parse(const char *time_str, time_t *time_out)
{return 0;}
struct Accident_data_info *accident_data_taos_get(struct Accident_data_info *tmp, char *time_str_start, char *time_str_end)
{return NULL;}
void accident_test()
{}
void *accident_dispose_loop(void *arg)
{return NULL;}
#endif
