#include <mutex>
#include <sstream>
#include <tuple>
#include <map>
#include <iostream>
#include <iomanip>

#include "dc_signal.h"
#include "dc_param.h"
#include "myLog.h"
#include "mySystem.h"
#include "myCmd.h"

#include "tinyxml2.h"

struct XXH128Hash {
    size_t operator()(const XXH128_hash_t& h) const noexcept {
        // 低版本库无XXH3_64bits_fromCanonical，手动混合高低64位生成哈希
        // 采用经典的混合算法，减少哈希冲突概率
        const uint64_t mix1 = h.low64 ^ (h.high64 << 17);
        const uint64_t mix2 = mix1 + (h.high64 >> 13);
        const uint64_t mix3 = mix2 ^ (h.low64 << 23);
        // 转size_t（兼容32/64位系统）
        return static_cast<size_t>(mix3 ^ (mix3 >> 32));
    }
};

struct XXH128Equal {
    bool operator()(const XXH128_hash_t& a, const XXH128_hash_t& b) const noexcept {
        // 优先用库自带的相等判断（v0.8.0+有），没有则手动比较
        #ifdef XXH128_isEqual
            return XXH128_isEqual(a, b);
        #else
            return (a.high64 == b.high64) && (a.low64 == b.low64);
        #endif
    }
};

typedef std::unordered_map<XXH128_hash_t, stru_signal, XXH128Hash, XXH128Equal> hash_signal_map;


typedef struct
{
    std::mutex mtx;
    uint32_t signal_id;
    hash_signal_map map_signals;
    std::unordered_map<XXH128_hash_t, std::vector<stru_signal*>, XXH128Hash, XXH128Equal> hash_conflict_table;
    std::unordered_map<XXH128_hash_t, std::string, XXH128Hash, XXH128Equal> hash_index;
    std::unordered_map<XXH128_hash_t, std::vector<std::string>, XXH128Hash, XXH128Equal> hash_conflicts;
}stru_signal_map;


typedef struct
{
    stru_signal_map signal_out;
    stru_signal_map signal_in;
    stru_signal_map signal_yk;
    stru_signal_map signal_ao;
    stru_signal_map signal_param;
}stru_datacenter;


static stru_datacenter g_datacenter = {};

LOCAL bool g_param_cfg_change = false;
LOCAL std::mutex g_param_cfg_change_mutex;



LOCAL int dc_data_compare(uint8_t data_type, void *p_data, void *p_data2);
LOCAL int dc_set_signal_val(void *p_data, uint8_t data_type, void *set_data);
LOCAL stru_signal* dc_find_signal(const XXH128_hash_t &hash, stru_signal_map &dc_signal_map);
LOCAL int dc_signal_add_to_map(stru_signal &signal, stru_signal_map &dc_signal_map);
LOCAL stru_signal* dc_find_out_signal(const std::string &saddr);
LOCAL stru_signal* dc_find_in_signal(const std::string &saddr);


bool dc_get_param_cfg_change()
{
    bool change = false;
    std::lock_guard<std::mutex> lock(g_param_cfg_change_mutex);
    change = g_param_cfg_change;

    return change;
}

void dc_set_param_cfg_change(bool change)
{
    std::lock_guard<std::mutex> lock(g_param_cfg_change_mutex);
    g_param_cfg_change = change;
}




int dc_get_out_signal_info(const std::string &saddr, std::string &desc, uint8_t &data_type, std::vector<void *> &vec_p_data)
{
    stru_signal *p_signal = dc_find_out_signal(saddr);
    if(p_signal == nullptr)
    {
        MY_LOG_E("saddr %s not found", saddr.c_str());
        return -1;
    }

    desc = p_signal->desc;
    data_type = p_signal->data_type;

    if(p_signal->vec_p_data.empty())
    {
        MY_LOG_E("saddr %s vec_p_data is empty", saddr.c_str());
        return -1;
    }

    if(nullptr == p_signal->vec_p_data[0])
    {
        MY_LOG_E("saddr %s vec_p_data[0] is nullptr", saddr.c_str());
        return -1;
    }

    vec_p_data.push_back(p_signal->vec_p_data[0]);

    return 0;
}

int dc_get_in_signal_info(const std::string &saddr, std::string &desc, uint8_t &data_type, std::vector<void *> &vec_p_data)
{
    stru_signal *p_signal = dc_find_in_signal(saddr);
    if(p_signal == nullptr)
    {
        MY_LOG_E("saddr %s not found", saddr.c_str());
        return -1;
    }

    desc = p_signal->desc;
    data_type = p_signal->data_type;

    if(!p_signal->vec_p_data.empty() && nullptr != p_signal->vec_p_data[0])
    {
        vec_p_data.push_back(p_signal->vec_p_data[0]);

        return 0;
    }

    MY_LOG_E("saddr %s vec_p_data is empty or vec_p_data[0] is nullptr", saddr.c_str());
    return -1;
}

int dc_get_ao_siganl_info(const std::string &saddr, std::string &desc, uint8_t &data_type, stru_signal_param &param, uint8_t &ctrl_type, std::vector<void *> &vec_p_data, std::vector<void *> &vec_p_default_data)
{
    stru_signal *p_signal = dc_find_signal(XXH3_128bits(saddr.c_str(), saddr.length()), g_datacenter.signal_ao);
    if(p_signal == nullptr)
    {
        MY_LOG_E("saddr %s not found", saddr.c_str());
        return -1;
    }

    desc = p_signal->desc;
    data_type = p_signal->data_type;
    param.min = p_signal->param.min;
    param.max = p_signal->param.max;
    param.step = p_signal->param.step;
    param.unit = p_signal->param.unit;
    ctrl_type = p_signal->ctrl_type;

    if(!p_signal->vec_p_data.empty() && nullptr != p_signal->vec_p_data[0])
    {
        vec_p_data.push_back(p_signal->vec_p_data[0]);
    }
    else
    {
        MY_LOG_E("saddr %s vec_p_data is empty or vec_p_data[0] is nullptr", saddr.c_str());
        return -1;
    }

    if(!p_signal->vec_p_default_data.empty() && nullptr != p_signal->vec_p_default_data[0])
    {
        vec_p_default_data.push_back(p_signal->vec_p_default_data[0]);
    }
    else
    {
        MY_LOG_E("saddr %s vec_p_default_data is empty or vec_p_default_data[0] is nullptr", saddr.c_str());
        return -1;
    }

    return 0;
}

int dc_get_param_signal_info(const std::string &saddr, std::string &desc, uint8_t &data_type, stru_signal_param &param, uint8_t &ctrl_type, std::vector<void *> &vec_p_data, std::vector<void *> &vec_p_default_data)
{
    stru_signal *p_signal = dc_find_signal(XXH3_128bits(saddr.c_str(), saddr.length()), g_datacenter.signal_param);
    if(p_signal == nullptr)
    {
        MY_LOG_E("saddr %s not found", saddr.c_str());
        return -1;
    }
    desc = p_signal->desc;
    data_type = p_signal->data_type;
    param.min = p_signal->param.min;
    param.max = p_signal->param.max;
    param.step = p_signal->param.step;
    param.unit = p_signal->param.unit;
    ctrl_type = p_signal->ctrl_type;
    
    for(size_t i = 0; i < p_signal->vec_p_data.size(); i++)
    {
        if(nullptr == p_signal->vec_p_data[i])
        {
            MY_LOG_E("saddr %s vec_p_data[%ld] is nullptr", saddr.c_str(), i);
            return -1;
        }

        vec_p_data.push_back(p_signal->vec_p_data[i]);
    }

    for(size_t i = 0; i < p_signal->vec_p_default_data.size(); i++)
    {
        if(nullptr == p_signal->vec_p_default_data[i])
        {
            MY_LOG_E("saddr %s vec_p_default_data[%ld] is nullptr", saddr.c_str(), i);
            return -1;
        }

        vec_p_default_data.push_back(p_signal->vec_p_default_data[i]);
    }

    return 0;
}

int dc_get_yk_signal_info(const std::string &saddr, std::string &desc, uint8_t &data_type, uint8_t &ctrl_type, std::vector<void *> &vec_p_data)
{
    stru_signal *p_signal = dc_find_signal(XXH3_128bits(saddr.c_str(), saddr.length()), g_datacenter.signal_yk);
    if(p_signal == nullptr)
    {
        MY_LOG_E("saddr %s not found", saddr.c_str());
        return -1;
    }
    desc = p_signal->desc;
    data_type = p_signal->data_type;
    ctrl_type = p_signal->ctrl_type;
    vec_p_data.push_back(p_signal->vec_p_data[0]);

    return 0;
}


void dc_signal_out_change_check()
{
    for(auto it = g_datacenter.signal_out.map_signals.begin(); it != g_datacenter.signal_out.map_signals.end(); it++)
    {
        stru_signal *p_signal = &it->second;
        if(!p_signal->out_change_cb_list.empty() && nullptr != p_signal->vec_p_data[0] && nullptr != p_signal->p_last_data)
        {
            if(0 != dc_data_compare(p_signal->data_type, p_signal->vec_p_data[0], p_signal->p_last_data))
            {
                for(auto cb : p_signal->out_change_cb_list)
                {
                    cb(p_signal->saddr, p_signal->data_type, p_signal->vec_p_data[0], p_signal->p_last_data);
                }

                dc_set_signal_val(p_signal->p_last_data, p_signal->data_type, p_signal->vec_p_data[0]);
            }
        }
    }
}

// LOCAL void dc_signal_ctrl_data_create(stru_signal_ctrl &ctrl)
// {
//     switch(ctrl.data_type)
//     {
//         case DATA_TYPE_B:
//         case DATA_TYPE_U8:
//             ctrl.p_data = new uint8_t;
//             *(uint8_t *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_S8:
//             ctrl.p_data = new int8_t;
//             *(int8_t *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_S16:
//             ctrl.p_data = new int16_t;
//             *(int16_t *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_U16:
//             ctrl.p_data = new uint16_t;
//             *(uint16_t *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_S32:
//             ctrl.p_data = new int32_t;
//             *(int32_t *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_U32:
//             ctrl.p_data = new uint32_t;
//             *(uint32_t *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_L64:
//             ctrl.p_data = new int64_t;
//             *(int64_t *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_UL64:
//             ctrl.p_data = new uint64_t;
//             *(uint64_t *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_F32:
//             ctrl.p_data = new float;
//             *(float *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_D64:
//             ctrl.p_data = new double;
//             *(double *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_IP:
//             ctrl.p_data = new char[4];
//             memset(ctrl.p_data, 0, 4);
//             break;
//         case DATA_TYPE_MAC:
//             ctrl.p_data = new char[8];
//             memset(ctrl.p_data, 0, 8);
//             break;
//         case DATA_TYPE_C1:
//             ctrl.p_data = new char;
//             *(char *)ctrl.p_data = 0;
//             break;
//         case DATA_TYPE_C8:
//             ctrl.p_data = new char[8];
//             memset(ctrl.p_data, 0, 8);
//             break;
//         case DATA_TYPE_C32:
//             ctrl.p_data = new char[32];
//             memset(ctrl.p_data, 0, 32);
//             break;
//         case DATA_TYPE_C64:
//             ctrl.p_data = new char[64];
//             memset(ctrl.p_data, 0, 64);
//             break;
//         case DATA_TYPE_C128:
//             ctrl.p_data = new char[128];
//             memset(ctrl.p_data, 0, 128);
//             break;
//         default:
//             LOG_E("unsupported data type %d", ctrl.data_type);
//             break;
//     }
// }

LOCAL int dc_data_compare(uint8_t data_type, void *p_data, void *p_data2)
{
    if(p_data == nullptr || p_data2 == nullptr)
    {
        LOG_E("p_data or p_data2 is nullptr");
        return -1;
    }

    float f1 = 0.0f;
    float f2 = 0.0f;
    double d1 = 0.0;
    double d2 = 0.0;

    switch(data_type)
    {
        case DATA_TYPE_B:
        case DATA_TYPE_U8:
            return (*(uint8_t *)p_data == *(uint8_t *)p_data2) ? 0 : -1;
        case DATA_TYPE_S8:
            return (*(int8_t *)p_data == *(int8_t *)p_data2) ? 0 : -1;
        case DATA_TYPE_S16:
            return (*(int16_t *)p_data == *(int16_t *)p_data2) ? 0 : -1;
        case DATA_TYPE_U16:
            return (*(uint16_t *)p_data == *(uint16_t *)p_data2) ? 0 : -1;
        case DATA_TYPE_S32:
            return (*(int32_t *)p_data == *(int32_t *)p_data2) ? 0 : -1;
        case DATA_TYPE_U32:
            return (*(uint32_t *)p_data == *(uint32_t *)p_data2) ? 0 : -1;
        case DATA_TYPE_L64:
            return (*(int64_t *)p_data == *(int64_t *)p_data2) ? 0 : -1;
        case DATA_TYPE_UL64:
            return (*(uint64_t *)p_data == *(uint64_t *)p_data2) ? 0 : -1;
        case DATA_TYPE_F32:
            f1 = *(float *)p_data;
            f2 = *(float *)p_data2;
            return f1 == f2 ? 0 : -1;
        case DATA_TYPE_D64:
            d1 = *(double *)p_data;
            d2 = *(double *)p_data2;
            return d1 == d2 ? 0 : -1;
        case DATA_TYPE_C1:
            return (*(char *)p_data == *(char *)p_data2) ? 0 : -1;
        case DATA_TYPE_IP:
        case DATA_TYPE_MAC:
        case DATA_TYPE_C8:
        case DATA_TYPE_C32:
        case DATA_TYPE_C64:
        case DATA_TYPE_C128:
        case DATA_TYPE_STR:
            return (0 == strcmp((char *)p_data, (char *)p_data2)) ? 0 : -1;
        default:
            LOG_E("unsupported data type %d", data_type);
            return -1;
    }

    return -1;
}

LOCAL bool dc_signal_ao_add_check(stru_signal *p_signal, const std::string &desc, uint8_t data_type, const stru_signal_param &param, uint8_t ctrl_type, void *p_data, void *p_default_data)
{
    if(p_signal == nullptr || p_data == nullptr || p_default_data == nullptr)
    {
        LOG_E("p_signal, p_data or p_default_data is nullptr");
        return false;
    }

    bool change = false;

    if(p_signal->desc != desc)
    {
        p_signal->desc = desc;
        change = true;
    }

    if(p_signal->data_type != data_type)
    {
        p_signal->data_type = data_type;
        change = true;
    }

    if(p_signal->param.min != param.min || p_signal->param.max != param.max || p_signal->param.step != param.step || p_signal->param.unit != param.unit)
    {
        p_signal->param = param;
        change = true;
    }

    if(p_signal->ctrl_type != ctrl_type)
    {
        p_signal->ctrl_type = ctrl_type;
        change = true;
    }

    // if(0 != dc_data_compare(p_signal->data_type, p_data, p_signal->vec_p_data[0]))
    // {
    //     change = true;
    // }

    if(0 != dc_data_compare(p_signal->data_type, p_default_data, p_signal->vec_p_default_data[0]))
    {
        change = true;
    }

    return change;
}

LOCAL bool dc_signal_param_add_check(stru_signal *p_signal, const std::string &desc, uint8_t data_type, const stru_signal_param &param, uint8_t ctrl_type, std::vector<void *> &vec_p_data, std::vector<void *> &vec_p_default_data)
{
    bool change = false;

    if(p_signal->desc != desc)
    {
        p_signal->desc = desc;
        change = true;
    }

    if(p_signal->data_type != data_type)
    {
        p_signal->data_type = data_type;
        change = true;
    }

    if(p_signal->param.min != param.min || p_signal->param.max != param.max || p_signal->param.step != param.step || p_signal->param.unit != param.unit)
    {
        p_signal->param = param;
        change = true;
    }

    if(p_signal->ctrl_type != ctrl_type)
    {
        p_signal->ctrl_type = ctrl_type;
        change = true;
    }

    // for(size_t i = 0; i < vec_p_default_data.size(); i++)
    // {
    //     if(0 != dc_data_compare(p_signal->data_type, vec_p_default_data[i], p_signal->vec_p_default_data[i]))
    //     {
    //         change = true;
    //     }
    // }

    for(size_t i = 0; i < vec_p_data.size(); i++)
    {
        if(0 != dc_data_compare(p_signal->data_type, vec_p_data[i], p_signal->vec_p_data[i]))
        {
            change = true;
        }
    }

    return change;
}


LOCAL int dc_check_ctrl_val_valid(const stru_signal_ctrl &ctrl, const void *p_data)
{
    if(ctrl.data_type == DATA_TYPE_B || ctrl.data_type == DATA_TYPE_U8)
    {
        if(*(uint8_t *)p_data != *(uint8_t *)ctrl.p_data)
        {
            LOG_E("direct ctrl value %d not match selected value %d", *(uint8_t*)p_data, *(uint8_t*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_S8)
    {
        if(*(int8_t *)p_data != *(int8_t *)ctrl.p_data)
        {
            LOG_E("direct ctrl value %d not match selected value %d", *(int8_t*)p_data, *(int8_t*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_S16)
    {
        if(*(int16_t *)p_data != *(int16_t *)ctrl.p_data)
        {
            LOG_E("direct ctrl value %d not match selected value %d", *(int16_t*)p_data, *(int16_t*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_U16)
    {
        if(*(uint16_t *)p_data != *(uint16_t *)ctrl.p_data)
        {
            LOG_E("direct ctrl value %d not match selected value %d", *(uint16_t*)p_data, *(uint16_t*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_S32)
    {
        if(*(int32_t *)p_data != *(int32_t *)ctrl.p_data)
        {
            LOG_E("direct ctrl value %d not match selected value %d", *(int32_t*)p_data, *(int32_t*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_U32)
    {
        if(*(uint32_t *)p_data != *(uint32_t *)ctrl.p_data)
        {
            LOG_E("direct ctrl value %d not match selected value %d", *(uint32_t*)p_data, *(uint32_t*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_L64)
    {
        if(*(int64_t *)p_data != *(int64_t *)ctrl.p_data)
        {
            LOG_E("direct ctrl value %lld not match selected value %lld", *(int64_t*)p_data, *(int64_t*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_UL64)
    {
        if(*(uint64_t *)p_data != *(uint64_t *)ctrl.p_data)
        {
            LOG_E("direct ctrl value %lld not match selected value %lld", *(uint64_t*)p_data, *(uint64_t*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_F32)
    {
        if(*(float*)p_data != *(float*)ctrl.p_data)
        {
            LOG_E("direct ctrl value %f not match selected value %f", *(float*)p_data, *(float*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_D64)
    {
        if(*(double*)p_data != *(double*)ctrl.p_data)
        {
            LOG_E("direct ctrl value %lf not match selected value %lf", *(double*)p_data, *(double*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_IP)
    {
        if(0 != memcmp((char *)ctrl.p_data, (char *)p_data, 4));
        {
            LOG_E("direct ctrl value %s not match selected value %s", 
                dc_get_signal_val(ctrl.p_data, DATA_TYPE_IP).c_str(), dc_get_signal_val((char *)p_data, DATA_TYPE_IP).c_str());
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_MAC)
    {
        if(0 != memcmp((char *)ctrl.p_data, (char *)p_data, 6));
        {
            LOG_E("direct ctrl value %s not match selected value %s", 
                dc_get_signal_val(ctrl.p_data, DATA_TYPE_MAC).c_str(), dc_get_signal_val((char *)p_data, DATA_TYPE_MAC).c_str());
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_C1)
    {
        if(*(char*)p_data != *(char*)ctrl.p_data)
        {
            LOG_E("direct ctrl value %c not match selected value %c", *(char*)p_data, *(char*)ctrl.p_data);
            return -1;
        }
    }
    else if(ctrl.data_type == DATA_TYPE_C8 || ctrl.data_type == DATA_TYPE_C32 || ctrl.data_type == DATA_TYPE_C64 || ctrl.data_type == DATA_TYPE_C128 || ctrl.data_type == DATA_TYPE_STR)
    {
        if(0 != strcmp((char *)ctrl.p_data, (char *)p_data))
        {
            LOG_E("direct ctrl value %s not match selected value %s", (char *)ctrl.p_data, (char *)p_data);
            return -1;
        }
    }
    else
    {
        LOG_E("invalid ctrl data_type %d", ctrl.data_type);
        return -1;
    }

    return 0;
}

LOCAL int dc_check_ctrl_valid(const stru_signal *p_signal, SIGNAL_CTRL_STEP step, stru_signal_ctrl &ctrl, const void *p_data)
{
    // LOG_I("dc_check_ctrl_valid %s, step %d, data_type %d, p_data %d", p_signal->saddr.c_str(), step, data_type, *(uint8_t *)p_data);

    if(p_signal->data_type != ctrl.data_type)
    {
        LOG_E("%s data_type %d not match ctrl data_type %d", p_signal->saddr.c_str(), p_signal->data_type, ctrl.data_type);
        return -1;
    }

    if(ctrl.type == SIGNAL_CTRL_TYPE::DIRECT_NORMAL)
    {
        if(step == SIGNAL_CTRL_STEP::DIRECT)
        {
            return 0;
        }
        else
        {
            LOG_E("%s ctrl_type %d, invalid ctrl step %d", p_signal->saddr.c_str(), ctrl.type, ctrl.step);
            return -1;
        }
    }
    else if(ctrl.type == SIGNAL_CTRL_TYPE::SBO_NORMAL)
    {
        if(ctrl.step == SIGNAL_CTRL_STEP::READY)
        {
            if(step == SIGNAL_CTRL_STEP::SELECT)
            {
                return 0;
            }
            else
            {
                LOG_E("%s ctrl_type %d, invalid ctrl step %d", p_signal->saddr.c_str(), ctrl.type, ctrl.step);
                return -1;
            }
        }
        else if(ctrl.step == SIGNAL_CTRL_STEP::SELECT)
        {
            if(step == SIGNAL_CTRL_STEP::DIRECT)
            {
                if(0 == dc_check_ctrl_val_valid(ctrl, p_data))
                {
                    return 0;
                }
                else
                {
                    LOG_E("%s ctrl_type %d, direct value not match selected value", p_signal->saddr.c_str(), ctrl.type);  
                    return -1;
                }
            }
            else if(step == SIGNAL_CTRL_STEP::CANCEL)
            {
                return 0;
            }
            else
            {
                LOG_E("%s ctrl_type %d, invalid ctrl step %d", p_signal->saddr.c_str(), ctrl.type, ctrl.step);
                return -1;
            }
        }
    }

    LOG_E("invalid ctrl type %d", ctrl.type);
    return -1;
}


LOCAL int dc_check_val_valid(const stru_signal *p_signal, uint8_t setting_zone, const void *p_data)
{
    stru_signal_param param = p_signal->param;

    if(setting_zone >= p_signal->vec_p_data.size())
    {
        LOG_E("%s setting_zone %d out of range, vec_p_data size %d", p_signal->saddr.c_str(), setting_zone, (int)p_signal->vec_p_data.size());
        return -1;
    }

    void *current_p_data = p_signal->vec_p_data[setting_zone];

    if(p_signal->data_type == DATA_TYPE_B)
    {
        if(*(uint8_t *)current_p_data == *(uint8_t *)p_data)
        {
            LOG_E("%s value %d not changed", p_signal->saddr.c_str(), *(uint8_t*)current_p_data);
            return -1;
        }

        uint8_t b = *(uint8_t*)p_data;
        if(b != 0 && b != 1)
        {
            LOG_E("invalid bool value %d", b);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_S8)
    {
        if(*(int8_t*)current_p_data == *(int8_t*)p_data)
        {
            LOG_E("%s value %d not changed", p_signal->saddr.c_str(), *(int8_t*)current_p_data);
            return -1;
        }

        int8_t s8 = *(int8_t*)p_data;
        if(s8 < (int8_t)param.min || s8 > (int8_t)param.max)
        {
            LOG_E("invalid int8_t value %d, range [%f, %f]", s8, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_U8)
    {
        if(*(uint8_t*)current_p_data == *(uint8_t*)p_data)
        {
            LOG_E("%s value %d not changed", p_signal->saddr.c_str(), *(uint8_t*)current_p_data);
            return -1;
        }

        uint8_t u8 = *(uint8_t*)p_data;
        if(u8 < param.min || u8 > param.max)
        {
            LOG_E("invalid uint8_t value %d, range [%f, %f]", u8, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_S16)
    {
        if(*(int16_t*)current_p_data == *(int16_t*)p_data)
        {
            LOG_E("%s value %d not changed", p_signal->saddr.c_str(), *(int16_t*)current_p_data);
            return -1;
        }

        int16_t s16 = *(int16_t*)p_data;
        if(s16 < (int16_t)param.min || s16 > (int16_t)param.max)
        {
            LOG_E("invalid int16_t value %d, range [%d, %d]", s16, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_U16)
    {
        if(*(uint16_t*)current_p_data == *(uint16_t*)p_data)
        {
            LOG_E("%s value %d not changed", p_signal->saddr.c_str(), *(uint16_t*)current_p_data);
            return -1;
        }

        uint16_t u16 = *(uint16_t*)p_data;
        if(u16 < param.min || u16 > param.max)
        {
            LOG_E("invalid uint16_t value %d, range [%f, %f]", u16, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_S32)
    {
        if(*(int32_t*)current_p_data == *(int32_t*)p_data)
        {
            LOG_E("%s value %d not changed", p_signal->saddr.c_str(), *(int32_t*)current_p_data);
            return -1;
        }

        int32_t s32 = *(int32_t*)p_data;
        if(s32 < (int32_t)param.min || s32 > (int32_t)param.max)
        {
            LOG_E("invalid int32_t value %d, range [%f, %f]", s32, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_U32)
    {
        if(*(uint32_t*)current_p_data == *(uint32_t*)p_data)
        {
            LOG_E("%s value %d not changed", p_signal->saddr.c_str(), *(uint32_t*)current_p_data);
            return -1;
        }

        uint32_t u32 = *(uint32_t*)p_data;
        if(u32 < param.min || u32 > param.max)
        {
            LOG_E("invalid uint32_t value %d, range [%f, %f]", u32, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_L64)
    {
        if(*(int64_t*)current_p_data == *(int64_t*)p_data)
        {
            LOG_E("%s value %lld not changed", p_signal->saddr.c_str(), *(int64_t*)current_p_data);
            return -1;
        }

        int64_t l64 = *(int64_t*)p_data;
        if(l64 < (int64_t)param.min || l64 > (int64_t)param.max)
        {
            LOG_E("invalid int64_t value %lld, range [%f, %f]", l64, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_UL64)
    {
        if(*(uint64_t*)current_p_data == *(uint64_t*)p_data)
        {
            LOG_E("%s value %llu not changed", p_signal->saddr.c_str(), *(uint64_t*)current_p_data);
            return -1;
        }

        uint64_t ul64 = *(uint64_t*)p_data;
        if(ul64 < param.min || ul64 > param.max)
        {
            LOG_E("invalid uint64_t value %llu, range [%f, %f]", ul64, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_F32)
    {
        if(*(float*)current_p_data == *(float*)p_data)
        {
            LOG_E("%s value %f not changed", p_signal->saddr.c_str(), *(float*)current_p_data);
            return -1;
        }

        float f32 = *(float*)p_data;
        if(f32 < (float)param.min || f32 > (float)param.max)
        {
            LOG_E("invalid float value %f, range [%f, %f]", f32, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_D64)
    {
        if(*(double*)current_p_data == *(double*)p_data)
        {
            LOG_E("%s value %f not changed", p_signal->saddr.c_str(), *(double*)current_p_data);
            return -1;
        }

        double d64 = *(double*)p_data;
        if(d64 < (double)param.min || d64 > (double)param.max)
        {
            LOG_E("invalid double value %f, range [%f, %f]", d64, param.min, param.max);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_IP || 
            p_signal->data_type == DATA_TYPE_MAC ||
            p_signal->data_type == DATA_TYPE_C8 ||
            p_signal->data_type == DATA_TYPE_C32 ||
            p_signal->data_type == DATA_TYPE_C64 ||
            p_signal->data_type == DATA_TYPE_C128 ||
            p_signal->data_type == DATA_TYPE_STR)
    {
        if(0 == strcmp((char *)current_p_data, (char *)p_data))
        {
            LOG_E("%s value %s not changed", p_signal->saddr.c_str(), (char*)current_p_data);
            return -1;
        }
    }
    else if(p_signal->data_type == DATA_TYPE_C1)
    {
        if(*(char*)current_p_data == *(char*)p_data)
        {
            LOG_E("%s value %c not changed", p_signal->saddr.c_str(), *(char*)current_p_data);
            return -1;
        }
    }
    else
    {
        LOG_E("data_type is not valid: %d", p_signal->data_type);
        return -1;
    }

    return 0;
}



LOCAL void dc_update_global_index(const stru_signal &signal, stru_signal_map &dc_signal_map)
{
    auto it = dc_signal_map.hash_index.find(signal.hash);
    if(it != dc_signal_map.hash_index.end())
    {
        if(it->second != signal.saddr)
        {
            MY_LOG_E("hash collision: %s -> %s", it->second.c_str(), signal.saddr.c_str());
            dc_signal_map.hash_conflicts[signal.hash].push_back(it->second);
            dc_signal_map.hash_conflicts[signal.hash].push_back(signal.saddr);
        }
    }
    else
    {
        dc_signal_map.hash_index.emplace(signal.hash, signal.saddr);
    }
}


LOCAL int dc_signal_add_to_map(stru_signal &signal, stru_signal_map &dc_signal_map)
{
    std::lock_guard<std::mutex> lock(dc_signal_map.mtx);

    hash_signal_map &signal_map = dc_signal_map.map_signals;
    auto conflict_it = dc_signal_map.hash_conflict_table.find(signal.hash);
    if (conflict_it != dc_signal_map.hash_conflict_table.end()) 
    {
        for(stru_signal *exist_signal : conflict_it->second)
        {
            if(exist_signal->saddr == signal.saddr)
            {
                uint32_t old_id = exist_signal->id;
                *exist_signal = signal;
                exist_signal->id = old_id;
                return 0;
            }
        }
        auto [it, inserted] = signal_map.emplace(signal.hash, signal);
        stru_signal *new_sig = &it->second;
        new_sig->id = dc_signal_map.signal_id++;
        conflict_it->second.push_back(new_sig);

        dc_update_global_index(*new_sig, dc_signal_map);
    }
    else
    {
        auto map_it = signal_map.find(signal.hash);
        if(map_it != signal_map.end())
        {
            if(map_it->second.saddr != signal.saddr)
            {
                std::vector<stru_signal*> conficts;
                conficts.push_back(&map_it->second);
                
                auto [it, inserted] = signal_map.emplace(signal.hash, signal);
                stru_signal *new_sig = &it->second;
                new_sig->id = dc_signal_map.signal_id++;

                conficts.push_back(new_sig);
                dc_signal_map.hash_conflict_table[signal.hash] = conficts;

                dc_update_global_index(*new_sig, dc_signal_map);
                return 0;
            }
            else
            {
                auto old_id = map_it->second.id;
                map_it->second = signal;
                map_it->second.id = old_id;
                return 0;
            }
        }
        else
        {
            auto [it, inserted] = signal_map.emplace(signal.hash, signal);
            stru_signal &new_sig = it->second;
            new_sig.id = dc_signal_map.signal_id++;

            dc_update_global_index(new_sig, dc_signal_map);
            return 0;
        }
    }
        
    return 0;
}


int dc_signal_out(const std::string &saddr, const std::string &desc, uint8_t data_type, void *p_data)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    stru_signal *p_signal = dc_find_out_signal(saddr);
    if(nullptr != p_signal)
    {
        MY_LOG_E("saddr %s already registered", saddr.c_str());
        return -1;
    }

    stru_signal signal;

    signal.hash = XXH3_128bits(saddr.c_str(), saddr.length());
    signal.saddr.assign(saddr);
    signal.desc.assign(desc);
    signal.data_type = data_type;
    signal.vec_p_data.push_back(p_data);
    void *p_last_data = dc_create_data_ptr_by_type(data_type);
    if(p_last_data == nullptr)
    {
        MY_LOG_E("create p_last_data failed");
        return -1;
    }
    signal.p_last_data = p_last_data;

    // MY_LOG_I("saddr %s, p_data %p", saddr.c_str(), p_data);
    return dc_signal_add_to_map(signal, g_datacenter.signal_out);
}

int dc_signal_out_with_callback(const std::string &saddr, const std::string &desc, uint8_t data_type, void *p_data, out_signal_change_cb cb)
{
    if(nullptr == p_data)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    stru_signal *p_signal = dc_find_out_signal(saddr);
    if(nullptr != p_signal)
    {
        MY_LOG_E("saddr %s already registered", saddr.c_str());
        return -1;
    }

    stru_signal signal;
    signal.hash = XXH3_128bits(saddr.c_str(), saddr.length());
    signal.saddr.assign(saddr);
    signal.desc.assign(desc);
    signal.data_type = data_type;
    signal.vec_p_data.push_back(p_data);
    void *p_last_data = dc_create_data_ptr_by_type(data_type);
    if(p_last_data == nullptr)
    {
        MY_LOG_E("create p_last_data failed");
        return -1;
    }
    signal.p_last_data = p_last_data;
    if(nullptr != cb)
    {
        signal.out_change_cb_list.push_back(cb);
    }

    return dc_signal_add_to_map(signal, g_datacenter.signal_out);
}

int dc_signal_in(const std::string &saddr, const std::string &desc, const std::string &link_saddr, uint8_t data_type, void **p_data)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    stru_signal *p_signal = dc_find_in_signal(saddr);
    if(nullptr != p_signal)
    {
        MY_LOG_E("saddr %s already registered", saddr.c_str());
        return -1;
    }

    stru_signal signal;

    signal.hash = XXH3_128bits(saddr.c_str(), saddr.length());
    signal.saddr.assign(saddr);
    signal.desc.assign(desc);
    signal.data_type = data_type;
    
    if(link_saddr.empty())
    {
        MY_LOG_E("link_saddr is empty");
        return -1;
    }

    stru_signal *p_signal_out = dc_find_out_signal(link_saddr);
    if(p_signal_out == nullptr)
    {
        MY_LOG_E("link_saddr %s not found", link_saddr.c_str());
        return -1;
    }
    
    (*p_data) = p_signal_out->vec_p_data[0];
    signal.vec_p_data.push_back((*p_data));

    bool found = false;
    for(auto it = p_signal_out->link_saddrs.begin(); it != p_signal_out->link_saddrs.end(); ++it)
    {
        if(it->compare(saddr) == 0)
        {
            found = true;
            break;
        }
    }

    if(!found)
    {
        p_signal_out->link_saddrs.push_back(saddr);
    }

    signal.link_saddrs.push_back(link_saddr);

    MY_LOG_I("saddr %s, p_data %p, link_saddr %s", saddr.c_str(), (*p_data), link_saddr.c_str());

    return dc_signal_add_to_map(signal, g_datacenter.signal_in);
}

int dc_signal_in_with_callback(const std::string &saddr, const std::string &desc, const std::string &link_saddr, uint8_t data_type, void **p_data, out_signal_change_cb cb)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    stru_signal *p_signal = dc_find_in_signal(saddr);
    if(nullptr != p_signal)
    {
        MY_LOG_E("saddr %s already registered", saddr.c_str());
        return -1;
    }

    stru_signal signal;
    signal.hash = XXH3_128bits(saddr.c_str(), saddr.length());
    signal.saddr.assign(saddr);
    signal.desc.assign(desc);
    signal.data_type = data_type;

    if(link_saddr.empty())
    {
        MY_LOG_E("link_saddr is empty");
        return -1;
    }

    stru_signal *p_signal_out = dc_find_out_signal(link_saddr);
    if(p_signal_out == nullptr)
    {
        MY_LOG_E("link_saddr %s not found", link_saddr.c_str());
        return -1;
    }

    (*p_data) = p_signal_out->vec_p_data[0];
    signal.vec_p_data.push_back((*p_data));

    if(nullptr != cb)
    {
        p_signal_out->out_change_cb_list.push_back(cb);
    }

    p_signal_out->link_saddrs.push_back(saddr);

    signal.link_saddrs.push_back(link_saddr);

    return dc_signal_add_to_map(signal, g_datacenter.signal_in);
}


int dc_signal_ao(const std::string &saddr, const std::string &desc, uint8_t data_type, const stru_signal_param &param, uint8_t ctrl_type, void *p_data, void *p_default_data, signal_change_cb cb)
{
    if(p_data == nullptr || p_default_data == nullptr)
    {
        MY_LOG_E("p_data or p_default_data is nullptr");
        return -1;
    }

    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_ao);
    if(p_signal != nullptr && !p_signal->vec_p_data.empty() && !p_signal->vec_p_default_data.empty() && nullptr != p_signal->vec_p_data[0] && nullptr != p_signal->vec_p_default_data[0])
    {
        if(true == dc_signal_ao_add_check(p_signal, desc, data_type, param, ctrl_type, p_data, p_default_data))
        {
            dc_set_param_cfg_change(true);
        }
        else
        {
            dc_set_signal_val(p_data, data_type, p_signal->vec_p_data[0]);
        }

        // delete p_signal->vec_p_data[0];
        dc_delete_signal_data(p_signal->vec_p_data[0], data_type);
        p_signal->vec_p_data[0] = p_data;

        p_signal->change_cb_list.push_back(cb);
        return 0;
    }
    
    dc_set_param_cfg_change(true);

    stru_signal signal;
    signal.hash = hash;
    signal.saddr.assign(saddr);
    signal.desc.assign(desc);
    signal.data_type = data_type;
    signal.param = param;
    signal.ctrl_type = ctrl_type;

    dc_signal_add_to_map(signal, g_datacenter.signal_ao);

    p_signal = dc_find_signal(hash, g_datacenter.signal_ao);
    if(p_signal != nullptr )
    {
        p_signal->vec_p_data.push_back(p_data);
        p_signal->vec_p_default_data.push_back(p_default_data);

        if(cb)
        {
            p_signal->change_cb_list.push_back(cb);
        }
        
        return 0;
    }

    return -1;
}

int dc_signal_ao_link_with_callback(const std::string &saddr, uint8_t data_type, void **p_data, signal_change_cb cb)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_ao);
    if(p_signal != nullptr)
    {
        if(cb)
        {
            p_signal->change_cb_list.push_back(cb);
        }

        (*p_data) = p_signal->vec_p_data.empty() ? nullptr : p_signal->vec_p_data[0];
        return (*p_data) != nullptr ? 0 : -1;
    }

    return -1;
}

int dc_signal_ao_set_val(const std::string &saddr, SIGNAL_CTRL_STEP step, stru_signal_ctrl &ctrl, void *p_data)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_ao);
    if(p_signal != nullptr)
    {
        if(0 == dc_check_ctrl_valid(p_signal, step, ctrl, p_data))
        {
            if(0 != dc_check_val_valid(p_signal, 0, p_data))
            {
                LOG_E("invalid value for saddr %s", saddr.c_str());

                return -1;
            }

            if(step == SIGNAL_CTRL_STEP::SELECT)
            {
                dc_set_signal_val(ctrl.p_data, ctrl.data_type, p_data);
                ctrl.step = step;
            }
            else if(step == SIGNAL_CTRL_STEP::DIRECT)
            {
                dc_set_signal_val(p_signal->vec_p_data[0], ctrl.data_type, p_data);
                ctrl.step = SIGNAL_CTRL_STEP::READY;
            }
            else if(step == SIGNAL_CTRL_STEP::CANCEL)
            {
                ctrl.step = SIGNAL_CTRL_STEP::READY;
            }
            else
            {
                LOG_E("invalid step %d", step);
                return -1;
            }

            for(auto cb : p_signal->change_cb_list)
            {
                if(cb)
                {
                    cb(saddr, step, ctrl.data_type, 0, p_data);
                }
            }

            dc_set_param_cfg_change(true);

            return 0;
        }
        else
        {
            LOG_E("saddr %s ctrl invalid", saddr.c_str());
            ctrl.step = SIGNAL_CTRL_STEP::READY;
            return -1;
        }
    }

    LOG_E("saddr %s not found", saddr.c_str());

    return -1;
}

int dc_signal_ao_set_val_without_check(const std::string &saddr, uint8_t data_type, void *p_data)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_ao);
    if(p_signal != nullptr && !p_signal->vec_p_data.empty() && p_signal->data_type == data_type && p_signal->vec_p_data[0])   
    {
        if(p_signal->data_type != data_type)
        {
            LOG_E("saddr %s data type %s(%d) not match ctrl data type %s(%d)", 
                saddr.c_str(), dc_get_data_type_str_by_id(p_signal->data_type), p_signal->data_type, dc_get_data_type_str_by_id(data_type), data_type);
            return -1;
        }

        dc_set_signal_val(p_signal->vec_p_data[0], data_type, p_data);
        dc_set_param_cfg_change(true);
        return 0;
    }
    else
    {
        // LOG_E("saddr %s, p_signal %p, vec_size %d, data_type %d,%d, p_data %p", saddr.c_str(), p_signal, p_signal->vec_p_data.size(), p_signal->data_type, data_type, p_signal->vec_p_data[0]);
    }

    LOG_E("saddr %s not found in ao table", saddr.c_str());
    return -1;
}

int dc_signal_param(const std::string &saddr, const std::string &desc, uint8_t data_type, const stru_signal_param &param, uint8_t ctrl_type, std::vector<void *> &vec_p_data, std::vector<void *> &vec_p_default_data, signal_change_cb cb)
{
    // 先从参数表里解析，调用参数注册
    // 程序注册时，从参数表里去查找，同一个信息，使用参数表中的保存的数据值，在线程初始化时，通知所有链接此信号的信号
    // 注册时不在参数表中的，新增到动态表里，并保存到参数表里，使用缺省值

    if(vec_p_data.size() != vec_p_default_data.size() || vec_p_data.empty())
    {
        MY_LOG_E("vec_p_data size %ld, vec_p_default_data size %ld", vec_p_data.size(), vec_p_default_data.size());
        return -1;
    }

    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_param);
    if(p_signal != nullptr)
    {
        // dc_set_signal_val(p_data, data_type, p_signal->p_data);
        if(true == dc_signal_param_add_check(p_signal, desc, data_type, param, ctrl_type, vec_p_data, vec_p_default_data))
        {
            dc_set_param_cfg_change(true);
        }
        else
        {
            for(size_t i = 0; i < vec_p_data.size(); i++)
            {
                dc_set_signal_val(vec_p_data[i], data_type, p_signal->vec_p_data[i]);
            }
        }

        for(size_t i = 0; i < vec_p_data.size(); i++)
        {
            // delete p_signal->vec_p_data[i];
            dc_delete_signal_data(p_signal->vec_p_data[i], data_type);
            dc_delete_signal_data(p_signal->vec_p_default_data[i], data_type);
            // p_signal->vec_p_data[i] = vec_p_data[i];
        }
        
        p_signal->vec_p_data.clear();
        p_signal->vec_p_default_data.clear();

        for(size_t i = 0; i < vec_p_data.size(); i++)
        {
            p_signal->vec_p_data.push_back(vec_p_data[i]);
            p_signal->vec_p_default_data.push_back(vec_p_default_data[i]);
        }
        
        p_signal->change_cb_list.push_back(cb);
        return 0;
    }

    dc_set_param_cfg_change(true);

    stru_signal signal;
    signal.hash = hash;
    signal.saddr.assign(saddr);
    signal.desc.assign(desc);
    signal.data_type = data_type;
    signal.param = param;
    signal.ctrl_type = ctrl_type;

    dc_signal_add_to_map(signal, g_datacenter.signal_param);

    p_signal = dc_find_signal(hash, g_datacenter.signal_param);
    if(p_signal != nullptr )
    {
        for(size_t i = 0; i < vec_p_data.size(); i++)
        {
            p_signal->vec_p_data.push_back(vec_p_data[i]);
            p_signal->vec_p_default_data.push_back(vec_p_default_data[i]);
        }

        if(cb)
        {
            p_signal->change_cb_list.push_back(cb);
        }
        return 0;
    }

    return -1;
}

int dc_signal_param_link_with_callback(const std::string &saddr, uint8_t data_type, std::vector<void *> &vec_p_data, signal_change_cb cb)
{
    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_param);
    if(p_signal != nullptr)
    {
        if(p_signal->data_type != data_type)
        {
            LOG_E("saddr %s data type %s(%d) not match %s(%d)", 
                saddr.c_str(), dc_get_data_type_str_by_id(p_signal->data_type), p_signal->data_type, dc_get_data_type_str_by_id(data_type), data_type);
            return -1;
        }
        
        if(cb)
        {
            p_signal->change_cb_list.push_back(cb);
        }

        for(size_t i = 0; i < vec_p_data.size(); i++)
        {
            vec_p_data.push_back(p_signal->vec_p_data[i]);
        }
        return 0;
    }

    return -1;
}


int dc_signal_param_set_val(const std::string &saddr, SIGNAL_CTRL_STEP step, stru_signal_ctrl &ctrl, uint8_t setting_zone, void *p_data)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_param);
    if(p_signal != nullptr)
    {
        if(0 == dc_check_ctrl_valid(p_signal, step, ctrl, p_data))
        {
            if(0 != dc_check_val_valid(p_signal, setting_zone, p_data))
            {
                LOG_E("invalid value for saddr %s", saddr.c_str());
                return -1;
            }

            if(step == SIGNAL_CTRL_STEP::SELECT)
            {
                dc_set_signal_val(ctrl.p_data, ctrl.data_type, p_data);
                ctrl.step = SIGNAL_CTRL_STEP::SELECT;
            }
            else if(step == SIGNAL_CTRL_STEP::DIRECT)
            {
                dc_set_signal_val(p_signal->vec_p_data[setting_zone], ctrl.data_type, p_data);
                ctrl.step = SIGNAL_CTRL_STEP::READY;
            }
            else if(step == SIGNAL_CTRL_STEP::CANCEL)
            {
                ctrl.step = SIGNAL_CTRL_STEP::READY;
            }
            else
            {
                LOG_E("invalid ctrl step %d", step);
                return -1;
            }

            for(auto cb : p_signal->change_cb_list)
            {
                if(cb != nullptr)
                {
                    cb(saddr, step, ctrl.data_type, setting_zone, p_data);
                }
            }

            dc_set_param_cfg_change(true);

            return 0;
        }
        else
        {
            LOG_E("saddr %s ctrl invalid", saddr.c_str());
            ctrl.step = SIGNAL_CTRL_STEP::READY;
            return -1;
        }
    }

    LOG_E("saddr %s not found in param table", saddr.c_str());

    return -1;
}

int dc_signal_param_set_val_without_check(const std::string &saddr, uint8_t data_type, uint8_t setting_zone, void *p_data)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_param);
    if(p_signal != nullptr && setting_zone < p_signal->vec_p_data.size())   
    {
        if(p_signal->data_type != data_type)
        {
            LOG_E("saddr %s data type %s(%d) not match ctrl data type %s(%d)", 
                saddr.c_str(), dc_get_data_type_str_by_id(p_signal->data_type), p_signal->data_type, dc_get_data_type_str_by_id(data_type), data_type);
            return -1;
        }

        dc_set_signal_val(p_signal->vec_p_data[setting_zone], data_type, p_data);
        dc_set_param_cfg_change(true);
        return 0;
    }

    LOG_E("saddr %s not found in param table", saddr.c_str());
    return -1;
}

int dc_signal_yk(const std::string &saddr, const std::string &desc, uint8_t data_type, uint8_t ctrl_type, void *p_data, signal_change_cb cb)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    stru_signal signal;

    signal.hash = XXH3_128bits(saddr.c_str(), saddr.length());
    signal.saddr.assign(saddr);
    signal.desc.assign(desc);
    signal.data_type = data_type;
    signal.vec_p_data.push_back(p_data);

    dc_signal_add_to_map(signal, g_datacenter.signal_yk);

    stru_signal *p_signal = dc_find_signal(signal.hash, g_datacenter.signal_yk);
    if(p_signal != nullptr)
    {
        if(cb)
        {
            p_signal->change_cb_list.push_back(cb);
        }

        p_signal->ctrl_type = ctrl_type;

        return 0;
    }

    return -1;
}

int dc_signal_yk_link_with_callback(const std::string &saddr, uint8_t data_type, void **p_data, signal_change_cb cb)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_yk);
    if(p_signal != nullptr)
    {
        if(cb)
        {
            p_signal->change_cb_list.push_back(cb);
        }
        (*p_data) = p_signal->vec_p_data.empty() ? nullptr : p_signal->vec_p_data[0];
        return (*p_data) != nullptr ? 0 : -1;
    }

    return -1;
}

int dc_signal_yk_set_status(const std::string &saddr, SIGNAL_CTRL_STEP step, stru_signal_ctrl &ctrl, void *p_data)
{
    if(p_data == nullptr)
    {
        MY_LOG_E("p_data is nullptr");
        return -1;
    }

    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());
    stru_signal *p_signal = dc_find_signal(hash, g_datacenter.signal_yk);
    if(p_signal != nullptr)
    {
        if(0 == dc_check_ctrl_valid(p_signal, step, ctrl, p_data))
        {
            if(step == SIGNAL_CTRL_STEP::SELECT)
            {
                dc_set_signal_val(ctrl.p_data, ctrl.data_type, p_data);
                ctrl.step = SIGNAL_CTRL_STEP::SELECT;
            }
            else if(step == SIGNAL_CTRL_STEP::DIRECT)
            {
                dc_set_signal_val(p_signal->vec_p_data[0], ctrl.data_type, p_data);
                ctrl.step = SIGNAL_CTRL_STEP::READY;
            }
            else if(step == SIGNAL_CTRL_STEP::CANCEL)
            {
                ctrl.step = SIGNAL_CTRL_STEP::READY;
            }
            else
            {
                LOG_E("invalid ctrl step %d", step);
                return -1;
            }

            for(auto cb : p_signal->change_cb_list)
            {
                if(cb != nullptr)
                {
                    cb(saddr, step, ctrl.data_type, 0, p_data);
                }
            }

            return 0;
        }
        else
        {
            LOG_E("saddr %s ctrl invalid", saddr.c_str());
            ctrl.step = SIGNAL_CTRL_STEP::READY;
            return -1;
        }
    }

    LOG_E("saddr %s not found in yk table", saddr.c_str());

    return -1;
}


LOCAL stru_signal* dc_find_signal(const XXH128_hash_t &hash, stru_signal_map &dc_signal_map)
{
    std::lock_guard<std::mutex> lock(dc_signal_map.mtx);

    hash_signal_map &signal_map = dc_signal_map.map_signals;
    auto map_it = signal_map.find(hash);
    if(map_it != signal_map.end())
    {
        return &map_it->second;
    }
    else
    {
        return nullptr;
    }
}


LOCAL stru_signal* dc_find_out_signal(const std::string &saddr)
{
    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());

    return dc_find_signal(hash, g_datacenter.signal_out);
}

LOCAL stru_signal* dc_find_in_signal(const std::string &saddr)
{
    XXH128_hash_t hash = XXH3_128bits(saddr.c_str(), saddr.length());

    return dc_find_signal(hash, g_datacenter.signal_in);
}

LOCAL std::map<uint8_t, std::string> g_data_type_str = {
    {DATA_TYPE_B, "bool"},
    {DATA_TYPE_S8, "int8_t"},
    {DATA_TYPE_U8, "uint8_t"},
    {DATA_TYPE_S16, "int16_t"},
    {DATA_TYPE_U16, "uint16_t"},
    {DATA_TYPE_S32, "int32_t"},
    {DATA_TYPE_U32, "uint32_t"},
    {DATA_TYPE_L64, "int64_t"},
    {DATA_TYPE_UL64, "uint64_t"},
    {DATA_TYPE_F32, "float"},
    {DATA_TYPE_D64, "double"},
    {DATA_TYPE_IP, "ip"},
    {DATA_TYPE_MAC, "mac"},
    {DATA_TYPE_C8, "char8"},
    {DATA_TYPE_C32, "char32"},
    {DATA_TYPE_C64, "char64"},
    {DATA_TYPE_C128, "char128"},
    {DATA_TYPE_C1, "char1"},
    {DATA_TYPE_STR, "str"}
};

LOCAL std::map<uint8_t, uint8_t> g_data_type_len = {
    {DATA_TYPE_B, 1},
    {DATA_TYPE_S8, 1},
    {DATA_TYPE_U8, 1},
    {DATA_TYPE_S16, 2},
    {DATA_TYPE_U16, 2},
    {DATA_TYPE_S32, 4},
    {DATA_TYPE_U32, 4},
    {DATA_TYPE_L64, 8},
    {DATA_TYPE_UL64, 8},
    {DATA_TYPE_F32, 4},
    {DATA_TYPE_D64, 8},
    {DATA_TYPE_IP, 4},
    {DATA_TYPE_MAC, 8},
    {DATA_TYPE_C8, 8},
    {DATA_TYPE_C32, 32},
    {DATA_TYPE_C64, 64},
    {DATA_TYPE_C128, 128},
    {DATA_TYPE_C1, 1},
    {DATA_TYPE_STR, 128}
};

std::string dc_get_data_type_str_by_id(uint8_t data_type)
{
    auto it = g_data_type_str.find(data_type);
    if(it != g_data_type_str.end())
    {
        return it->second;
    }
    else
    {
        return "unknown";
    }
}

uint8_t dc_get_data_type_id_by_str(const std::string &data_type_str)
{
    for(const auto &p : g_data_type_str)
    {
        if(p.second.compare(data_type_str) == 0)
        {
            return p.first;
        }
    }
    return 0;
}

uint8_t dc_get_data_type_len(uint8_t data_type)
{
    auto it = g_data_type_len.find(data_type);
    if(it != g_data_type_len.end())
    {
        return it->second;
    }
    else
    {
        return 0;
    }
}

std::string dc_get_signal_val(void *p_data, uint8_t data_type)
{
    if(NULL == p_data)
    {
        return "";
    }

    std::string str = "";
    char temp[128] = {0};
    char *ptr = NULL;

    switch(data_type)
    {
        case DATA_TYPE_B:
            str = (*(int8_t *)p_data) > 0 ? "true" : "false";
            break;
        case DATA_TYPE_S8:
            str = std::to_string(*(int8_t *)p_data);
            break;
        case DATA_TYPE_U8:
            str = std::to_string(*(uint8_t*)p_data);
            break;
        case DATA_TYPE_S16:
            str = std::to_string(*(int16_t *)p_data);
            break;
        case DATA_TYPE_U16:
            str = std::to_string(*(uint16_t*)p_data);
            break;
        case DATA_TYPE_S32:
            str = std::to_string(*(int32_t *)p_data);   
            break;
        case DATA_TYPE_U32:
            str = std::to_string(*(uint32_t*)p_data);
            break;
        case DATA_TYPE_L64:
            str = std::to_string(*(int64_t *)p_data);
            break;
        case DATA_TYPE_UL64:
            str = std::to_string(*(uint64_t*)p_data);
            break;
        case DATA_TYPE_F32:
            str = std::to_string(*(float*)p_data);
            break;
        case DATA_TYPE_D64:
            str = std::to_string(*(double*)p_data);
            break;
        case DATA_TYPE_IP:
            ptr = (char *)p_data;
            snprintf(temp, 32, "%d.%d.%d.%d", (uint8_t)ptr[0], (uint8_t)ptr[1], (uint8_t)ptr[2], (uint8_t)ptr[3]);
            str = std::string(temp);
            break;
        case DATA_TYPE_MAC:
            ptr = (char *)p_data;
            snprintf(temp, 32, "%02X:%02X:%02X:%02X:%02X:%02X", (uint8_t)ptr[0], (uint8_t)ptr[1], (uint8_t)ptr[2], (uint8_t)ptr[3], (uint8_t)ptr[4], (uint8_t)ptr[5]);
            str = std::string(temp);
            break;
        case DATA_TYPE_C8:
        case DATA_TYPE_C32:
        case DATA_TYPE_C64:
        case DATA_TYPE_C128:
            // str = (char *)p_data;
            snprintf(temp, dc_get_data_type_len(data_type), "%s", (char *)p_data);
            str = std::string(temp);
            break;
        case DATA_TYPE_C1:
            str = std::string(1, *(char*)p_data);
            break;
        case DATA_TYPE_STR:
            snprintf(temp, dc_get_data_type_len(data_type), "%s", (char *)p_data);
            str = std::string(temp);
            break;
        default:
            break;
    }

    return str;
}

LOCAL int dc_set_signal_val(void *p_data, uint8_t data_type, void *set_data)
{
    if(NULL == p_data || NULL == set_data)
    {
        LOG_E("p_data or str is NULL, p_data: %p, set_data: %p", p_data, set_data);
        return -1;
    }

    switch(data_type)
    {
        case DATA_TYPE_B:
            *(int8_t *)p_data = ((char *)set_data)[0] & 0x01;
            break;
        case DATA_TYPE_S8:
            *(int8_t *)p_data = *(int8_t *)set_data;
            break;
        case DATA_TYPE_U8:
            *(uint8_t*)p_data = *(uint8_t*)set_data;
            break;
        case DATA_TYPE_S16:
            *(int16_t *)p_data = *(int16_t *)set_data;
            break;
        case DATA_TYPE_U16:
            *(uint16_t*)p_data = *(uint16_t*)set_data;
            break;
        case DATA_TYPE_S32:
            *(int32_t *)p_data = *(int32_t *)set_data;
            break;
        case DATA_TYPE_U32:
            *(uint32_t*)p_data = *(uint32_t*)set_data;
            break;
        case DATA_TYPE_L64:
            *(int64_t *)p_data = *(int64_t *)set_data;
            break;
        case DATA_TYPE_UL64:
            *(uint64_t*)p_data = *(uint64_t*)set_data;
            break;
        case DATA_TYPE_F32:
            *(float*)p_data = *(float*)set_data;
            break;
        case DATA_TYPE_D64:
            *(double*)p_data = *(double*)set_data;
            break;
        case DATA_TYPE_IP:
            strncpy((char *)p_data, (char *)set_data, 4);
            break;
        case DATA_TYPE_MAC:
            strncpy((char *)p_data, (char *)set_data, 6);
            break;
        case DATA_TYPE_C8:
        case DATA_TYPE_C32:
        case DATA_TYPE_C64:
        case DATA_TYPE_C128:
        case DATA_TYPE_STR:
            strncpy((char *)p_data, (char *)set_data, dc_get_data_type_len(data_type));
            break;
        case DATA_TYPE_C1:
            *(char*)p_data = *(char*)set_data;
            break;
        default:
            LOG_E("unknown data_type: %d", data_type);
            return -1;
    }
    
    return 0;
}

void dc_set_signal_val_from_str(void *p_data, uint8_t data_type, const std::string &str)
{
    if (str.empty() || p_data == nullptr) return;

    switch (data_type)
    {
        case DATA_TYPE_B:
            *(uint8_t *)p_data = (str == "true" || str == "1") ? 1 : 0;
            break;
        case DATA_TYPE_S8:
            *(int8_t *)p_data = (int8_t)std::stoi(str);
            break;
        case DATA_TYPE_U8:
            *(uint8_t *)p_data = (uint8_t)std::stoul(str);
            break;
        case DATA_TYPE_S16:
            *(int16_t *)p_data = (int16_t)std::stoi(str);
            break;
        case DATA_TYPE_U16:
            *(uint16_t *)p_data = (uint16_t)std::stoul(str);
            break;
        case DATA_TYPE_S32:
            *(int32_t *)p_data = std::stoi(str);
            break;
        case DATA_TYPE_U32:
            *(uint32_t *)p_data = std::stoul(str);
            break;
        case DATA_TYPE_L64:
            *(int64_t *)p_data = std::stoll(str);
            break;
        case DATA_TYPE_UL64:
            *(uint64_t *)p_data = std::stoull(str);
            break;
        case DATA_TYPE_F32:
            *(float *)p_data = std::stof(str);
            break;
        case DATA_TYPE_D64:
            *(double *)p_data = std::stod(str);
            break;
        case DATA_TYPE_IP:
            {
                int a, b, c, d;
                if (4 == sscanf(str.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d))
                {
                    ((uint8_t *)p_data)[0] = (uint8_t)a;
                    ((uint8_t *)p_data)[1] = (uint8_t)b;
                    ((uint8_t *)p_data)[2] = (uint8_t)c;
                    ((uint8_t *)p_data)[3] = (uint8_t)d;
                }
            }
            break;
        case DATA_TYPE_MAC:
            {
                int m[6];
                if (6 == sscanf(str.c_str(), "%02X:%02X:%02X:%02X:%02X:%02X", &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]))
                {
                    for (int i = 0; i < 6; i++)
                        ((uint8_t *)p_data)[i] = (uint8_t)m[i];
                }
            }
            break;
        case DATA_TYPE_C8:
        case DATA_TYPE_C32:
        case DATA_TYPE_C64:
        case DATA_TYPE_C128:
        case DATA_TYPE_STR:
            strncpy((char *)p_data, str.c_str(), dc_get_data_type_len(data_type));
            break;
        case DATA_TYPE_C1:
            *(char *)p_data = str[0];
            break;
        default:
            LOG_E("unknown data_type: %d", data_type);
            break;
    }
}

int dc_set_out_signal_val(const std::string &saddr, void *set_data)
{
    stru_signal *p_signal = dc_find_out_signal(saddr);
    if(p_signal != nullptr)
    {
        return dc_set_signal_val(p_signal->vec_p_data[0], p_signal->data_type, set_data);
    }

    return -1;
}

void *dc_create_data_ptr_by_type(uint8_t data_type)
{
    uint8_t *p_u8 = nullptr;    
    int8_t *p_s8 = nullptr;
    int16_t *p_s16 = nullptr;
    uint16_t *p_u16 = nullptr;
    int32_t *p_s32 = nullptr;
    uint32_t *p_u32 = nullptr;
    int64_t *p_l64 = nullptr;
    uint64_t *p_ul64 = nullptr;
    float *p_f = nullptr;
    double *p_d = nullptr;
    char *p_char = nullptr;
    
    switch(data_type)
    {
        case DATA_TYPE_B:
            p_u8 = new uint8_t;
            (*p_u8) = 0;
            return p_u8;
        case DATA_TYPE_S8:
            p_s8 = new int8_t;
            (*p_s8) = 0;
            return p_s8;
        case DATA_TYPE_U8:
            p_u8 = new uint8_t;
            (*p_u8) = 0;
            return p_u8;
        case DATA_TYPE_S16:
            p_s16 = new int16_t;
            (*p_s16) = 0;
            return p_s16;
        case DATA_TYPE_U16:
            p_u16 = new uint16_t;
            (*p_u16) = 0;
            return p_u16;
        case DATA_TYPE_S32:
            p_s32 = new int32_t;
            (*p_s32) = 0;
            return p_s32;
        case DATA_TYPE_U32:
            p_u32 = new uint32_t;
            (*p_u32) = 0;
            return p_u32;
        case DATA_TYPE_L64:
            p_l64 = new int64_t;
            (*p_l64) = 0;
            return p_l64;
        case DATA_TYPE_UL64:
            p_ul64 = new uint64_t;
            (*p_ul64) = 0;
            return p_ul64;
        case DATA_TYPE_F32:
            p_f = new float;
            (*p_f) = 0.0;
            return p_f;
        case DATA_TYPE_D64:
            p_d = new double;
            (*p_d) = 0.0;
            return p_d;
        case DATA_TYPE_IP:
            p_char = new char[4];
            memset(p_char, 0, 4);
            return p_char;
        case DATA_TYPE_MAC:
            p_char = new char[8];
            memset(p_char, 0, 8);
            return p_char;
        case DATA_TYPE_C8:
            p_char = new char[8];
            memset(p_char, 0, 8);
            return p_char;
        case DATA_TYPE_C32:
            p_char = new char[32];
            memset(p_char, 0, 32);
            return p_char;
        case DATA_TYPE_C64:
            p_char = new char[64];
            memset(p_char, 0, 64);
            return p_char;
        case DATA_TYPE_C128:
            p_char = new char[128];
            memset(p_char, 0, 128);
            return p_char;
        case DATA_TYPE_C1:
            p_char = new char[1];
            memset(p_char, 0, 1);
            return p_char;
        case DATA_TYPE_STR:
            p_char = new char[128];
            memset(p_char, 0, 128);
            return p_char;
        default:
            LOG_E("unknown data_type: %d", data_type);
            return nullptr;
    }
}

void dc_delete_signal_data(void *p_data, uint8_t data_type)
{
    if(nullptr == p_data) return;
    
    switch(data_type)
    {
        case DATA_TYPE_B:
            delete static_cast<uint8_t*>(p_data);
            break;
        case DATA_TYPE_S8:
            delete static_cast<int8_t*>(p_data);
            break;
        case DATA_TYPE_U8:
            delete static_cast<uint8_t*>(p_data);
            break;
        case DATA_TYPE_S16:
            delete static_cast<int16_t*>(p_data);
            break;
        case DATA_TYPE_U16:
            delete static_cast<uint16_t*>(p_data);
            break;
        case DATA_TYPE_S32:
            delete static_cast<int32_t*>(p_data);
            break;
        case DATA_TYPE_U32:
            delete static_cast<uint32_t*>(p_data);
            break;
        case DATA_TYPE_L64:
            delete static_cast<int64_t*>(p_data);
            break;
        case DATA_TYPE_UL64:
            delete static_cast<uint64_t*>(p_data);
            break;
        case DATA_TYPE_F32:
            delete static_cast<float*>(p_data);
            break;
        case DATA_TYPE_D64:
            delete static_cast<double*>(p_data);
            break;
        case DATA_TYPE_IP:
        case DATA_TYPE_MAC:
        case DATA_TYPE_C8:
        case DATA_TYPE_C32:
        case DATA_TYPE_C64:
        case DATA_TYPE_C128:
        case DATA_TYPE_C1:
        case DATA_TYPE_STR:
            delete [] static_cast<char*>(p_data);
            break;
        default:
            MY_LOG_E("Unknown data_type %d for delete", data_type);
            break;
    }
}


LOCAL stru_signal *dc_find_signal_by_id(uint32_t id, stru_signal_map &dc_signal_map)
{
    for(auto it = dc_signal_map.map_signals.begin(); it != dc_signal_map.map_signals.end(); ++it)
    {
        if(it->second.id == id)
        {
            return &it->second;
        }
    }
    return nullptr;
}


void dc_param_cfg_check()
{
    if (false == dc_get_param_cfg_change()) return;

    using namespace tinyxml2;

    XMLDocument doc;
    doc.InsertEndChild(doc.NewDeclaration());

    XMLElement *root = doc.NewElement("Root");
    doc.InsertEndChild(root);

    int ao_no = 0, param_no = 0;

    XMLElement *ao_elem = doc.NewElement("Ao");

    // for(auto it = g_datacenter.signal_ao.map_signals.begin(); it != g_datacenter.signal_ao.map_signals.end(); it++)
    for(uint32_t i = 0; i < g_datacenter.signal_ao.signal_id; i++)
    {
        stru_signal *p_ao = dc_find_signal_by_id(i, g_datacenter.signal_ao);
        if(nullptr == p_ao)
        {
            MY_LOG_E("ao id %d not found", i);
            continue;
        }

        XMLElement *sig_elem = doc.NewElement("Signal");
        sig_elem->SetAttribute("id", p_ao->id);
        sig_elem->SetAttribute("saddr", p_ao->saddr.c_str());
        sig_elem->SetAttribute("desc", p_ao->desc.c_str());
        sig_elem->SetAttribute("type", dc_get_data_type_str_by_id(p_ao->data_type).c_str());
        sig_elem->SetAttribute("min", p_ao->param.min);
        sig_elem->SetAttribute("max", p_ao->param.max);
        sig_elem->SetAttribute("step", p_ao->param.step);
        sig_elem->SetAttribute("unit", p_ao->param.unit.c_str());
        sig_elem->SetAttribute("value", dc_get_signal_val(p_ao->vec_p_data[0], p_ao->data_type).c_str());
        sig_elem->SetAttribute("default", dc_get_signal_val(p_ao->vec_p_default_data[0], p_ao->data_type).c_str());

        ao_elem->InsertEndChild(sig_elem);
    }

    XMLElement *param_elem = doc.NewElement("Param");

    // for(auto it = g_datacenter.signal_param.map_signals.begin(); it != g_datacenter.signal_param.map_signals.end(); it++)
    for(uint32_t i = 0; i < g_datacenter.signal_param.signal_id; i++)
    {
        stru_signal *p_param = dc_find_signal_by_id(i, g_datacenter.signal_param);
        if(nullptr == p_param)
        {
            MY_LOG_E("param id %d not found", i);
            continue;
        }

        XMLElement *sig_elem = doc.NewElement("Signal");
        sig_elem->SetAttribute("saddr", p_param->saddr.c_str());
        sig_elem->SetAttribute("desc", p_param->desc.c_str());
        sig_elem->SetAttribute("type", dc_get_data_type_str_by_id(p_param->data_type).c_str());
        sig_elem->SetAttribute("min", p_param->param.min);
        sig_elem->SetAttribute("max", p_param->param.max);
        sig_elem->SetAttribute("step", p_param->param.step);
        sig_elem->SetAttribute("unit", p_param->param.unit.c_str());
        sig_elem->SetAttribute("num", p_param->vec_p_data.size());

        
        for(int i = 0; i < p_param->vec_p_data.size(); i++)
        {
            XMLElement *item_elem = doc.NewElement("Item");
            item_elem->SetAttribute("index", i+1);
            item_elem->SetAttribute("val", dc_get_signal_val(p_param->vec_p_data[i], p_param->data_type).c_str());
            item_elem->SetAttribute("default", dc_get_signal_val(p_param->vec_p_default_data[i], p_param->data_type).c_str());

            sig_elem->InsertEndChild(item_elem);
        }

        param_elem->InsertEndChild(sig_elem);
    }

    root->InsertEndChild(ao_elem);
    root->InsertEndChild(param_elem);

    if (XML_SUCCESS == doc.SaveFile(DC_PARAM_PATH))
    {
        dc_set_param_cfg_change(false);
        MY_LOG_I("param config file saved to %s", DC_PARAM_PATH);
    }
    else
    {
        MY_LOG_E("failed to save param config file to %s", DC_PARAM_PATH);
    }
}


LOCAL void dc_show_signals(stru_signal_map &dc_signal_map)
{
    using namespace std;

    hash_signal_map &signal_map = dc_signal_map.map_signals;

    // 定义列宽（单位：字符，考虑中英文混合）
    const int COL_ID = 12;
    const int COL_SADDR = 48;
    const int COL_DESC = 64;
    const int COL_TYPE = 12;
    const int COL_VAL = 32;
    const int COL_LINK = 64;

    // 打印表头（严格对齐，使用left对齐）
    cout << "\t"
         << left 
         << setw(COL_ID) << "id"
         << setw(COL_SADDR) << "saddr"
         << setw(COL_DESC) << "desc"
         << setw(COL_TYPE) << "data_type"
         << setw(COL_VAL) << "val"
         << setw(COL_LINK) << "link_saddrs"
         << endl;

    // 打印分隔线（总长度 = 各列宽度之和）
    int total_width = COL_SADDR + COL_DESC + COL_TYPE + COL_VAL + COL_LINK;
    cout << "\t" << string(total_width, '-') << endl;

    // 遍历信号表
    // for(auto it = signal_map.begin(); it != signal_map.end(); ++it)
    for(uint32_t i = 0; i < dc_signal_map.signal_id; i++)
    {
        // const stru_signal *p_signal = &it->second;
        stru_signal *p_signal = dc_find_signal_by_id(i, dc_signal_map);
        if(nullptr == p_signal)
        {
            MY_LOG_E("signal id %d not found", i);
            continue;
        }

        // 1. 先把所有列的内容拼接成字符串，再一次性输出，避免中间打断setw对齐
        string link_str;
        bool first = true;
        for (const auto &link : p_signal->link_saddrs)
        {
            if (!first) link_str += ", ";
            link_str += link;
            first = false;
        }

        // 2. 输出所有列，严格使用setw控制宽度
        cout << "\t"
             << left
             << setw(COL_ID) << p_signal->id
             << setw(COL_SADDR) << p_signal->saddr
             << setw(COL_DESC) << p_signal->desc
             << setw(COL_TYPE) << dc_get_data_type_str_by_id(p_signal->data_type)
             << setw(COL_VAL) << dc_get_signal_val(p_signal->vec_p_data[0], p_signal->data_type)
             << setw(COL_LINK) << link_str
             << endl;
    }

    cout << endl;

    cout << "signal_id: " << dc_signal_map.signal_id << endl;
    cout << "signal count: " << signal_map.size() << endl;
    cout << "conflict count: " << dc_signal_map.hash_conflicts.size() << endl;
    
    if(dc_signal_map.hash_conflicts.empty())
    {
        cout << "no conflict" << endl;
    }
    else
    {
        cout << "conflicts: count " << dc_signal_map.hash_conflicts.size() << endl;
        for(auto it = dc_signal_map.hash_conflicts.begin(); it != dc_signal_map.hash_conflicts.end(); ++it)
        {
            const auto &hash = it->first;
            const auto &saddrs = it->second;
            cout << "  hash: " << hash.low64 << "-" << hash.high64 << " num: " << saddrs.size() << endl;
            for(auto &saddr : saddrs)
            {
                cout << "    " << saddr.c_str() << endl;
            }
        }
    }

    cout << endl;
}

LOCAL void dc_show_out_signals()
{
    using namespace std;
    cout << "out signals: " << endl;
    dc_show_signals(g_datacenter.signal_out);
}

LOCAL void dc_show_in_signals()
{
    using namespace std;

    cout << "in signals: " << endl;
    dc_show_signals(g_datacenter.signal_in);
}

LOCAL void dc_show_yk_signals()
{
    using namespace std;

    cout << "yk signals: " << endl;
    dc_show_signals(g_datacenter.signal_yk);
}

LOCAL void dc_show_ao_signals()
{
    using namespace std;

    cout << "ao signals: " << endl;
    dc_show_signals(g_datacenter.signal_ao);
}



LOCAL void dc_show_param_signals()
{
    using namespace std;

    cout << "param signals: " << endl;
    dc_show_signals(g_datacenter.signal_param);
}



LOCAL void cmd_dc(int argc, char *argv[])
{
    const char *str = "\
        datacenter out ----------> 查看out信号信息\n\
        datacenter in ----------> 查看in信号信息\n\
        datacenter yk ----------> 查看yk信号信息\n\
        datacenter ao ----------> 查看ao信号信息\n\
        datacenter param ----------> 查看param信号信息\n\
        datacenter all ----------> 查看所有信号信息\n";


    if(argc < 2)
    {
        printf("%s", str);
        return;
    }

    if(0 == strcmp(argv[1], "out"))
    {
        dc_show_out_signals();
    }
    else if(0 == strcmp(argv[1], "in"))
    {
        dc_show_in_signals();
    }
    else if(0 == strcmp(argv[1], "yk"))
    {
        dc_show_yk_signals();
    }
    else if(0 == strcmp(argv[1], "ao"))
    {
        dc_show_ao_signals();
    }
    else if(0 == strcmp(argv[1], "param"))
    {
        dc_show_param_signals();
    }
    else if(0 == strcmp(argv[1], "all"))
    {
        dc_show_out_signals();
        dc_show_in_signals();
        dc_show_yk_signals();
        dc_show_ao_signals();
        dc_show_param_signals();
    }
}

CMD_REGISTER("datacenter", cmd_dc, "数据中心的控制命令");


