#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdbool.h>
#include "sqlite3.h"
#include "sqlite_backend.h"
#include "ua-server.h"
#include "../modbus_tcp_ser.h"
#include "open62541.h"
#include "../tag.h"
#include "../proto_forward.h"
#include "signal.h"
#include <sys/time.h>
#include <sys/timerfd.h>
#include <linux/rtc.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include "../local_data_lasting.h"

#include <sys/types.h>
#include "../cmdclient.h"
#include "../log_module/log_module.h"
#include "ua_com.h"
#include "../multilanguage.h"
#include "../local_data_lasting.h"
#include "../opt_log.h"
#include "collector-api.h"
#include "../ems/jsonct.h"
static const char *channelStateTexts[9] = {
    "Fresh", "HELSent", "HELReceived", "ACKSent",
    "AckReceived", "OPNSent", "Open", "Closing", "Closed"};
static const char *sessionStateTexts[6] =
    {"Closed", "CreateRequested", "Created",
     "ActivateRequested", "Activated", "Closing"};


static UA_StatusCode
total_read_cb(UA_Server *server,
                const UA_NodeId *sessionId, void *sessionContext,
                const UA_NodeId *nodeId, void *nodeContext,
                UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
                UA_DataValue *dataValue);
static UA_StatusCode
total_write_cb(UA_Server *server,
               const UA_NodeId *sessionId, void *sessionContext,
               const UA_NodeId *nodeId, void *nodeContext,
               const UA_NumericRange *range, const UA_DataValue *data);
     
//---------------------------------------------------------------------------------------//
static int createUA_Argment_Scalar(UA_Argument *arg, char *name,int type, char *disp_name)
{
    UA_Argument *inputArg = arg;
    UA_Argument_init(inputArg);
    inputArg->description = UA_LOCALIZEDTEXT("zh_CN", disp_name);
    inputArg->name = UA_STRING(name);
    inputArg->dataType = UA_TYPES[type].typeId;
    inputArg->valueRank = UA_VALUERANK_SCALAR;

    return 0;
}

int creat_a_base_file(UA_SERVER_PARAM *ua_param,char *nodeId,char *name,UA_NodeId *node_id)
{
    if(ua_param == NULL) return -1;
    UA_Server *server = ua_param->ua_server;
    *node_id = UA_NODEID_STRING(1, name);
    UA_ObjectAttributes object_attr1 = UA_ObjectAttributes_default;
    //object_attr1.description = UA_LOCALIZEDTEXT("zh_CN", name);
    object_attr1.displayName = UA_LOCALIZEDTEXT("zh_CN", nodeId);
    UA_Server_addObjectNode(server, *node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, name),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr1, NULL, NULL);
    return 0;
}

int creat_a_base_string(UA_SERVER_PARAM *ua_param,char *nodeId,char *name,UA_NodeId *node_id,UA_NodeId parent_id,char *defaultString)
{
    if(ua_param == NULL) return -1;
    UA_Server *server = ua_param->ua_server;
    if (node_id != NULL)
    {
        UA_NodeId_init(node_id);
    }
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    //attr.description = UA_LOCALIZEDTEXT("zh_CN", name);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", name);
    attr.accessLevel = (ua_param->user_data->open_history) ? UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_HISTORYREAD : UA_ACCESSLEVELMASK_READ;


    UA_String ua_result = UA_String_fromChars(defaultString);
    UA_Variant_setScalar(&attr.value, &ua_result, &UA_TYPES[UA_TYPES_STRING]);   

    UA_StatusCode retval = UA_Server_addVariableNode(server, UA_NODEID_STRING(1, nodeId),
                              parent_id,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, name),
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr,/*timeDataSource,*/ NULL, node_id);
    if (retval != UA_STATUSCODE_GOOD) {
        proto_syslog(LOG_WARNING,
                "tag:%s  UA_Server_addVariableNode %s", name,UA_StatusCode_name(retval));
    }
    UA_String_clear(&ua_result);
    return 0;
}

int creat_a_rw_string(UA_SERVER_PARAM *ua_param,char *displayname,char *name,UA_NodeId *node_id,UA_NodeId parent_id,char *defaultString,total_cb_t *total_cb)
{
    if(ua_param == NULL) return -1;
    UA_Server *server = ua_param->ua_server;
    UA_NodeId n_id;
    UA_NodeId *p_id = node_id;
    if (node_id == NULL)
    {
        node_id = &n_id;
    }
    UA_NodeId_init(node_id);
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    //attr.description = UA_LOCALIZEDTEXT("zh_CN", name);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", displayname);
    attr.accessLevel = UA_ACCESSLEVELMASK_READ|UA_ACCESSLEVELMASK_WRITE;


    UA_String ua_result = UA_String_fromChars(defaultString);
    UA_Variant_setScalar(&attr.value, &ua_result, &UA_TYPES[UA_TYPES_STRING]);   

    UA_StatusCode retval = UA_Server_addVariableNode(server, UA_NODEID_STRING(1, name),
                              parent_id,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, name),
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr,/*timeDataSource,*/ total_cb, node_id);
    if (retval != UA_STATUSCODE_GOOD) {                        
        proto_syslog(LOG_WARNING,
                "tag:%s  UA_Server_addVariableNode %s", name,UA_StatusCode_name(retval));
    }

    if((total_cb->onRead!=NULL)||total_cb->onWrite!=NULL)
    {
        total_cb->data_type = 2;
        UA_DataSource varDataSource;
        varDataSource.read = total_read_cb;
        varDataSource.write = total_write_cb;
        UA_StatusCode retval = UA_Server_setVariableNode_dataSource(server, *node_id, varDataSource);
        if (retval != UA_STATUSCODE_GOOD) {                        
        proto_syslog(LOG_WARNING,
                " setVariableNode %s[%s]", name, UA_StatusCode_name(retval));
                return -1;
        }
    }
    if (p_id == NULL)
    {
        UA_NodeId_clear(&n_id);
    }
    UA_String_clear(&ua_result);
    return 0;
}
void creat_table(sqlite3 *db,char *node_name);
int creat_a_base_double(UA_SERVER_PARAM *ua_param,char *nodeId,char *name,UA_NodeId *node_id,UA_NodeId parent_id,char *defaultString)
{
    if(ua_param == NULL) return -1;
    UA_Server *server = ua_param->ua_server;
    UA_NodeId_init(node_id);
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    //attr.description = UA_LOCALIZEDTEXT("zh_CN", name);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", name);
    attr.accessLevel = (ua_param->user_data->open_history) ? UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_HISTORYREAD : UA_ACCESSLEVELMASK_READ;
    attr.historizing = true;
    UA_Double ua_result = 0.0;
    // UA_String ua_result = UA_String_fromChars(defaultString);
    UA_Variant_setScalar(&attr.value, &ua_result, &UA_TYPES[UA_TYPES_DOUBLE]);   

    UA_StatusCode retval = UA_Server_addVariableNode(server, UA_NODEID_STRING(1, nodeId),
                              parent_id,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, name),
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr,/*timeDataSource,*/ NULL, node_id);
    if (retval != UA_STATUSCODE_GOOD) {                        
        proto_syslog(LOG_WARNING,
                "tag:%s  UA_Server_addVariableNode %s", name,UA_StatusCode_name(retval));
    }

    // sqlite3 *db;
    // int ret = sqlite3_open(DBFILE, &db);
    // if (ret < 0)
    //     proto_syslog(LOG_ERR, "OPEN " DBFILE " ERROR!");
    // else
    //     proto_syslog(LOG_NOTICE, "OPEN " DBFILE " SUCCESS!");
    // retval = ua_param->gathering.registerNodeId(server, ua_param->gathering.context, node_id, ua_param->setting);
    //     if(retval != UA_STATUSCODE_GOOD) proto_syslog(LOG_WARNING, "registerNodeId %s", UA_StatusCode_name(retval));
    //     creat_table(db,nodeId);
    // sqlite3_close(db);
    // UA_String_clear(&ua_result);
    return 0;
}
//---------------------------------------------------------------------------------------//


//------------------------------------下面主要是数采部分用的----------------------------------------------------//
/*------------------------------------------sqlite3-------------------------------------------------------*/

int sqlite3_exec_command(sqlite3 *db,char* sql)
{
    int nrow=0;

	int ncolumn = 0;
	char *zErrMsg =NULL;
	char **azResult=NULL; 
	sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
	if(zErrMsg )
	{
		printf("getHistoryData_backend_sqlite error:%s\n",zErrMsg);
		sqlite3_free_table(azResult);
		sqlite3_free(zErrMsg);
		return -1;
	}

    sqlite3_free_table(azResult);
	sqlite3_free(zErrMsg);
    return 0;
}

long long sqlite3_get_database_data_num(sqlite3 *db,char* table_name)
{
    int nrow=0;

	int ncolumn = 0;
	char *zErrMsg =NULL;
	char **azResult=NULL; //二维数组存放结果
	char sql[512] = {0};

    sprintf(sql,"SELECT count(*) FROM %s",table_name);
	sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
	if(zErrMsg )
	{
		printf("getHistoryData_backend_sqlite error:%s\n",zErrMsg);
		sqlite3_free_table(azResult);
		sqlite3_free(zErrMsg);
		return -1;
	}
    long long  count = (int32_t)atoll(azResult[1]);
    sqlite3_free_table(azResult);
	sqlite3_free(zErrMsg);
    return count;
}

int sqlite3_delete_head_n_data(sqlite3 *db,char* table_name,long long num)
{
    int nrow=0;
 
	int ncolumn = 0;
	char *zErrMsg =NULL;
	char **azResult=NULL; //二维数组存放结果
	char sql[512] = {0};

    sprintf(sql,"DELETE FROM %s WHERE time IN (SELECT time  FROM %s ORDER BY time ASC LIMIT %lld)",table_name,table_name,num);
    //printf("sql:%s\n",sql);
	sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
	if(zErrMsg )
	{
		printf("getHistoryData_backend_sqlite error:%s\n",zErrMsg);
		sqlite3_free_table(azResult);
		sqlite3_free(zErrMsg);
		return -1;
	}
    sqlite3_free_table(azResult);
	sqlite3_free(zErrMsg);
    return 0;
}
int sqlite3_get_table_list_and_del(sqlite3 *db,int percent)
{
    int nrow=0;
	int ncolumn = 0;
	char *zErrMsg =NULL;
	char **azResult=NULL; //二维数组存放结果
	char sql[512] = {0};
    proto_syslog(LOG_NOTICE,"------------------------------------------\n");
    sprintf(sql,"SELECT name FROM sqlite_master WHERE type='table';");
	sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
	if(zErrMsg )
	{
		proto_syslog(LOG_NOTICE,"getHistoryData_backend_sqlite error:%s\n",zErrMsg);
		sqlite3_free_table(azResult);
		sqlite3_free(zErrMsg);
		return -1;
	}
    for(int i=1;i<nrow;i++) //去除第一个name
    {
        proto_syslog(LOG_NOTICE, "del table:%d [%s]",i,azResult[i]);
        long long num = sqlite3_get_database_data_num(db,azResult[i]);
        proto_syslog(LOG_NOTICE,"**************************\n");
        proto_syslog(LOG_NOTICE,"before delete num:%lld\n",num);
        long long del_num = num*percent/100;
        sqlite3_delete_head_n_data(db,azResult[i],del_num);
        num = sqlite3_get_database_data_num(db,azResult[i]);
        proto_syslog(LOG_NOTICE,"after delete num:%lld\n",num);
        proto_syslog(LOG_NOTICE,"**************************\n");
    }
    //sqlite3_exec_command(db,"VACUUM;"); 
    proto_syslog(LOG_NOTICE,"------------------------------------------\n");
    sqlite3_free_table(azResult);
	sqlite3_free(zErrMsg); 

    return 0;
}

int sqlite3_get_table_list_and_del_by_time(sqlite3 *db, time_t end_time)
{
    int nrow = 0;
    int ncolumn = 0;
    char *zErrMsg = NULL;
    char **azResult = NULL; // 二维数组存放结果
    char sql[512] = {0};
    int ret = 0;
    sprintf(sql, "SELECT name FROM sqlite_master WHERE type='table';");
    sqlite3_get_table(db, sql, &azResult, &nrow, &ncolumn, &zErrMsg);
    if (zErrMsg)
    {
        proto_syslog(LOG_NOTICE, "getHistoryData_backend_sqlite error:%s\n", zErrMsg);
        sqlite3_free_table(azResult);
        sqlite3_free(zErrMsg);
        return -1;
    }
    //如果点位比较多，下面这个循环需要执行很久
    for (int i = 1; i < nrow; i++)
    {
        sprintf(sql, "delete from %s where time < %"PRId64";", azResult[i], ENCODE_TIME_UA(end_time));
        proto_syslog(LOG_NOTICE, "sqlite3_exec:%s", sql);
        if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, &zErrMsg))
        {
            proto_syslog(LOG_ERR, "sqlite3_exec error:%s, errMsg:%s", sql, zErrMsg);
            sqlite3_free(zErrMsg);
            ret = -2;
        }
        sleep(10);
    }
    // sqlite3_exec_command(db,"VACUUM;");
    sqlite3_free_table(azResult);

    return ret;
}

long long sqlite3_get_database_size(sqlite3 *db)
{
	int nrow=0;

	int ncolumn = 0;
	char *zErrMsg =NULL;
	char **azResult=NULL; //二维数组存放结果
	char sql[512] = {0};

    sprintf(sql,"PRAGMA page_count;");
	sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
	if(zErrMsg )
	{
		printf("getHistoryData_backend_sqlite error:%s\n",zErrMsg);
		sqlite3_free_table(azResult);
		sqlite3_free(zErrMsg);
		return -1;
	}
    int page_count = (int32_t)atol(azResult[1]);
    sqlite3_free_table(azResult);
	sqlite3_free(zErrMsg);


    sprintf(sql,"PRAGMA page_size;");
	sqlite3_get_table( db , sql , &azResult , &nrow , &ncolumn , &zErrMsg );
	if(zErrMsg )
	{
		printf("getHistoryData_backend_sqlite error:%s\n",zErrMsg);
		sqlite3_free_table(azResult);
		sqlite3_free(zErrMsg);
		return -1;
	}
    int page_size = (int32_t)atol(azResult[1]);

	sqlite3_free_table(azResult);
	sqlite3_free(zErrMsg);
    return page_count*page_size/1024;
}


long long ua_get_history_database_size(UA_SERVER_PARAM *ua_param) //返回单位kb
{
    if (!ua_param->setting.historizingBackend.context) {
        proto_syslog(LOG_ERR, "history backend context is NULL, skip get_database_size");
        return 0;
    }
    return sqlite3_get_database_size((sqlite3*)ua_param->setting.historizingBackend.context);
}
long long ua_get_history_num(UA_SERVER_PARAM *ua_param,char* table_name)
{
    if (!ua_param->setting.historizingBackend.context) {
        proto_syslog(LOG_ERR, "history backend context is NULL, skip get_history_num");
        return 0;
    }
    return sqlite3_get_database_data_num((sqlite3*)ua_param->setting.historizingBackend.context,table_name);
}

long long ua_del_history_num(UA_SERVER_PARAM *ua_param,char* table_name,long long num)
{
    if (!ua_param->setting.historizingBackend.context) {
        proto_syslog(LOG_ERR, "history backend context is NULL, skip del_history_num");
        return 0;
    }
    return  sqlite3_delete_head_n_data((sqlite3*)ua_param->setting.historizingBackend.context,table_name, num);
}

void ua_del_history_percent(UA_SERVER_PARAM *ua_param,int percent)
{
    if (!ua_param->setting.historizingBackend.context) {
        proto_syslog(LOG_ERR, "history backend context is NULL, skip del_history_percent");
        return;
    }
    sqlite3_get_table_list_and_del((sqlite3*)ua_param->setting.historizingBackend.context,percent);
}

static void str_replace_chr(char *line)
{
    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] = '_';
        }
    }
}

void creat_table(sqlite3 *db,char *node_name)
{
    if (db == NULL)
    {
        return;
    }
		/* 创建表 */
	char *zErrMsg =NULL;	
	char sql[256] = {0};
    char name[DEV_NAME_LEN + TAG_NAME_LEN] = {0};
    strncpy(name, node_name, sizeof(name) - 1);
	str_replace_chr(name);
	sprintf(sql," CREATE TABLE %s (\
		ID INTEDER PRIMARY KEY,\
		datatype INTEGER,\
		result VARCHAR(64),\
		time INTEGER\
	);",name);
    proto_syslog(LOG_DEBUG, "Creat Table :%s", name);
    sqlite3_exec(db,sql,NULL,NULL,&zErrMsg);
    if(zErrMsg)
    {
        proto_syslog(LOG_DEBUG, "Creat Table:%s, errMsg:%s", name, zErrMsg);
        sqlite3_free(zErrMsg);
        return;
    }
	sqlite3_free(zErrMsg);
	/* 创建索引 */
    // sprintf(sql,"CREATE INDEX variable_index ON %s (time);",name);
    // sqlite3_exec(db,sql,NULL,NULL,&zErrMsg);
    // if(zErrMsg)
    // {
    //     proto_syslog(LOG_ERR, "Creat Table:%s, errMsg:%s", name, zErrMsg);
    //     sqlite3_free(zErrMsg);
    //     return;
    // }
	// sqlite3_free(zErrMsg);
}


/*--------------------------------------------method-------------------------------------------------------*/

static UA_StatusCode file_download(UA_Server *server,
                         const UA_NodeId *sessionId, void *sessionHandle,
                         const UA_NodeId *methodId, void *methodContext,
                         const UA_NodeId *objectId, void *objectContext,
                         size_t inputSize, const UA_Variant *input_ptr,
                         size_t outputSize, UA_Variant *output_ptr) {
    UA_String *Name = (UA_String*)input_ptr[0].data;                        
    UA_String *Path = (UA_String*)input_ptr[1].data;
    UA_ByteString *File = (UA_ByteString*)input_ptr[2].data;

    proto_syslog(LOG_NOTICE,"receive file length:%lu",(long unsigned)File->length);
    UA_Int16 code = 0;
    UA_String message;
    char name[128*2]={0};
    char path[128*2]="/app";
    char buff[256*2]={0};
    if(!Name->data || Name->length <= 0)
    {
        message = UA_STRING_ALLOC("Error:File name missing");
    }
    else
    {
        strncpy(name,(char*)Name->data,Name->length);
        if(Path->data && Path->length > 0) strncpy(path,(char*)Path->data,Path->length);
        snprintf(buff,sizeof(buff),"%s/%s",path,name);
        proto_syslog(LOG_NOTICE,"Create file:%s", buff);
        server_add_new_log(LOG_CODE_OPC_FILE,"EMS",LOG_MODE_TRIG,2,buff);
        int ret = write_file_data(buff, (char*)File->data, File->length);
        if(ret != 0)
        {
            code = errno;      
            char * str = strerror(errno);
            UA_ByteString_allocBuffer(&message,strlen(str));
            strcpy((char*)message.data,str);
            proto_syslog(LOG_WARNING,"Failed to write file:%s",buff);
        }
        else{
            message = UA_STRING_ALLOC(_("Download completed"));
        }
    }
    UA_String_clear(Name);
    UA_String_clear(Path);
    UA_String_clear(File);


    UA_Variant_setScalarCopy(&output_ptr[0], &code, &UA_TYPES[UA_TYPES_INT16]);
    UA_Variant_setScalarCopy(&output_ptr[1], &message, &UA_TYPES[UA_TYPES_STRING]);
    UA_String_clear(&message);
    return UA_STATUSCODE_GOOD;
}

int addDownloadMethod(UA_SERVER_PARAM *_var,UA_NodeId parent_noteid) {

    UA_SERVER_PARAM *ua_param = _var;
    if(ua_param == NULL) return -1;
    UA_Server *server = ua_param->ua_server;
    UA_Argument inputArgs[3];
    createUA_Argment_Scalar(&inputArgs[0], "Name", UA_TYPES_STRING, _("File name"));
    createUA_Argment_Scalar(&inputArgs[1], "Path", UA_TYPES_STRING, _("File path[default /app/]"));
    createUA_Argment_Scalar(&inputArgs[2], "File", UA_TYPES_BYTESTRING, _("File content"));

    UA_Argument outputArgs[2];
    createUA_Argment_Scalar(&outputArgs[0], "ErrorCode", UA_TYPES_INT16, _("Upgrade response code"));
    createUA_Argment_Scalar(&outputArgs[1], "ErrorStr", UA_TYPES_STRING, _("Upgrade response code characters"));

    UA_MethodAttributes Attr = UA_MethodAttributes_default;
    char *node_name = "Download";
    Attr.description = UA_LOCALIZEDTEXT("zh_CN",node_name);
    Attr.displayName              = UA_LOCALIZEDTEXT("zh_CN", _("Download file"));
    Attr.executable = true;
    Attr.userExecutable = true;
    UA_StatusCode retval = UA_Server_addMethodNode(server, UA_NODEID_STRING(1,node_name),
                            parent_noteid,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                            UA_QUALIFIEDNAME(1, node_name),
                            Attr, file_download,
                            3, inputArgs, 2, outputArgs, NULL, NULL);
    return retval;
}


static UA_StatusCode file_upload(UA_Server *server,
                         const UA_NodeId *sessionId, void *sessionHandle,
                         const UA_NodeId *methodId, void *methodContext,
                         const UA_NodeId *objectId, void *objectContext,
                         size_t inputSize, const UA_Variant *input_ptr,
                         size_t outputSize, UA_Variant *output_ptr) {
    UA_String *Name = (UA_String*)input_ptr[0].data;                        
    UA_String *Path = (UA_String*)input_ptr[1].data;
    UA_Int16 code = 0;
    UA_String message;
    UA_ByteString file_content = {0};
    char name[128*2]={0};
    char path[128*2]="/app";
    char buff[256*2]={0};
    if(!Name->data || Name->length <= 0)
    {
        message = UA_STRING_ALLOC("Error:File name missing");
    }
    else
    {
        strncpy(name,(char*)Name->data,Name->length);
        if(Path->data && Path->length > 0) strncpy(path,(char*)Path->data,Path->length);
        snprintf(buff,sizeof(buff),"%s/%s",path,name);
        proto_syslog(LOG_NOTICE,"Read file:%s", buff);
        server_add_new_log(LOG_CODE_OPC_FILE,"EMS",LOG_MODE_TRIG,1,buff);
        int len = 0;
        file_content.data = (void*)read_file_data_and_lens(buff, &len);
        if(file_content.data == NULL)
        {
            code = errno;      
            char * str = strerror(errno);
            UA_ByteString_allocBuffer(&message,strlen(str));
            strcpy((char*)message.data,str);
            proto_syslog(LOG_WARNING,"Failed to write file:%s",buff);
        }
        else{
            file_content.length = len;
            proto_syslog(LOG_INFO,"copy file:%s to the buff",buff);
            message = UA_STRING_ALLOC(_("Upload completed"));
        }
    }
    UA_String_clear(Name);
    UA_String_clear(Path);


    UA_Variant_setScalarCopy(&output_ptr[0], &code, &UA_TYPES[UA_TYPES_INT16]);
    UA_Variant_setScalarCopy(&output_ptr[1], &message, &UA_TYPES[UA_TYPES_STRING]);
    UA_Variant_setScalarCopy(&output_ptr[2], &file_content, &UA_TYPES[UA_TYPES_BYTESTRING]);
    UA_String_clear(&message);
    UA_String_clear(&file_content);
    free(file_content.data);
    return UA_STATUSCODE_GOOD;
}

int addUploadMethod(UA_SERVER_PARAM *_var,UA_NodeId parent_noteid) {

    UA_SERVER_PARAM *ua_param = _var;
    if(ua_param == NULL) return -1;
    UA_Server *server = ua_param->ua_server;

    UA_Argument inputArgs[2];
    createUA_Argment_Scalar(&inputArgs[0], "Name", UA_TYPES_STRING, _("File Name"));
    createUA_Argment_Scalar(&inputArgs[1], "Path", UA_TYPES_STRING, _("File path[default /app/]"));

    UA_Argument outputArgs[3];
    createUA_Argment_Scalar(&outputArgs[0], "ErrorCode", UA_TYPES_INT16, _("Upgrade response code"));
    createUA_Argment_Scalar(&outputArgs[1], "ErrorStr", UA_TYPES_STRING, _("Upgrade response code characters"));
    createUA_Argment_Scalar(&outputArgs[2], "Content", UA_TYPES_BYTESTRING, _("File content"));

    UA_MethodAttributes Attr = UA_MethodAttributes_default;
    char *node_name = "Upload";
    Attr.description = UA_LOCALIZEDTEXT("zh_CN",node_name);
    Attr.displayName              = UA_LOCALIZEDTEXT("zh_CN", _("Upload files"));
    Attr.executable = true;
    Attr.userExecutable = true;
    UA_StatusCode retval = UA_Server_addMethodNode(server, UA_NODEID_STRING(1,node_name),
                            parent_noteid,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                            UA_QUALIFIEDNAME(1, node_name),
                            Attr, file_upload,
                            2, inputArgs, 3, outputArgs, NULL, NULL);
    return retval;
}


// int addMethodToNodeNoInput(UA_SERVER_PARAM *_var,UA_NodeId parent_noteid,char *functionName,ua_cb method) {

//     UA_SERVER_PARAM *ua_param = _var;
//     if(ua_param == NULL) return -1;
//     UA_Server *server = ua_param->ua_server;


//     UA_Argument outputArgument;
//     UA_Argument_init(&outputArgument);
//     outputArgument.description = UA_LOCALIZEDTEXT("zh_CN", "Output String");
//     outputArgument.name = UA_STRING("Output");
//     outputArgument.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
//     outputArgument.valueRank = UA_VALUERANK_SCALAR;

//     UA_MethodAttributes Attr = UA_MethodAttributes_default;
//     Attr.description = UA_LOCALIZEDTEXT("zh_CN",functionName);
//     Attr.displayName = UA_LOCALIZEDTEXT("zh_CN",functionName);
//     Attr.executable = true;
//     Attr.userExecutable = true;
//     UA_StatusCode retval = UA_Server_addMethodNode(server, UA_NODEID_STRING(1,functionName),
//                             parent_noteid,
//                             UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
//                             UA_QUALIFIEDNAME(1, functionName),
//                             Attr, MethodCallback,
//                             0, NULL, 1, &outputArgument, method, NULL);
//     if (retval != UA_STATUSCODE_GOOD) {                        
//         proto_syslog(LOG_WARNING,
//                 "method:%s  UA_Server_addMethodNode %s", functionName,UA_StatusCode_name(retval));
//                 return -1;
//     }
//     return 0;
// }
/*---------------------------------------------------------------------------------------------------------*/


void set_double_to_node(UA_SERVER_PARAM *ua_ser_para,UA_NodeId nodeId,double value_f,int tag_state_code) //设置浮点型
{
    UA_Double value = value_f;
    UA_Variant variant;
    UA_Variant_init(&variant);
    UA_Variant_setScalar(&variant, &value, &UA_TYPES[UA_TYPES_DOUBLE]);

    UA_StatusCode statusCode = UA_STATUSCODE_BAD;
    if(tag_state_code == 0) statusCode = UA_STATUSCODE_GOOD;
    UA_DataValue dataValue;
    UA_DataValue_init(&dataValue);
    dataValue.value = variant;
    dataValue.hasValue = true;
    dataValue.hasStatus = true;
    dataValue.status = statusCode;

    UA_Server_writeDataValue(ua_ser_para->ua_server, nodeId, dataValue);
    // if (ua_ser_para->setting.historizingBackend.serverSetHistoryData != NULL)
    //     ua_ser_para->setting.historizingBackend.serverSetHistoryData(NULL,
    //         ua_ser_para->setting.historizingBackend.context, &nodeId, NULL, &nodeId, true, &dataValue);
}

void set_char_to_node(UA_Server *server,UA_NodeId nodeId,char *valuec) 
{   
    UA_String value = UA_STRING(valuec);
    UA_DataValue dataValue;
    UA_DataValue_init(&dataValue);
    UA_Variant_setScalar(&dataValue.value, &value, &UA_TYPES[UA_TYPES_STRING]);

    UA_Server_writeDataValue(server, nodeId, dataValue);
}

void set_int_to_node(UA_Server *server,UA_NodeId nodeId,int value_i,int tag_state_code) //设置整形
{
    UA_Int32 value = value_i;
    UA_Variant variant;
    UA_Variant_init(&variant);
    UA_Variant_setScalar(&variant, &value, &UA_TYPES[UA_TYPES_INT32]);

    UA_StatusCode statusCode = UA_STATUSCODE_BAD;
    if(tag_state_code == 0) statusCode = UA_STATUSCODE_GOOD;
    UA_DataValue dataValue;
    UA_DataValue_init(&dataValue);
    dataValue.value = variant;
    dataValue.hasValue = true;
    dataValue.hasStatus = true;
    dataValue.status = statusCode;

    UA_Server_writeDataValue(server, nodeId, dataValue);
}

//浮点数没有判断，以为浮点数现在设计上认为浮点数不怎么关注变化上报
#define tag_value_chang(tag) ( \
	((tag)->data_type == TYPE_TAG_INT && (tag)->last_read_cache.to_int != (tag)->read_cache.to_int) || \
	((tag)->data_type == TYPE_TAG_FLOAT && (tag)->last_read_cache.to_float != (tag)->read_cache.to_float) \
)

static int save_history_data(UA_SERVER_PARAM *ua_ser_para, tag_t *tag)
{
    if (!ua_ser_para->setting.historizingBackend.context) {
        proto_syslog(LOG_ERR, "history backend context is NULL, skip history operation");
        return 0;
    }
    device_t *dev = (device_t *)tag->dev_ptr;
    int need_save = 0;
    int persistence = 0;
    struct opcua_write_limit
    {
        const int limit;
        int cur;
    } write_limit = {
        .limit = 20000,
        .cur = 0};

    switch (tag->history_type)
    {
        case HISTORY_TYPE_OFF:
        break;
        
        case HISTORY_TYPE_CHANGE:
        if (tag_value_chang(tag))
        {
            need_save = 1;
        }
        break;

        case HISTORY_TYPE_BOTH:
        if (tag_value_chang(tag))
        {
            need_save = 1;
            persistence = 1;
        };
		/* fall through */
        case HISTORY_TYPE_PERIOD:
        tag->sample_counts++;
        if (tag->sample_counts >= dev->sample_period)
        {
            tag->sample_counts = 0;
            need_save = 1;
        }
        break;    
        default:
            proto_syslog(LOG_ERR, "%s history type cfg error, type = %d", tag->name, tag->history_type);
            break;
    }
    // proto_syslog(LOG_ERR, "1tag:%s, save %d, %f", tag->name, tag->read_cache.to_int, tag->read_cache.to_float);
    if (need_save != 0)
    {
        // proto_syslog(LOG_ERR, "2tag:%s, save %d, %f", tag->name, tag->read_cache.to_int, tag->read_cache.to_float);
        UA_DataValue dv;
        UA_DataValue_init(&dv);
	    dv.hasValue = true;
	    UA_Variant_init(&dv.value);
        if (tag->data_type == 0)
        {
            UA_Int32 temp = tag->read_cache.to_int;
            UA_Variant_setScalar(&dv.value, &temp, &UA_TYPES[UA_TYPES_INT32]);
        }
        else
        {
            UA_Double temp = tag->read_cache.to_float;
            UA_Variant_setScalar(&dv.value, &temp, &UA_TYPES[UA_TYPES_DOUBLE]);
        }
        ua_ser_para->setting.historizingBackend.serverSetHistoryData(NULL, ua_ser_para->setting.historizingBackend.context, &tag->node_id, &write_limit, &tag->node_id, true, &dv);
        if (write_limit.cur > ((double)write_limit.limit / 0.75))
        {
            proto_syslog(LOG_WARNING, "save history write_limit:[limit:%d,cur:%d]", write_limit.limit, write_limit.cur);
        }
        else
        {
            // proto_syslog(LOG_WARNING, "save history write_limit:[limit:%d,cur:%d]", write_limit.limit, write_limit.cur);
        }
    }
    if(persistence&&ua_ser_para->user_data->data_persistence && strcmp(dev->no, "DIDO") != 0) // DIDO设备时不做数据持久化 
    {
        updata_data_persistence(tag);
    }
    return 0;
}

void set_val_to_node_by_tag(void *context,tag_t *tag) //从tag刷新值
{
    UA_SERVER_PARAM *ua_ser_para = context;
    // if(tag->data_type == TYPE_TAG_INT) //不需要刷新数据了 只用存历史数据
    //     set_int_to_node(ua_ser_para->ua_server,tag->node_id,tag->read_cache.to_int,tag->state_code);
    // else
    //     set_float_to_node(ua_ser_para->ua_server,tag->node_id,tag->read_cache.to_float,tag->state_code);
    if(ua_ser_para->isUAHasHistoryData==1)
        save_history_data(ua_ser_para, tag);
}

int get_a_value_float(UA_Server *server,UA_NodeId nodeId,float *data_f) //获取浮点型 返回0错误 1成功 实际可以不用判断返回值 逻辑正确就不会出错
{
    //UA_NodeId nodeId=UA_NODEID_STRING(1,"PCS01.ONLINE");//PCS01.ONLINE
    int ret = -1;
    UA_Variant value;
    ret = UA_Server_readValue(server, nodeId, &value);
    if(ret != UA_STATUSCODE_GOOD)
    {
        proto_syslog(LOG_WARNING,
                "get_a_value_float err code:%d",ret);
        return -1;
    }
    if (value.data == NULL)
    {
        proto_syslog(LOG_WARNING,
                "get_a_value_float type err"); 
        return -1;
    }
    *data_f = *(UA_Double*)value.data;
    proto_syslog(LOG_WARNING,
            "get_a_value_float:%f",*data_f); 

    return 0;
}


int get_a_value_int(UA_Server *server,UA_NodeId nodeId,int *data_i) //获取整形 返回0错误 1成功
{
    //UA_NodeId nodeId=UA_NODEID_STRING(1,"PCS01.ONLINE");//PCS01.ONLINE
    int ret = -1;
    UA_Variant value;
    ret = UA_Server_readValue(server, nodeId, &value);
    if(ret != UA_STATUSCODE_GOOD)
    {
        proto_syslog(LOG_WARNING,
                "get_a_value_int err code:%d",ret);
        return -1;
    }
    if (value.data == NULL)
    {
        proto_syslog(LOG_WARNING,
                "get_a_value_int type err"); 
        return -1;
    }
    *data_i = *(UA_Int32*)value.data;
    proto_syslog(LOG_WARNING,
                "get_a_value_int:%d",*data_i); 
    return 0;
}


void updateCurrentTime(UA_Server *server) {
    UA_DateTime now = UA_DateTime_now();
    UA_Variant value;
    UA_Variant_setScalar(&value, &now, &UA_TYPES[UA_TYPES_DATETIME]);
    // UA_NodeId currentNodeId = UA_NODEID_STRING(1, "PCS01.ONLINE");
    //UA_Server_writeValueWithoutCallback(server, currentNodeId, value);
}

void ua_write_data_to_tag(tag_t *tag, UA_DataValue *data)
{
    UA_Variant val = data->value;
    if (tag->data_type == TYPE_TAG_INT)
    {
        tag->write_cache.to_int = *(UA_Int32*) val.data;;
    }
    else
    {
        tag->write_cache.to_float = *(UA_Double*) val.data;;
    }

    if (tag->data_type == TYPE_TAG_INT)
    {
        proto_syslog(LOG_DEBUG, "ua write tag:%s value:%ld\n", tag->name, tag->write_cache.to_int);
    }
    else
    {
        proto_syslog(LOG_DEBUG, "ua write tag:%s value:%f\n", tag->name, tag->write_cache.to_float);
    }
}


#define GET_STRING_BY_NODEID(nodeId)  (nodeId->identifierType == UA_NODEIDTYPE_STRING)?nodeId->identifier.string.data:NULL
#define GET_STRING_LEN_BY_NODEID(nodeId)  (nodeId->identifierType == UA_NODEIDTYPE_STRING)?nodeId->identifier.string.length:0
#define GET_INT_BY_NODEID(nodeId)  (nodeId->identifierType == UA_NODEIDTYPE_NUMERIC)?nodeId->identifier.numeric:-1
 //UA_NODEID_STRING(namespaceIndex,sting)
#if 0
static void
onReadTag(UA_Server *server,
               const UA_NodeId *sessionId, void *sessionContext,
               const UA_NodeId *nodeid, void *nodeContext,
               const UA_NumericRange *range, const UA_DataValue *data) {

                if (sessionId->identifierType == UA_NODEIDTYPE_GUID && sessionId->identifier.guid.data1 == 1)
                {
                    //   proto_syslog(LOG_WARNING,
                    //     "server read");
                    // proto_syslog(LOG_DEBUG, "server read");
                    return;
                }

                // proto_syslog(LOG_WARNING,
                // "client read");
}

static void
onWriteTag(UA_Server *server,
               const UA_NodeId *sessionId, void *sessionContext,
               const UA_NodeId *nodeId, void *nodeContext,
               const UA_NumericRange *range, const UA_DataValue *data) {
                proto_syslog(LOG_WARNING,"sessionId->identifierType:%d sessionId->identifier.guid.data1:%d data:%d",
                    sessionId->identifierType,sessionId->identifier.guid.data1,*(int *)data->value.data); 
                if (sessionId->identifierType == UA_NODEIDTYPE_GUID && sessionId->identifier.guid.data1 == 1)
                {
                    //   proto_syslog(LOG_WARNING,
                    //     "server write");
                    // ems_syslog(LOG_WARNING, "server write");
                    return;
                }
                tag_t *tag = (tag_t*)nodeContext;
                int ret = 0;
                int times = 0;
                while (check_list_num(tag->chan_ptr) > MAX_LIST_DATA_NUM)
                {
                    times++;
                    if(times>MAX_WTIRE_TIMES)
                    {
                        proto_syslog(LOG_WARNING, "error:[%s] list have to many message write error!!",tag->disp_name);
                        return;
                    }
                    proto_syslog(LOG_WARNING, "warning:[%s] list have to many message write wait next write!!",tag->disp_name);
                }
                // proto_syslog(LOG_WARNING, "state code :[ %d][ %d][ %d][ %d] !!",tag->state_code,data->status,data->hasValue,data->hasStatus);
                // if(ret||(!check_online(tag->dev_ptr)))
                // {
                //     if(tag->state_code == TAG_STATE_BAD) return;
                //     tag->last_state_code = tag->state_code;
                //     tag->state_code = TAG_STATE_BAD; 
                //     set_int_to_node(server,tag->node_id,tag->read_cache.to_int,tag->state_code);
                //     return;
                // }
                ua_write_data_to_tag(tag,(UA_DataValue *)data);
                //data->status = UA_STATUSCODE_BADINTERNALERROR;
                awake_dev_sync(tag);
                // ems_syslog(LOG_WARNING,
                // "client write");
}
#endif
/*****************************************load file**************************************/

static UA_INLINE UA_StatusCode
writeFile(const char* const path, const UA_ByteString buffer) {
    FILE *fp = NULL;

    fp = fopen(path, "wb");
    if(fp == NULL) 
        return UA_STATUSCODE_BADINTERNALERROR;

    for(UA_UInt32 bufIndex = 0; bufIndex < buffer.length; bufIndex++) {
        int retVal = fputc(buffer.data[bufIndex], fp);
        if(retVal == EOF) {
            fclose(fp);
            return UA_STATUSCODE_BADINTERNALERROR;
        }
    }

    fclose(fp);
    return UA_STATUSCODE_GOOD;
}
/**----------------------------------------------------------------------------------------------*/
/**
 * data source
*/
#if 1
static UA_StatusCode
readTag(UA_Server *server,
                const UA_NodeId *sessionId, void *sessionContext,
                const UA_NodeId *nodeId, void *nodeContext,
                UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
                UA_DataValue *dataValue) {

    if (sessionId->identifierType == UA_NODEIDTYPE_GUID && sessionId->identifier.guid.data1 == 1)
    {
        return UA_STATUSCODE_GOOD;
    }

    tag_t *tag = (tag_t*)nodeContext;

    UA_Variant variant;
    UA_Variant_init(&variant);
    UA_StatusCode statusCode = UA_STATUSCODE_BAD;
    if (tag->force_set_cache.valid == true)
    {
        statusCode = UA_STATUSCODE_GOOD;
    }
    else{
        if((tag->state_code == 0)&&check_online(tag->dev_ptr)) statusCode = UA_STATUSCODE_GOOD;
    }

    UA_Variant_clear(&dataValue->value);
    if(tag->data_type == TYPE_TAG_INT)
    {
        
        UA_Int32 value = tag->read_cache.to_int;
        UA_Variant_setScalarCopy(&dataValue->value, &value,
                            &UA_TYPES[UA_TYPES_INT32]);
    }
    else
    {
        UA_Double value = tag->read_cache.to_float;
        UA_Variant_setScalarCopy(&dataValue->value, &value,
                            &UA_TYPES[UA_TYPES_DOUBLE]);
    }
    
    dataValue->hasValue = true;
    dataValue->hasStatus = true;
    dataValue->status = statusCode;
    return UA_STATUSCODE_GOOD;
}

#endif
extern cabinet_info_t *get_cabinet_info();
static UA_StatusCode
writeTag(UA_Server *server,
                 const UA_NodeId *sessionId, void *sessionContext,
                 const UA_NodeId *nodeId, void *nodeContext,
                 const UA_NumericRange *range, const UA_DataValue *data) {

                if (sessionId->identifierType == UA_NODEIDTYPE_GUID && sessionId->identifier.guid.data1 == 1)
                {
                    return UA_STATUSCODE_GOOD;
                }
                tag_t *tag = (tag_t*)nodeContext;
                int ret = 0;
                int times = 0;
                while (check_list_num(tag->chan_ptr) > MAX_LIST_DATA_NUM)
                {
                    times++;
                    if(times>MAX_WTIRE_TIMES)
                    {
                        proto_syslog(LOG_WARNING, "error:[%s] list have to many message write error!!",tag->disp_name);
                        ret = 1;
                        break;
                    }
                    proto_syslog(LOG_WARNING, "warning:[%s] list have to many message write wait next write!!",tag->disp_name);
                }
                
                if (tag->force_set_cache.valid == true)
                {
                    proto_syslog(LOG_WARNING, "tag %s have force set!",tag->disp_name);
                    return UA_STATUSCODE_GOOD;
                }

                if(ret||(!check_online(tag->dev_ptr)))
                {
                    return UA_STATUSCODE_BAD;
                }
                char addtmp[256] = {0};
                if (tag->data_type == TYPE_TAG_INT)
                {
                    snprintf(addtmp, sizeof(addtmp), ":%s/%d", tag->name, *(UA_Int32*) data->value.data);
                }
                else
                {
                    snprintf(addtmp, sizeof(addtmp), ":%s/%f", tag->name, *(UA_Double*)data->value.data);
                }
                server_add_new_log(LOG_CODE_OPC_CLIENT_WTIRE,((device_t*)tag->dev_ptr)->no,LOG_MODE_TRIG,1,addtmp);

                ua_write_data_to_tag(tag,(UA_DataValue *)data);
                awake_dev_sync(tag);

                char value_str[64] = {0};
                if(tag->data_type == TYPE_TAG_INT) {
                    snprintf(value_str, sizeof(value_str), "%d", *(UA_Int32*)data->value.data);
                } else {
                    snprintf(value_str, sizeof(value_str), "%f", *(UA_Double*)data->value.data);
                }

                UA_String *user_str = (UA_String *)sessionContext;
                char user[128] = {0};
                if (user_str && user_str->data && user_str->length > 0 && user_str->length < sizeof(user)) {
                    memcpy(user, user_str->data, user_str->length);
                    user[user_str->length] = '\0';
                } else {
                    strcpy(user, "unknown");
                }
                if(((proto_forward_t*)get_pcs_ctrl_var()->device_layer_ptr)->opt_log != NULL)
                {
                    if(get_cabinet_info()->is_slave == 0 || (get_cabinet_info()->is_slave != 0 && strcmp(user, "host") != 0)){
                        device_t *dev = (device_t*)tag->dev_ptr;
                        opt_log_add(((proto_forward_t*)get_pcs_ctrl_var()->device_layer_ptr)->opt_log, user,dev->no , _("Set"),  tag->disp_name, value_str);
                    }
                }
                

    return UA_STATUSCODE_GOOD;
}


#define DATASOURCE 1
void
addValueCallbackToVariable(UA_Server *server,UA_NodeId currentNodeId) {
    //UA_NodeId currentNodeId = UA_NODEID_STRING(1, "current-time-value-callback");
    #if DATASOURCE

    UA_DataSource varDataSource;
    varDataSource.read = readTag;
    varDataSource.write = writeTag;
    UA_StatusCode retval = UA_Server_setVariableNode_dataSource(server, currentNodeId, varDataSource);
    #else
    UA_ValueCallback callback ;
    callback.onRead = NULL;//onReadTag;//ua读缓存数据之前回调
    callback.onWrite = onWriteTag;//ua写缓存数据之后回调
    UA_StatusCode retval =UA_Server_setVariableNode_valueCallback(server, currentNodeId, callback);
    #endif

    if (retval != UA_STATUSCODE_GOOD) {                        
        proto_syslog(LOG_WARNING,
                " setVariableNode %s",UA_StatusCode_name(retval));
    }
}



int ua_server_add_data_item(proto_forward_t *var, device_t *dev, tag_t *tag,int tag_type,int readonly, sqlite3 *db)
{
    if(var->ua_server_param == NULL)
        return -1;
    UA_SERVER_PARAM *ua_ser_para = var->ua_server_param;
    UA_Server *server = ua_ser_para->ua_server;
    //注册UA的回调
    register_data_flush_cb_by_tag(var,tag,set_val_to_node_by_tag,ua_ser_para);
    UA_NodeId_init(&tag->node_id);
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.description = UA_LOCALIZEDTEXT("zh_CN", tag->name);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", tag->disp_name);
    attr.accessLevel = UA_ACCESSLEVELMASK_READ;
    if(readonly==!READ_ONLY)
        attr.accessLevel |= UA_ACCESSLEVELMASK_WRITE;
    UA_Double actposValue_d = 0.0f;//默认值
    UA_Int32 actposValue_i = 0;//默认值
    if((ua_ser_para->user_data->data_persistence == true))
    {
        if ((strcmp(dev->no, "DIDO") != 0) && (tag->history_type == HISTORY_TYPE_BOTH))
        {
            get_last_data_from_data_presistence(var->gv, tag);
            actposValue_d = tag->read_cache.to_float;
            actposValue_i = tag->read_cache.to_int;
        }
    }
    void *value1=NULL;
    // printf("tag->data_type:%d",tag->data_type);
    if(tag->data_type == TYPE_TAG_FLOAT)
    {
        if (tag->ua_enum_type != NULL)
        {
            proto_syslog(LOG_WARNING, "tag:%s type is float, but the desc is configured for enum", tag->name);
        }
        tag->read_cache.to_float = actposValue_d;//默认值
        attr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
        value1= &actposValue_d;
        UA_Variant_setScalar(&attr.value, value1, &UA_TYPES[UA_TYPES_DOUBLE]);
    }
    else 
    {
        tag->read_cache.to_int = actposValue_i;//默认值
        attr.dataType = (tag->ua_enum_type == NULL) ? UA_TYPES[UA_TYPES_INT32].typeId : ((ua_custom_enum_type*)tag->ua_enum_type)->typeId;
        value1= &actposValue_i;
        UA_Variant_setScalar(&attr.value, value1, &UA_TYPES[UA_TYPES_INT32]);
    }
    if ((ua_ser_para->isUAHasHistoryData)&&(tag->history_type!=HISTORY_TYPE_OFF))
    {
        attr.accessLevel |= UA_ACCESSLEVELMASK_HISTORYREAD;
        attr.historizing = true;
    }
    if (tag->data_type == 0)
    {
        proto_syslog(LOG_NOTICE, "init value tag:%s.%s -> type:%d, v=%ld", dev->no, tag->name,tag->data_type, tag->read_cache.to_int);
    }
    else
    {
        proto_syslog(LOG_NOTICE, "init value tag:%s.%s -> type:%d, v=%f", dev->no, tag->name,tag->data_type, tag->read_cache.to_float);
    }
    UA_NodeId *parent_note_id = NULL;
    switch (tag_type)
    {
    case PROPERTY_REALDATA:
        {
            if(readonly==READ_ONLY)
                parent_note_id = &dev->child_nod.data_node_id;
            else
                parent_note_id = &dev->child_nod.setting_node_id;
        }
        break;
     case PROPERTY_SYSTEM:
        {
            parent_note_id = &dev->child_nod.state_node_id;
        }
        break;
     case PROPERTY_ALARM:
        {
            parent_note_id = &dev->child_nod.alarm_node_id;
        }
        break;
    
    default:
        break;
    }

    // UA_DataSource DataSource;
    // DataSource.read = NULL;
    // DataSource.write = writeTag;

//    UA_VariableAttributes attr = UA_VariableAttributes_default;
//     attr.displayName = UA_LOCALIZEDTEXT("en-US", "Current time - data source");
    //  attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

    // UA_DataSource timeDataSource;
    // timeDataSource.read = readTag;
    // timeDataSource.write = writeTag;
    // UA_StatusCode retval = UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, tag->dev_tag_name), *parent_note_id,
    //                                      UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, tag->name),
    //                                     UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr,
    //                                     timeDataSource, tag,  &tag->node_id);

    // UA_StatusCode retval = UA_Server_addDataSourceVariableNode(server,  UA_NODEID_STRING(1, tag->dev_tag_name), *parent_note_id,
    //                                     UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, tag->name),
    //                                     UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr,
    //                                     DataSource, NULL, NULL);

    UA_StatusCode retval = UA_Server_addVariableNode(server, UA_NODEID_STRING(1, tag->dev_tag_name),
                              *parent_note_id,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, tag->name),
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr,/*timeDataSource,*/ tag, &tag->node_id);
    if (retval != UA_STATUSCODE_GOOD) {                        
        proto_syslog(LOG_WARNING,
                "tag:%s  UA_Server_addVariableNode %s", tag->dev_tag_name,UA_StatusCode_name(retval));
    }
    if ((ua_ser_para->isUAHasHistoryData)&&(tag->history_type!=HISTORY_TYPE_OFF))
    {
        retval = ua_ser_para->gathering.registerNodeId(server, ua_ser_para->gathering.context, &tag->node_id, ua_ser_para->setting);
        if(retval != UA_STATUSCODE_GOOD) proto_syslog(LOG_WARNING, "registerNodeId %s", UA_StatusCode_name(retval));
        creat_table(db,tag->dev_tag_name);
    }

    addValueCallbackToVariable(server,tag->node_id);
    return 0;
}

int ua_server_add_string_node(proto_forward_t *var, device_t *dev, char *node_name, char *node_disp_name, char *v_string)
{
    if (var->ua_server_param == NULL)
        return -1;
    UA_SERVER_PARAM *ua_ser_para = var->ua_server_param;
    UA_Server *server = ua_ser_para->ua_server;

    UA_VariableAttributes attr = UA_VariableAttributes_default;

    char dev_tag_name[DEV_NO_LEN + TAG_NAME_LEN] = {0};
    snprintf(dev_tag_name, sizeof(dev_tag_name), "%s.%s", dev->no, node_name);

    attr.description = UA_LOCALIZEDTEXT("zh_CN", node_name);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", node_disp_name);
    attr.accessLevel = UA_ACCESSLEVELMASK_READ;
    attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;

    UA_String classVar = UA_STRING(v_string);
    UA_Variant_setScalar(&attr.value, &classVar, &UA_TYPES[UA_TYPES_STRING]);
    // UA_Variant_setScalar(&attr.value, &classVar, &UA_TYPES[UA_TYPES_STRING]);

    UA_NodeId *parent_note_id = &dev->child_nod.state_node_id;

    UA_StatusCode retval = UA_Server_addVariableNode(server, UA_NODEID_STRING(1, dev_tag_name),
                                                     *parent_note_id,
                                                     UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, node_name),
                                                     UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, /*timeDataSource,*/ NULL, NULL);
    if (retval != UA_STATUSCODE_GOOD)
    {
        proto_syslog(LOG_WARNING,
                  "tag:%s  UA_Server_addVariableNode %s", dev_tag_name, UA_StatusCode_name(retval));
        return -2;
    }

    return 0;
}

UA_NodeId *ua_server_add_dir_for_dev(void *par, char *dev_no, const char *node, char *description, char *displayName)
{
    UA_Server *server = ((UA_SERVER_PARAM *)par)->ua_server;
    char buff[256] = {0};
    snprintf(buff, sizeof(buff), "%s.%s", dev_no, node);
    //
    UA_ObjectAttributes attr = UA_ObjectAttributes_default;
    UA_NodeId dev_node_id = UA_NODEID_STRING(1, dev_no);
    UA_NodeId node_id = UA_NODEID_STRING_ALLOC(1, buff);
    attr.description = UA_LOCALIZEDTEXT("zh_CN", description);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", displayName);
    UA_Server_addObjectNode(server, node_id,
                            dev_node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, description),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), attr, NULL, NULL);
    //
    UA_NodeId *ro_rw_id = calloc(2, sizeof(UA_NodeId));
    snprintf(buff, sizeof(buff), "%s.%s.DATA", dev_no, node);
    ro_rw_id[0] = UA_NODEID_STRING_ALLOC(1, buff);
    UA_NodeId ro_id = ro_rw_id[0];
    attr.description = UA_LOCALIZEDTEXT("zh_CN", "DATA");
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", _("Read data"));
    UA_Server_addObjectNode(server, ro_id,
                            node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, description),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), attr, NULL, NULL);
    //
    snprintf(buff, sizeof(buff), "%s.%s.SETTING", dev_no, node);
    ro_rw_id[1] = UA_NODEID_STRING_ALLOC(1, buff);
    UA_NodeId rw_id = ro_rw_id[1];
    attr.description = UA_LOCALIZEDTEXT("zh_CN", "SETTING");
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", _("Set parameter"));
    UA_Server_addObjectNode(server, rw_id,
                            node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, description),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), attr, NULL, NULL);

    return ro_rw_id;
}

int ua_server_add_dev_node(void *_var,channel_t *channel, device_t *dev)
{
    UA_SERVER_PARAM *ua_param = _var;
        if(ua_param == NULL) return -1;
    UA_Server *server = ua_param->ua_server;


    char thistnodeid[256] = {0};
    int ret = 0;
    dev->node_id = UA_NODEID_STRING(1, dev->no);
    UA_ObjectAttributes object_attr1 = UA_ObjectAttributes_default;
    object_attr1.description = UA_LOCALIZEDTEXT("zh_CN", dev->no);
    object_attr1.displayName = UA_LOCALIZEDTEXT("zh_CN", dev->disp_name);
    UA_Server_addObjectNode(server, dev->node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, dev->no),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr1, NULL, NULL);



    snprintf(thistnodeid,sizeof(thistnodeid),"%s.STATUS",dev->no);
    dev->child_nod.state_node_id = UA_NODEID_STRING_ALLOC(1, thistnodeid);

    object_attr1.description = UA_LOCALIZEDTEXT("zh_CN", "STATUS");
    object_attr1.displayName = UA_LOCALIZEDTEXT("zh_CN", _("Status"));
    UA_Server_addObjectNode(server, dev->child_nod.state_node_id,
                            dev->node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES) , UA_QUALIFIEDNAME(1,"STATUS"),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr1, NULL, NULL);

    snprintf(thistnodeid,sizeof(thistnodeid),"%s.DATA",dev->no);
    dev->child_nod.data_node_id = UA_NODEID_STRING_ALLOC(1, thistnodeid);

    object_attr1.description = UA_LOCALIZEDTEXT("zh_CN", "DATA");
    object_attr1.displayName = UA_LOCALIZEDTEXT("zh_CN", _("Read data"));
    UA_Server_addObjectNode(server, dev->child_nod.data_node_id,
                            dev->node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES) , UA_QUALIFIEDNAME(1,"DATA"),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr1, NULL, NULL);

    snprintf(thistnodeid,sizeof(thistnodeid),"%s.SETTING",dev->no);
    dev->child_nod.setting_node_id = UA_NODEID_STRING_ALLOC(1, thistnodeid);

    object_attr1.description = UA_LOCALIZEDTEXT("zh_CN", "SETTING");
    object_attr1.displayName = UA_LOCALIZEDTEXT("zh_CN", _("Set parameter"));
    UA_Server_addObjectNode(server, dev->child_nod.setting_node_id,
                            dev->node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES) ,UA_QUALIFIEDNAME(1,"SETTING"),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr1, NULL, NULL);

    snprintf(thistnodeid,sizeof(thistnodeid),"%s.ALARM",dev->no);
    dev->child_nod.alarm_node_id = UA_NODEID_STRING_ALLOC(1, thistnodeid);


    object_attr1.description = UA_LOCALIZEDTEXT("zh_CN", "ALARM");
    object_attr1.displayName = UA_LOCALIZEDTEXT("zh_CN", _("fault words"));
    UA_Server_addObjectNode(server, dev->child_nod.alarm_node_id,
                            dev->node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES) , UA_QUALIFIEDNAME(1,"ALARM"),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr1, NULL, NULL);


    return ret;
}


UA_NodeId ua_server_add_single_dev_node(void *_var, device_t *dev, const char *dev_no, const char *description, const char *displayName)
{
    UA_Server *server = ((UA_SERVER_PARAM*)_var)->ua_server;
    UA_NodeId node_id = UA_NODEID_STRING_ALLOC(1, (char*)dev_no);
    dev->node_id = UA_NODEID_STRING(1, (char*)dev_no);
    UA_ObjectAttributes object_attr1 = UA_ObjectAttributes_default;
    object_attr1.description = UA_LOCALIZEDTEXT("zh_CN", (char*)description);
    object_attr1.displayName = UA_LOCALIZEDTEXT("zh_CN", (char*)displayName);
    UA_Server_addObjectNode(server, dev->node_id,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, (char*)dev_no),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr1, NULL, NULL);

    return node_id;
}
/*
static int ua_server_update_service_one_data(proto_forward_t *var, const char *sn, const char *sid, const char *key, json_object *val)
{
    object_data_t *data_cfg = NULL;
    char key_str[128];
    char nodeid[128] = {0};

    snprintf(key_str, sizeof(key_str), "%s#%s#%s", sn, sid, key);

    hash_intptr_findptr(var->data_val, key_str, strlen(key_str), (void **)&data_cfg);
    if (data_cfg == NULL)
    {
        int i = 0;

        data_cfg = find_data_cfg(var, sn, sid, key);

        if (data_cfg == NULL)
        {
            proto_syslog(LOG_ERR, "not find the data cfg");
            return -1;
        }

        int ret = hash_intptr_addptr(&var->data_val, key_str, strlen(key_str), data_cfg);
        if (ret < 0)
        {
            proto_syslog(LOG_ERR, "failed to add data item");
            return -1;
        }
        hash_intptr_findptr(var->data_val, key_str, strlen(key_str), (void **)&data_cfg);
        if (data_cfg == NULL)
        {
            proto_syslog(LOG_ERR, "still NULL after insert");
            return -1;
        }
    }
    snprintf(nodeid, sizeof(nodeid), "%s.%s.%s", sn, sid, key);

    UA_Server *server = var->ua_server;
    UA_Variant value;
    UA_NodeId currentNodeId = UA_NODEID_STRING(1, nodeid);
    UA_Variant_init(&value);

    switch (data_cfg->data_type)
    {
    case D_BIT:
    case D_INT8:
    case D_INT16:
    {
        int16_t temp1 = json_object_get_double(val);
        UA_Variant_setScalar(&value, &temp1, &UA_TYPES[UA_TYPES_INT16]);
        UA_Server_writeValue(server, currentNodeId, value);
    }
    break;
    case D_INT32:
    {
        int32_t temp1 = json_object_get_double(val);
        UA_Variant_setScalar(&value, &temp1, &UA_TYPES[UA_TYPES_INT32]);
        UA_Server_writeValue(server, currentNodeId, value);
    }
    break;
    case D_INT64:
    {
        int64_t temp1 = json_object_get_double(val);
        UA_Variant_setScalar(&value, &temp1, &UA_TYPES[UA_TYPES_INT64]);
        UA_Server_writeValue(server, currentNodeId, value);
    }
    break;
    case D_FLOAT:
    {
        float temp1 = json_object_get_double(val);
        UA_Variant_setScalar(&value, &temp1, &UA_TYPES[UA_TYPES_FLOAT]);
        UA_Server_writeValue(server, currentNodeId, value);
    }
    break;
    case D_DOUBLE:
    {
        double temp1 = json_object_get_double(val);
        UA_Variant_setScalar(&value, &temp1, &UA_TYPES[UA_TYPES_DOUBLE]);
        UA_Server_writeValue(server, currentNodeId, value);
    }
    break;
    case D_UINT8:
    case D_UINT16:
    {
        uint16_t temp1 = json_object_get_double(val);
        UA_Variant_setScalar(&value, &temp1, &UA_TYPES[UA_TYPES_UINT16]);
        UA_Server_writeValue(server, currentNodeId, value);
    }
    break;
    case D_UINT32:
    {
        uint32_t temp1 = json_object_get_double(val);
        UA_Variant_setScalar(&value, &temp1, &UA_TYPES[UA_TYPES_UINT32]);
        UA_Server_writeValue(server, currentNodeId, value);
    }
    break;
    case D_UINT64:
    {
        uint64_t temp1 = json_object_get_double(val);
        UA_Variant_setScalar(&value, &temp1, &UA_TYPES[UA_TYPES_UINT64]);
        UA_Server_writeValue(server, currentNodeId, value);
    }
    case D_STR:
    {
        UA_String ua_result = UA_String_fromChars(json_object_get_string(val));
        UA_Variant_setScalarCopy(&value, &ua_result, &UA_TYPES[UA_TYPES_STRING]);
        UA_Server_writeValue(server, currentNodeId, value);
        UA_String_clear(&ua_result);
    }
    default:
        break;
    }
}

int ua_server_update_service_datas(proto_forward_t *var, const char *sn, const char *sid, json_object *datas)
{
    if (var->ua_server == NULL)
    {
        proto_syslog(LOG_WARNING, "server not running");
        return -1;
    }
    json_object_object_foreach(datas, key, val)
    {
        json_object *json_val;
        if (json_object_object_get_ex(val, "v", &json_val))
        {
            ua_server_update_service_one_data(var, sn, sid, key, json_val);
        }
    }
}
*/
int UA_Server_addRepeatedCallback1(UA_SERVER_PARAM *ua_param, UA_ServerCallback callback, //添加周期回调
                              void *data, UA_Double interval_ms,
                              UA_UInt64 *callbackId)
{
    return UA_Server_addRepeatedCallback( ua_param->ua_server, callback, data, interval_ms, callbackId);
}


UA_Boolean running = true;
pthread_t UAServer_thread_id = {0};
bool pthread_run = false;


void historyCycleCallback(UA_Server *server, void *data) 
{
    // UA_SERVER_PARAM *ua_param = data;
	// if(ua_get_history_database_size(ua_param)<(ua_param->user_data->history_database_size==0?900*1024:ua_param->user_data->history_database_size)) //超过1.2g删除前20%的数据
    //     return;
    // proto_syslog(LOG_NOTICE,"database is too large :%lldM,while delete 0.2",ua_param->user_data->history_database_size);
    // ua_del_history_percent(ua_param,20); //删除20%的数据
}

ua_custom_enum_type *ua_creat_custom_enum(UA_Server *server, char *name, char *enum_discribe)
{
    char *p = enum_discribe;
    int enum_sum = 0;
    int tmp2 = 0;
    while (*p != '\0') // 简单的语法检查
    {
        if (*p == ':')
        {
                enum_sum++;
                if ((tmp2 + 1) != enum_sum)
                {
                    break;
                }
        }
        else if (*p == ';')
        {
                tmp2++;
                if (tmp2 != enum_sum)
                {
                    break;
                }
        }
        p++;
    }

    if ((enum_sum == 0) || (tmp2 == 0))
    {
        proto_syslog(LOG_ERR, "enum(%s), error in the description string(%s)", name, enum_discribe);
        return NULL;
    }
    else if (enum_sum != tmp2)
    {
        proto_syslog(LOG_WARNING, "enum(%s), warning in the description string(%s)", name, enum_discribe);
    }

    ua_custom_enum_type *new_type = calloc(1, sizeof(ua_custom_enum_type));
    ua_custom_enum_map *enum_arr = calloc(enum_sum+1, sizeof(ua_custom_enum_map));
    new_type->name = strdup(name);
    new_type->typeId = UA_NODEID_STRING(1, name);
    new_type->enum_arr = enum_arr;

    p = enum_discribe;
    for (int i = 0; i < enum_sum; i++)
    {
        sscanf(p, "%d:%[^;]", &enum_arr[i].value, enum_arr[i].name);
        proto_syslog(LOG_INFO, "enum(%s): %d, %s", name, enum_arr[i].value, enum_arr[i].name);
        p = strstr(p, ";") + 1;
    }
    //
    UA_DataTypeAttributes typeAttr = UA_DataTypeAttributes_default;
    typeAttr.description = UA_LOCALIZEDTEXT("en-US", name);
    typeAttr.displayName = UA_LOCALIZEDTEXT("en-US", name);
    int ret = UA_Server_addDataTypeNode(server, new_type->typeId,
                                        UA_NODEID_NUMERIC(0, UA_NS0ID_ENUMERATION),
                                        UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
                                        UA_QUALIFIEDNAME(1, name), typeAttr, NULL, &new_type->typeId);
    if (UA_STATUSCODE_GOOD != ret)
    {
        proto_syslog(LOG_ERR, "UAServer add DataType Node Error, ret = %s", UA_StatusCode_name(ret));
        goto ua_creat_custom_enum_err;
    }
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.valueRank = UA_VALUERANK_ONE_DIMENSION;
    attr.arrayDimensionsSize = 1;
    attr.dataType = UA_NODEID_NUMERIC(0, UA_NS0ID_ENUMVALUETYPE);
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "EnumValues"); // EnumValues:这个名称不能使用其他字符串
    attr.description = UA_LOCALIZEDTEXT("en-US", "EnumValues");
    attr.arrayDimensions = UA_malloc(sizeof(uint32_t));
    *attr.arrayDimensions = enum_sum;
    UA_EnumValueType *uaValues = UA_malloc(sizeof(UA_EnumValueType) * enum_sum);

    for (size_t i = 0; i < enum_sum; i++)
    {
        UA_init(&uaValues[i], &UA_TYPES[UA_TYPES_ENUMVALUETYPE]);
        uaValues[i].value = enum_arr[i].value;
        uaValues[i].description = UA_LOCALIZEDTEXT("en-US", enum_arr[i].name);
        uaValues[i].displayName = UA_LOCALIZEDTEXT("en-US", enum_arr[i].name);
    }

    UA_Variant_setArray(&attr.value, uaValues, enum_sum, &UA_TYPES[UA_TYPES_ENUMVALUETYPE]);
    UA_NodeId enumValuesId = {0};
    ret = UA_Server_addVariableNode(server, UA_NODEID_NULL, new_type->typeId,
                                    UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY),
                                    UA_QUALIFIEDNAME(0, "EnumValues"),
                                    UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE),
                                    attr, NULL, &enumValuesId);
    UA_free(attr.arrayDimensions);
    UA_free(uaValues);
    if (UA_STATUSCODE_GOOD != ret)
    {
        proto_syslog(LOG_ERR, "UAServer add VariableNode error, ret = %s", UA_StatusCode_name(ret));
        goto ua_creat_custom_enum_err;
    }
    else
    {
        ret = UA_Server_addReference(server, enumValuesId,
                                     UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
                                     UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY),
                                     true);
                                     
        if (UA_STATUSCODE_GOOD != ret)
        {
            proto_syslog(LOG_ERR, "UAServer add Reference error, ret = %s", UA_StatusCode_name(ret));
        }
    }

    if (ret == UA_STATUSCODE_GOOD)
    {
        return new_type;
    }

ua_creat_custom_enum_err:
    free(new_type);
    free(enum_arr);
    return NULL;
}


char *get_enum_value_string(void *enum_type,int value)
{
    if(enum_type ==  NULL)return NULL;
    ua_custom_enum_type *new_type = enum_type;
    
    for (size_t i = 0; new_type->enum_arr!=NULL; i++)
    {
        if (value == new_type->enum_arr[i].value)
        {
            return new_type->enum_arr[i].name;
        }
    }
    return NULL;
}

// static
// UA_EndpointDescription *getRegisterEndpointFromServer(const char *discoveryServerUrl) ;

static
UA_Client *getRegisterClient(UA_SERVER_PARAM *ua_param);


#if 0
static void
serverOnNetworkCallback(const UA_ServerOnNetwork *serverOnNetwork, UA_Boolean isServerAnnounce,
                        UA_Boolean isTxtReceived, void *data) {

        if(!running) return;
    UA_SERVER_PARAM *ua_param = (UA_SERVER_PARAM *)data;
    if(!isServerAnnounce) {
        // proto_syslog(LOG_ERR, "isServerAnnounce %.*s",
        //             (int)serverOnNetwork->discoveryUrl.length, serverOnNetwork->discoveryUrl.data);
            if((serverOnNetwork!=NULL)&&(serverOnNetwork->discoveryUrl.length>0))
            {
                ua_clinet_info *ua_client_i = NULL;
                if (hash_intptr_findptr(ua_param->discover_hash, serverOnNetwork->discoveryUrl.data,serverOnNetwork->discoveryUrl.length, (void **)&ua_client_i) < 0)
                {
                    ua_client_i = calloc(sizeof(ua_clinet_info),1);
                    int ret = hash_intptr_addptr(&ua_param->discover_hash,serverOnNetwork->discoveryUrl.data,serverOnNetwork->discoveryUrl.length,ua_client_i);
                    if (ret == 0)
                    {
                        proto_syslog(LOG_NOTICE, "creat new client on :%.*s!",(int)(serverOnNetwork->discoveryUrl.length),serverOnNetwork->discoveryUrl.data);
                    }
                }
                if(ua_client_i!=NULL)
                    ua_client_i->valid = 0;
            }
            else
                proto_syslog(LOG_ERR,"url is NULL!");
            // proto_syslog(LOG_ERR,"retrun isServerAnnounce!");
            return;
    }
    if(!isTxtReceived)

    {
        // proto_syslog(LOG_ERR, "isTxtReceived%.*s",
        //             (int)serverOnNetwork->discoveryUrl.length, serverOnNetwork->discoveryUrl.data);
        if((serverOnNetwork!=NULL)&&(serverOnNetwork->discoveryUrl.length>0))
            {
                ua_clinet_info *ua_client_i = NULL;
                if (hash_intptr_findptr(ua_param->discover_hash, serverOnNetwork->discoveryUrl.data,serverOnNetwork->discoveryUrl.length, (void **)&ua_client_i) < 0)
                {
                    ua_client_i = calloc(sizeof(ua_clinet_info),1);
                    int ret = hash_intptr_addptr(&ua_param->discover_hash,serverOnNetwork->discoveryUrl.data,serverOnNetwork->discoveryUrl.length,ua_client_i);
                    if (ret == 0)
                    {
                        proto_syslog(LOG_NOTICE, "creat new client on :%.*s!",(int)(serverOnNetwork->discoveryUrl.length),serverOnNetwork->discoveryUrl.data);
                    }
                }
                if(ua_client_i!=NULL)
                    ua_client_i->valid = 0;
            }
            else
                proto_syslog(LOG_ERR,"url is NULL!");

        return;
    }
    if((serverOnNetwork!=NULL)&&(serverOnNetwork->discoveryUrl.length>0))
    {
        ua_clinet_info *ua_client_i = NULL;
        if (hash_intptr_findptr(ua_param->discover_hash, serverOnNetwork->discoveryUrl.data, serverOnNetwork->discoveryUrl.length, (void **)&ua_client_i) < 0)
        {
            ua_client_i = calloc(sizeof(ua_clinet_info),1);
            int ret = hash_intptr_addptr(&ua_param->discover_hash,serverOnNetwork->discoveryUrl.data, serverOnNetwork->discoveryUrl.length,ua_client_i);
            if (ret == 0)
            {
                proto_syslog(LOG_NOTICE, "creat new client :%*.s!",(int)(serverOnNetwork->discoveryUrl.length),serverOnNetwork->discoveryUrl.data);
            }     
        }
        if(ua_client_i!=NULL)
            ua_client_i->valid = 1;
    }
    else
            proto_syslog(LOG_ERR,"url is NULL22222!");
    // proto_syslog(LOG_ERR,"retrun other!");
}
#endif

#if 0
void *start_lds_server(void *data)
{
    bool running = true;
    
    UA_Server *server = UA_Server_new();
    UA_ServerConfig *config = UA_Server_getConfig(server);
    UA_ServerConfig_setDefault(config);

    // This is an LDS server only. Set the application type to DISCOVERYSERVER.
    // NOTE: This will cause UaExpert to not show this instance in the server list.
    // See also: https://forum.unified-automation.com/topic1987.html
    #if 1 //opc.tcp://21881FFF0028:4840
    config->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
    UA_String_clear(&config->applicationDescription.applicationUri);
    config->applicationDescription.applicationUri =
        UA_String_fromChars("urn:ems.server_multicast1");

    // Enable the mDNS announce and response functionality
    config->mdnsEnabled = true;

    config->mdnsConfig.mdnsServerName = UA_String_fromChars("EMS Multicast Server1");

    //setting custom outbound interface
    config->mdnsInterfaceIP = UA_String_fromChars("0.0.0.0");

    // See http://www.opcfoundation.org/UA/schemas/1.03/ServerCapabilities.csv
    // For a LDS server, you should only indicate the LDS capability.
    // If this instance is an LDS and at the same time a normal OPC UA server, you also have to indicate
    // the additional capabilities.
    // NOTE: UaExpert does not show LDS-only servers in the list.
    // See also: https://forum.unified-automation.com/topic1987.html

    config->mdnsConfig.serverCapabilitiesSize = 2;
    UA_String *caps = (UA_String *) UA_Array_new(2, &UA_TYPES[UA_TYPES_STRING]);
    caps[0] = UA_String_fromChars("LDS");
    caps[1] = UA_String_fromChars("NA");
    config->mdnsConfig.serverCapabilities = caps;

    //UA_StatusCode retval = UA_Server_run_startup(server);
    UA_Server_setServerOnNetworkCallback(server, serverOnNetworkCallback, NULL);

    proto_syslog(LOG_ERR,
                    "Server started. Waiting for announce of LDS Server.");
    while (running)
    {
        UA_Server_run_iterate(server, true);
    }
        
    if(!running) {
        UA_Server_delete(server);
        UA_free(discovery_url);
    }

    #else
    config->applicationDescription.applicationType = UA_APPLICATIONTYPE_DISCOVERYSERVER;
    UA_String_clear(&config->applicationDescription.applicationUri);
    config->applicationDescription.applicationUri =
            UA_String_fromChars("urn:local_discovery_server");

    // Enable the mDNS announce and response functionality
    config->mdnsEnabled = true;

    config->mdnsConfig.mdnsServerName = UA_String_fromChars("LDS");

    // See http://www.opcfoundation.org/UA/schemas/1.03/ServerCapabilities.csv
    // For a LDS server, you should only indicate the LDS capability.
    // If this instance is an LDS and at the same time a normal OPC UA server, you also have to indicate
    // the additional capabilities.
    // NOTE: UaExpert does not show LDS-only servers in the list.
    // See also: https://forum.unified-automation.com/topic1987.html

    // E.g. here we only set LDS, and you will not see it in UaExpert
    config->mdnsConfig.serverCapabilitiesSize = 1;
    UA_String *caps = (UA_String *) UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
    caps[0] = UA_String_fromChars("LDS");
    config->mdnsConfig.serverCapabilities = caps;

    /* timeout in seconds when to automatically remove a registered server from
     * the list, if it doesn't re-register within the given time frame. A value
     * of 0 disables automatic removal. Default is 60 Minutes (60*60). Must be
     * bigger than 10 seconds, because cleanup is only triggered approximately
     * every 10 seconds. The server will still be removed depending on the
     * state of the semaphore file. */
    // config->discoveryCleanupTimeout = 60*60;

    UA_StatusCode retval = UA_Server_run(server, &running);
    UA_Server_delete(server);
    #endif
    return NULL;
}
#endif
#define MASTER_EMS 1
int lds_server_init()
{
    //pthread_t tid;
    //if(MASTER_EMS==1)
       // pthread_create(&tid,NULL,start_lds_server,NULL);
    return 0;
}

const UA_ByteString UA_SECURITY_POLICY_BASIC128_URI =
    {56, (UA_Byte *)"http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15"};

#if 0
static
UA_EndpointDescription *getRegisterEndpointFromServer(const char *discoveryServerUrl) {
    UA_Client *client = UA_Client_new();
    UA_ClientConfig_setDefault(UA_Client_getConfig(client));
    UA_EndpointDescription *endpointArray = NULL;
    size_t endpointArraySize = 0;
    UA_StatusCode retval = UA_Client_getEndpoints(client, discoveryServerUrl,
                                                  &endpointArraySize, &endpointArray);
    if(retval != UA_STATUSCODE_GOOD) {
        UA_Array_delete(endpointArray, endpointArraySize,
                        &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
        proto_syslog(LOG_ERR,
                     "GetEndpoints failed with %s", UA_StatusCode_name(retval));
        UA_Client_delete(client);
        return NULL;
    }

    UA_LOG_DEBUG(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Server has %d endpoints", (unsigned long)endpointArraySize);
    UA_EndpointDescription *foundEndpoint = NULL;
    for(size_t i = 0; i < endpointArraySize; i++) {
        proto_syslog(LOG_ERR, "\tURL = %.*s, SecurityMode = %s",
                     (int) endpointArray[i].endpointUrl.length,
                     endpointArray[i].endpointUrl.data,
                     endpointArray[i].securityMode == UA_MESSAGESECURITYMODE_NONE ? "None" :
                     endpointArray[i].securityMode == UA_MESSAGESECURITYMODE_SIGN ? "Sign" :
                     endpointArray[i].securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT ? "SignAndEncrypt" :
                     "Invalid"
        );
        // find the endpoint with highest supported security mode
        if((UA_String_equal(&endpointArray[i].securityPolicyUri, &UA_SECURITY_POLICY_NONE_URI) ||
            UA_String_equal(&endpointArray[i].securityPolicyUri, &UA_SECURITY_POLICY_BASIC128_URI)) && (
            foundEndpoint == NULL || foundEndpoint->securityMode < endpointArray[i].securityMode))
            foundEndpoint = &endpointArray[i];
    }
    UA_EndpointDescription *returnEndpoint = NULL;
    if(foundEndpoint != NULL) {
        returnEndpoint = UA_EndpointDescription_new();
        UA_EndpointDescription_copy(foundEndpoint, returnEndpoint);
    }
    UA_Array_delete(endpointArray, endpointArraySize,
                    &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
    UA_Client_delete(client);
    return returnEndpoint;
}
#endif

static
UA_Client *getRegisterClient(UA_SERVER_PARAM *ua_param) {

    if(ua_param->user_data->en_encryption)
    {
        proto_syslog(LOG_INFO, "Using LDS endpoint with security None");
        UA_Client *client = UA_Client_new();
        
        UA_ClientConfig *config_client = UA_Client_getConfig(client);
        UA_ClientConfig_setDefault(config_client);
        config_client->timeout = 500;
        config_client->requestedSessionTimeout = 10000;
        return client;
    }
    else
    {
        UA_ByteString certificate = loadFile(CLIENT_CERT_CERT);
        UA_ByteString privateKey = loadFile(CLIENT_CERT_KEY);

        /* Load the trustList */
        UA_ByteString *revocationList = NULL;
        size_t revocationListSize = 0;
        size_t trustListSize = 1;
        
        UA_STACKARRAY(UA_ByteString, trustList, trustListSize+1);
        for(size_t trustListCount = 0; trustListCount < trustListSize; trustListCount++)
            trustList[trustListCount] = loadFile(SERVER_CERT_CERT);

        /* Secure client initialization */
        UA_Client *clientRegister = UA_Client_new();
        UA_ClientConfig *cc = UA_Client_getConfig(clientRegister);
        UA_ClientConfig_setDefaultEncryption(cc, certificate, privateKey,
                                            trustList, trustListSize,
                                            revocationList, revocationListSize);
        cc->securityMode = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT;

        UA_ByteString_clear(&certificate);
        UA_ByteString_clear(&privateKey);
        for(size_t deleteCount = 0; deleteCount < trustListSize; deleteCount++)
            UA_ByteString_clear(&trustList[deleteCount]);

        return clientRegister;
    }       
}

#define LDS 1
extern int mdnsd_log_level(char *level);
UA_SERVER_PARAM *ua_server_init(UA_BASE_DATA *data_ua)
{
#ifdef EN_LC
    int port = 4841;
#else
    int port = 4840;
#endif
    proto_syslog(LOG_NOTICE,"ua server init starting...");
    UA_ServerConfig *config = NULL;
    UA_SERVER_PARAM *ua_param = calloc(1,sizeof(UA_SERVER_PARAM));

    UA_BASE_DATA *ua_data_t=calloc(1,sizeof(UA_BASE_DATA));
    if(data_ua!=NULL)
        memcpy(ua_data_t,data_ua,sizeof(UA_BASE_DATA));
    //int slave = (strcmp(ua_data_t->someExInfo[TYPE_INDEX],SLAVE_NAME)==0)?0:1; //从柜0 非从柜1
    // if(slave)
    //     lds_server_init();
    ua_param->ems_online = -1;//先默认-1在线，防止开机故障
    ua_param->ua_server = UA_Server_new();
    ua_param->user_data = ua_data_t;
    config = UA_Server_getConfig(ua_param->ua_server);
    //addCurrentTimeDataSourceVariable(var->ua_server);
    //addCurrentTimeExternalDataSource(var->ua_server);
    // addNodeManageMethod(var->ua_server);
    // deleteNodeManageMethod(var->ua_server);
    // UA_ServerConfig_setDefault(config);
    if(data_ua->en_encryption)
    {
        UA_ByteString certificate = UA_BYTESTRING_NULL;
        UA_ByteString privateKey = UA_BYTESTRING_NULL;
        /* Load certificate and private key */
        certificate = loadFile(SERVER_CERT_CERT);
        privateKey = loadFile(SERVER_CERT_KEY);
        if (UA_ByteString_equal(&certificate, &UA_BYTESTRING_NULL) || UA_ByteString_equal(&privateKey, &UA_BYTESTRING_NULL))
        {
            proto_syslog(LOG_ERR, "load %s or %s error", SERVER_CERT_CERT, SERVER_CERT_KEY);
            exit(0);
        }
        size_t trustListSize = 1;
        UA_STACKARRAY(UA_ByteString, trustList, trustListSize+1);
        for(size_t i = 0; i < trustListSize; i++)
            trustList[i] = loadFile(CLIENT_CERT_CERT);

        size_t issuerListSize = 0;
        UA_ByteString *issuerList = NULL;
        UA_ByteString *revocationList = NULL;
        size_t revocationListSize = 0;
        UA_ServerConfig_setDefaultWithSecurityPolicies(config, port,
                                                       &certificate, &privateKey,
                                                       trustList, trustListSize,
                                                       issuerList, issuerListSize,
                                                       revocationList, revocationListSize);
        UA_String_clear(&config->applicationDescription.applicationUri);                                                  
        config->applicationDescription.applicationUri = UA_STRING_ALLOC("urn:open62541.server.application");
        for (size_t i = 0; i < config->endpointsSize; ++i)
        {
            UA_String_clear(&config->endpoints[i].server.applicationUri);
            config->endpoints[i].server.applicationUri = UA_String_fromChars("urn:open62541.server.application");
        }
        UA_ByteString_clear(&certificate);
        UA_ByteString_clear(&privateKey);
        for(size_t i = 0; i < trustListSize; i++)
            UA_ByteString_clear(&trustList[i]);
    }
    else
    {
        UA_ServerConfig_setMinimal(config,port, NULL);
        char urn[64];
        gethostname(urn,sizeof(urn));
        UA_String_clear(&config->applicationDescription.applicationUri);
        config->applicationDescription.applicationUri =
        UA_String_fromChars(urn);
    }


    // config->networkLayers[0].discoveryUrl = UA_String_fromChars("opc.tcp://192.168.10.23:123");
    // config->networkLayersSize = 1;
//s.length = 0; s.data = NULL;

    // UA_Server_setServerCertificate(server, certificate, privateKey);
    // UA_Server_setNetworkLayers(server, config->networkLayers);

    // config->serverUrlsSize= 1;
    // config->serverUrls = (UA_String *)UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
    // config->serverUrls[0] = UA_STRING_ALLOC("opc.tcp://your-desired-url");
    //proto_syslog(LOG_ERR, "config->customHostname:%s\n",config->customHostname);
    // config->applicationDescription.discoveryUrlsSize = 1;
    // config->applicationDescription.discoveryUrls = (UA_String *)UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
    // config->applicationDescription.discoveryUrls[0] = UA_String_fromChars("opc.tcp://192.168.20.71:4812");

    char local_ip[20] = {0}; 
    if((ua_data_t->someExInfo[3]!=NULL)&&(strlen(ua_data_t->someExInfo[3])>0))
        if(get_localip(ua_data_t->someExInfo[3],local_ip)==0);
            //config->customHostname = UA_STRING_ALLOC(local_ip); //TODO:用默认hostname的话 删除这这个
#if 0

    config->applicationDescription.applicationType = UA_APPLICATIONTYPE_SERVER;
    // UA_String_clear(&config->applicationDescription.applicationUri);
    // config->applicationDescription.applicationUri =
    //     UA_String_fromChars("urn:ems.server_multicast");//TODO:确保唯一

    // Enable the mDNS announce and response functionality
    config->mdnsEnabled = true;

    char mdns_name[20] = {0}; 
    sprintf(mdns_name,"mdns%s",local_ip);
    config->mdnsConfig.mdnsServerName = UA_String_fromChars(mdns_name);

    //setting custom outbound interface
    config->mdnsInterfaceIP = UA_String_fromChars("0.0.0.0"); //this line will produce an error and set the interface to 0.0.0.0

    // See http://www.opcfoundation.org/UA/schemas/1.03/ServerCapabilities.csv
    // For a LDS server, you should only indicate the LDS capability.
    // If this instance is an LDS and at the same time a normal OPC UA server, you also have to indicate
    // the additional capabilities.
    // NOTE: UaExpert does not show LDS-only servers in the list.
    // See also: https://forum.unified-automation.com/topic1987.html

    config->mdnsConfig.serverCapabilitiesSize = 2;
    UA_String *caps = (UA_String *) UA_Array_new(2, &UA_TYPES[UA_TYPES_STRING]);
    caps[0] = UA_String_fromChars("LDS");
    caps[1] = UA_String_fromChars("NA");
    config->mdnsConfig.serverCapabilities = caps;

    UA_Server_run_startup(ua_param->ua_server);

    UA_Server_setServerOnNetworkCallback(ua_param->ua_server, serverOnNetworkCallback, ua_param);
   
    // while (running)
    // {
    //     UA_Server_run_iterate(server, true);
    // }
        
    // if(!running) {
    //     UA_Server_delete(server);
    //     UA_free(discovery_url);
    // }
#endif

    {
        // char server_add[64] = {0};
        // if(slave)
        //     sprintf(server_add,"opc.tcp://localhost:4840");
        // else
        //     sprintf(server_add,"opc.tcp://localhost:4840");//TODO:改成MASTER IP
            
        // UA_Client *clientRegister = UA_Client_new();
        // UA_ClientConfig *config_client = UA_Client_getConfig(clientRegister);
        // UA_ClientConfig_setDefault(config_client);
        // // config_client->endpoint.endpointUrl = UA_String("opc.tcp://192.168.20.71:4840");

        // UA_StatusCode retval =
        // UA_Server_addPeriodicServerRegisterCallback(ua_param->ua_server, clientRegister, server_add,
        //                                                 10 * 60 * 1000, 500, NULL);
        // // UA_StatusCode retval = UA_Server_addPeriodicServerRegisterJob(server,
        // // "opc.tcp://localhost:4840", 10*60*1000, 500, NULL);
        // if(retval != UA_STATUSCODE_GOOD) {
        //     UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
        //                 "Could not create periodic job for server register. StatusCode %s",
        //                 UA_StatusCode_name(retval));
        //     UA_Client_disconnect(clientRegister);
        //     UA_Client_delete(clientRegister);
        // }
    }

#if 1  //修改server的名字
    	// delete server name
    //为了去除编译警告使用UA_LocalizedText_clear函数替换UA_LocalizedText_deleteMembers(已经弃用)，这两个函数的实现都是一个宏，实现方式一模一样
	UA_LocalizedText_clear(&config->applicationDescription.applicationName); // UA_LocalizedText_deleteMembers(&config->applicationDescription.applicationName);
	// update server name
    char OPCUA_SERVER_NAME[256] ={0};

    {
        strcat(OPCUA_SERVER_NAME,data_ua->someExInfo[0]);
    }

    strcat(OPCUA_SERVER_NAME,"(EMS)") ;

    strcat(OPCUA_SERVER_NAME,local_ip) ;

	config->applicationDescription.applicationName = UA_LOCALIZEDTEXT_ALLOC("en", OPCUA_SERVER_NAME);
	
	// modify endpoint name
	for (size_t i = 0; i < config->endpointsSize; ++i)
	{
        UA_LocalizedText * ptr = &config->endpoints[i].server.applicationName;
        //为了去除编译警告使用UA_LocalizedText_clear函数替换UA_LocalizedText_deleteMembers(已经弃用)，这两个函数的实现都是一个宏，实现方式一模一样
        UA_LocalizedText_clear(ptr); // UA_LocalizedText_deleteMembers(ptr);
        (*ptr) = UA_LOCALIZEDTEXT_ALLOC("en", OPCUA_SERVER_NAME);
    }
#endif

  //用户密码登录
    {
        char *sn = get_pcs_ctrl_var()->sn_str;

        UA_UsernamePasswordLogin usernamePasswords[] = {
            { UA_STRING("admin"),    UA_STRING("lnxall123") },
            { UA_STRING("host"),   UA_STRING("lnxall123") },
            { UA_STRING("smu"),    UA_STRING("lnxall123") },
            { UA_STRING("user"),   UA_STRING(sn) }
        };

        config->accessControl.clear(&config->accessControl);
        UA_AccessControl_default(config, false, NULL, &config->securityPolicies[config->securityPoliciesSize - 1].policyUri, sizeof(usernamePasswords) / sizeof(usernamePasswords[0]), usernamePasswords);
        // UA_String_delete(&usernamePasswords[0].username);
        // UA_String_delete(&usernamePasswords[0].password);
        // config->accessControl.allowAddNode = allowAddNode;
        // config->accessControl.allowDeleteNode = allowDeleteNode;
    }

    // UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_default, cfg->port, 0);
    // UA_ServerNetworkLayer *p = calloc(1, sizeof(UA_ServerNetworkLayer));
    // *p = nl;
    // config->networkLayers = p;
    // config->networkLayersSize = 1;
    // UA_ServerConfig_addNetworkLayerTCP(config, cfg->port, 0, 0);

    // config->logger = UA_Log_Stdout_withLevel(0);

 //没有使能历史数据
    // history
    if (ua_data_t->open_history)
    {
        proto_syslog(LOG_INFO, "ua server open history");
        // if (cfg->history == NULL)
        // {
        //     char db_name[128] = {0};
        //     // char hasSDCard[128] ={0};
        //     // int ret = system_reply("cat /var/run/hassdcard",hasSDCard);
        //     // proto_syslog(LOG_NOTICE, "hasSDCard:%s",hasSDCard);
        //     // if (hasSDCard != NULL && strcmp(hasSDCard, "1") == 0)
        //     // {
        //     //     // default page size is 4k
        //     //     sprintf(db_name, "/mnt/sdcard/opc-history.db");
        //     //     if (cfg->history_size == 0)
        //     //     {
        //     //         cfg->history_size = 100000;
        //     //     }
        //     //     free(hasSDCard);
        //     // }
        //     // else
        //     // {
        //     //     sprintf(db_name, "/tmp/opc-history.db");
        //     //     if (cfg->history_size == 0)
        //     //     {
        //     //         cfg->history_size = 500;
        //     //     }
        //     // }
        //     sprintf(db_name, DBFILE);
        //     if (cfg->history_size == 0)
        //     {
        //         cfg->history_size = 100000;
        //     }
        //     cfg->history = strdup(db_name);
        // }
        // proto_syslog(LOG_NOTICE, "history:%s",cfg->history);
        ua_param->gathering = UA_HistoryDataGathering_Default(ua_data_t->point_num==0?1000:ua_data_t->point_num);//最多点位数
        config->historyDatabase = UA_HistoryDatabase_default(ua_param->gathering);
        struct stat st;
#ifdef EN_SQLITE
        if (stat(ua_data_t->history_db_pwd==NULL?DBFILE:ua_data_t->history_db_pwd, &st) != 0)
        {
            ua_param->isUAHasHistoryData = false;
        }
        else
        {
            ua_param->setting.historizingBackend = UA_HistoryDataBackend_Sqlite(ua_data_t->history_db_pwd==NULL?DBFILE:ua_data_t->history_db_pwd);
            if (ua_param->setting.historizingBackend.context == NULL) {
                proto_syslog(LOG_ERR,"Sqlite backend context is NULL, disable history feature!");
                ua_param->isUAHasHistoryData = false;
            } else {
                ua_param->isUAHasHistoryData = true;
            }
        }
#else
        if (stat(ua_data_t->history_db_pwd==NULL?TDENGINEFILE:ua_data_t->history_db_pwd, &st) != 0)
        {
            ua_param->isUAHasHistoryData = false;
        }
        else
        {
            ua_param->setting.historizingBackend = UA_HistoryDataBackend_TDengine(NULL, NULL, NULL, "opchistory");
            if (ua_param->setting.historizingBackend.context == NULL) {
                proto_syslog(LOG_ERR,"TDengine backend context is NULL, disable history feature!");
                ua_param->isUAHasHistoryData = false;
            } else {
                ua_param->isUAHasHistoryData = true;
            }
        }
#endif
        ua_param->setting.maxHistoryDataResponseSize = ua_data_t->history_num==0?100:ua_data_t->history_num;//响应客户端查询历史数据最大数量
        ua_param->setting.historizingUpdateStrategy = UA_HISTORIZINGUPDATESTRATEGY_USER;//UA_HISTORIZINGUPDATESTRATEGY_VALUESET;//UA_HISTORIZINGUPDATESTRATEGY_POLL;
        //var->setting.pollingInterval = 1000;
        //ua_param->isUAHasHistoryData = true;
#ifdef EN_SQLITE
        if(ua_param->setting.historizingBackend.context != NULL)
        {
            char *zErrMsg =NULL;
            sqlite3_exec((sqlite3 *)ua_param->setting.historizingBackend.context, "PRAGMA page_size = 4096;", NULL, NULL, &zErrMsg);
            sqlite3_free(zErrMsg);
            if (SQLITE_OK != sqlite3_exec((sqlite3*)ua_param->setting.historizingBackend.context, "PRAGMA auto_vacuum=INCREMENTAL;", NULL, NULL, &zErrMsg))
            {
                ems_syslog(LOG_ERR, "sqlite3_exec error:%s", zErrMsg);
                exit(-1);
            }
            sqlite3_free(zErrMsg);
#ifdef EN_LC
            sqlite3_exec((sqlite3 *)ua_param->setting.historizingBackend.context,"PRAGMA max_page_count  = 65536;",NULL,NULL,&zErrMsg); // 256M
#else
            sqlite3_exec((sqlite3 *)ua_param->setting.historizingBackend.context,"PRAGMA max_page_count  = 524288;",NULL,NULL,&zErrMsg);// 2G
#endif
            sqlite3_free(zErrMsg);
        }
#endif  
    }
    else
        ua_param->isUAHasHistoryData = false;
    // ua_server_add_nodes(var);
     // addNewEventType(ua_param->ua_server);
    // addGenerateEventMethod(ua_param->ua_server);
    proto_syslog(LOG_NOTICE,"ua server init complete");
    return ua_param;
}

void *loopWriteValue(void *param) //测试用的
{
    UA_SERVER_PARAM *var = (UA_SERVER_PARAM *)param;
    int val_int;
    int write_int = 0;
    UA_NodeId nodeId=UA_NODEID_STRING(1,"PCS01.ONLINE");//PCS01.ONLINE
    while (1)
    {
        write_int++;
        
        sleep(5);
        proto_syslog(LOG_WARNING,"run-----------------------------------------");
        set_int_to_node(var->ua_server,nodeId,write_int,0);
        proto_syslog(LOG_WARNING,"run-----------------------------------------1");
        sleep(5);
        proto_syslog(LOG_WARNING,"run-----------------------------------------2");
        get_a_value_int(var->ua_server,nodeId,&val_int);
        proto_syslog(LOG_WARNING,"run-----------------------------------------3");
    }
}
#if 0
void *UAclient_thread(void *param)
{
    UA_SERVER_PARAM *ua_param = (UA_SERVER_PARAM *)param;
    while(1)
    {
        sleep(1);
        UA_EndpointDescription *endpointRegister = getRegisterEndpointFromServer(discovery_url); 
        // UA_free(discovery_url);
        if(endpointRegister == NULL || endpointRegister->securityMode == UA_MESSAGESECURITYMODE_INVALID) {
            proto_syslog(LOG_ERR,
                        "Could not find any suitable endpoints on discovery server");
                        continue;
        }
        UA_Client *clientRegister = getRegisterClient(endpointRegister);
        if(!clientRegister) {
            proto_syslog(LOG_ERR,
                        "Could not create the client for remote registering");
                        continue;
        }

        /* Connect the client */
        char *endpointUrl = (char*)UA_malloc(endpointRegister->endpointUrl.length + 1);
        memcpy(endpointUrl, endpointRegister->endpointUrl.data, endpointRegister->endpointUrl.length);
        endpointUrl[endpointRegister->endpointUrl.length] = 0;
        UA_EndpointDescription_delete(endpointRegister);
        UA_StatusCode retval = UA_Server_addPeriodicServerRegisterCallback(ua_param->ua_server, clientRegister, endpointUrl,
                                                            1 * 60 * 1000, 500, NULL);
        if(retval != UA_STATUSCODE_GOOD) {
            proto_syslog(LOG_ERR,
                        "Could not create periodic job for server register. StatusCode %s",
                        UA_StatusCode_name(retval));

        }

    }
}

#endif       

int iter_func(struct hash_intptr *hash_p, int unknow, void *param)
{
    UA_SERVER_PARAM *ua_param = (UA_SERVER_PARAM *)param;
    ua_clinet_info *ua_client_i = hash_p->hh_ptr;
    if(ua_client_i==NULL)
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,"ua_client_i is NULL");
        return 0;
    }

    UA_Client *clientRegister = ua_client_i->clientRegister;
    
    UA_SecureChannelState i;
    UA_SessionState j;
    UA_StatusCode k;
    proto_syslog(LOG_INFO, "iter_func:%.*s\n",(int)(hash_p->hh_keylen),hash_p->hh_key);
    if(ua_client_i->valid == 1) //需要创建
    { 
        if(clientRegister == NULL)
        {
            clientRegister = getRegisterClient(ua_param);
            if(!clientRegister) {
                proto_syslog(LOG_ERR,
                            "Could not create the client for remote registering");
                            return 0;
            }
            
            ua_client_i->clientRegister = clientRegister;
        }
        if(ua_client_i->cb_id == 0)
        {
            // UA_EndpointDescription *endpointRegister = getRegisterEndpointFromServer(hash_p->hh_key); 
            // // UA_free(discovery_url);
            // if(endpointRegister == NULL || endpointRegister->securityMode == UA_MESSAGESECURITYMODE_INVALID) {
            //     proto_syslog(LOG_ERR,
            //                 "Could not find any suitable endpoints on discovery server");
            //                 return;
            // }
            // char regs_url[256] = {0};
            // memcpy(regs_url, endpointRegister->endpointUrl.data, endpointRegister->endpointUrl.length);
            // regs_url[endpointRegister->endpointUrl.length] = 0;
            // UA_EndpointDescription_delete(endpointRegister);

            /* Connect the client */ 
            UA_StatusCode retval = UA_Server_addPeriodicServerRegisterCallback(ua_param->ua_server, clientRegister, (char*)hash_p->hh_key,
                                                                1 *60 * 1000, 500, &ua_client_i->cb_id);
            if(retval != UA_STATUSCODE_GOOD) {
                proto_syslog(LOG_ERR,
                            "Could not create periodic job for server register. StatusCode %s",
                            UA_StatusCode_name(retval));
            }
        }
        else
        {
            UA_Client_getState(clientRegister,&i,&j,&k);

            const char *channelStateText = channelStateTexts[i];
            const char *sessionStateText = sessionStateTexts[j];
            const char *connectStatusText = UA_StatusCode_name(k);
            proto_syslog(LOG_INFO, "nUA_SecureChannelState:%s,UA_SessionState:%s,UA_StatusCode:%s\n",channelStateText,sessionStateText,connectStatusText);
            if(k!=UA_STATUSCODE_GOOD)
            {
                proto_syslog(LOG_WARNING,"remove client!");
                UA_Client_disconnect(clientRegister);
                UA_Server_removeCallback(ua_param->ua_server, ua_client_i->cb_id);
                // UA_Client_delete( ua_client_i->clientRegister);
                // ua_client_i->clientRegister=NULL;
                ua_client_i->valid = 0;
                ua_client_i->cb_id = 0;
            }
        }
    }
    else{ //需要删除
            if( (ua_client_i->clientRegister == NULL)||(ua_client_i->cb_id==0))
                return 0;
            proto_syslog(LOG_ERR,"remove client!");
            UA_Client_disconnect(clientRegister);
            UA_Server_removeCallback(ua_param->ua_server, ua_client_i->cb_id);
            // ua_client_i->clientRegister=NULL;
            ua_client_i->cb_id = 0;
    }
    return 0;

}

int iter_rm_send_mul(struct hash_intptr *hash_p, int unknow, void *param)
{
    UA_SERVER_PARAM *ua_param = (UA_SERVER_PARAM *)param;
    ua_clinet_info *ua_client_i = hash_p->hh_ptr;
    if(ua_client_i==NULL)
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,"ua_client_i is NULL");
        return 0;
    }
        

    UA_Client *clientRegister = ua_client_i->clientRegister;
    
    // UA_SecureChannelState i;
    // UA_SessionState j;
    // UA_StatusCode k;

    if(clientRegister != NULL)
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,"unregister server:%s",hash_p->hh_key);
        UA_Server_unregister_discovery(ua_param->ua_server,clientRegister);
    }
    else
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,"client connect to :%s is null",hash_p->hh_key);
    }
    // UA_SERVER_PARAM *ua_param = (UA_SERVER_PARAM *)param;
    // UA_Server *server = ua_param->ua_server; 
    // UA_String hostname = UA_STRING_NULL;
    // UA_String path = UA_STRING_NULL;
    // UA_UInt16 port = 0;

    // UA_ServerConfig *config = UA_Server_getConfig(server);

    // UA_String url = UA_String_fromChars(hash_p->hh_key);;

    // UA_StatusCode retval = UA_parseEndpointUrl(&url, &hostname,
    //                                             &port, &path);
    // UA_Discovery_removeRecord(server, &config->mdnsConfig.mdnsServerName,
    //                             &hostname, port, true);
    return 0;
}

void *UAServer_thread(void *param)
{
    // pthread_detach(pthread_self());
    UA_SERVER_PARAM *ua_param = (UA_SERVER_PARAM *)param;
    proto_syslog(LOG_NOTICE, "UA_Server Starting running\n");
    #if 0
    // UA_QualifiedName ackedStateField = UA_QUALIFIEDNAME(0,"AckedState");
    long long num = 0;
    pthread_run = true;
    while(running)
    {   
        if(num%50 == 0)
        {
            proto_syslog(LOG_INFO, "hash_intptr_count(ua_param->discover_hash):%d\n",hash_intptr_count(ua_param->discover_hash));
            hash_intptr_iter(&ua_param->discover_hash,iter_func,param);
            //isTwoStateVariableInTrueState(ua_param->ua_server,&ua_param->alarm_info[SOURC_TEST_1]->InstanceInfo[INSTANCE_TEST_1]->ConditionInstance,&ackedStateField);
        }
        num++;
        UA_Server_run_iterate(ua_param->ua_server, true);

    }

    hash_intptr_iter(&ua_param->discover_hash,iter_rm_send_mul,param);

    UA_Server_run_shutdown(ua_param->ua_server);
    UA_Server_delete(ua_param->ua_server);
    
    #else
    UA_StatusCode ret = UA_Server_run(ua_param->ua_server, &running);
    proto_syslog(LOG_ERR, "UA_Server Stopping %s\n", UA_StatusCode_name(ret));
    // if (ua_param->ua_server != NULL)
    // {
    //     UA_Server_delete(ua_param->ua_server);
    // }
    #endif
    while(1)
    {
        for (int j = 0; j < 60; j++)
        {
            for (int i = 0; i < 10; i ++)
            {
                proto_syslog(LOG_ERR, "UA_Server EXIT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1");
                proto_syslog(LOG_ERR, "UA_Server EXIT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1");
                proto_syslog(LOG_ERR, "UA_Server EXIT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1");
                proto_syslog(LOG_ERR, "UA_Server EXIT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1");
                proto_syslog(LOG_ERR, "UA_Server EXIT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1");
            }
            sleep(1);
        }
        exit(-12345);
        
    }
    return NULL;
}


int ua_add_file(UA_Server *server,UA_NodeId parentNodeId,UA_NodeId *NodeId,char *name,char*displayName)
{
    // char thistnodeid[256] = {0};
    int ret = 0;
    UA_ObjectAttributes object_attr1 = UA_ObjectAttributes_default;
    object_attr1.description = UA_LOCALIZEDTEXT("zh_CN", name);
    object_attr1.displayName = UA_LOCALIZEDTEXT("zh_CN", displayName);
    UA_Server_addObjectNode(server, ua_user_string_2_node_id(name),parentNodeId,
                            UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
                            UA_QUALIFIEDNAME(1, name),
                            UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE), object_attr1, NULL, NodeId);
    return ret;
}

void add_float_to_node(UA_Server *server,UA_NodeId parentNodeId,UA_NodeId *NodeId,char *name,char*displayName)
{
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    UA_Double myvalue = 0.0f;
    UA_NodeId_init(NodeId);
    UA_Variant_setScalar(&attr.value, &myvalue, &UA_TYPES[UA_TYPES_DOUBLE]);
    // attr.description = UA_LOCALIZEDTEXT("zh_CN",displayName);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN",displayName);
    attr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ;

    UA_QualifiedName mydataName = UA_QUALIFIEDNAME(1, name);
 
    UA_Server_addVariableNode(server, UA_NODEID_STRING(1, name), parentNodeId,
                              parentReferenceNodeId, mydataName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NodeId);

}
static UA_StatusCode
total_write_cb(UA_Server *server,
               const UA_NodeId *sessionId, void *sessionContext,
               const UA_NodeId *nodeId, void *nodeContext,
               const UA_NumericRange *range, const UA_DataValue *data) {
                
                total_cb_t *total_cb = nodeContext;
                if (sessionId->identifierType == UA_NODEIDTYPE_GUID && sessionId->identifier.guid.data1 == 1)
                {
                    //   proto_syslog(LOG_WARNING,
                    //     "server write");
                    // proto_syslog(LOG_DEBUG, "server write");
                    return UA_STATUSCODE_GOOD;
                }
                if (total_cb->onWrite==NULL)
                    return UA_STATUSCODE_BAD;
                char name[64] = {0};
                size_t node_name_len = GET_STRING_LEN_BY_NODEID(nodeId);
                snprintf(name, sizeof(name), "%.*s", (int)((node_name_len < sizeof(name) - 1) ? node_name_len : (sizeof(name) - 1)), GET_STRING_BY_NODEID(nodeId));
                UA_StatusCode ret = UA_STATUSCODE_BAD;
                char addtmp[2048] = {0};
                switch(total_cb->data_type)
                {
                    case 0:
                    {
                        snprintf(addtmp, sizeof(addtmp), "%s,%d", name, *(UA_Int32*) data->value.data);
                        ret =(total_cb->onWrite(name,data->value.data) == 0)?UA_STATUSCODE_GOOD:UA_STATUSCODE_BAD;
                    };break;
                    case 2:
                    {
                        size_t value_len = ((UA_String*)(data->value.data))->length;
                        snprintf(addtmp, sizeof(addtmp), "%s,%.*s", name, (int)((value_len < (sizeof(addtmp) - strlen(name) - 2)) ? value_len : (sizeof(addtmp) - strlen(name) - 2)), (char *)(*(UA_String*)(data->value.data)).data);
                        char value_string[128] = {0};
                        snprintf(value_string, sizeof(value_string), "%.*s", (int)((value_len < sizeof(value_string) - 1) ? value_len : (sizeof(value_string) - 1)), (char *)(*(UA_String*)(data->value.data)).data);
                        ret =(total_cb->onWrite(name,value_string) == 0)?UA_STATUSCODE_GOOD:UA_STATUSCODE_BAD;
                    };break;
                    default:
                        break;
                }
                server_add_new_log(LOG_CODE_OPC_CLIENT_WTIRE,"EMS",LOG_MODE_TRIG,1,addtmp);
                return ret;
}


static UA_StatusCode
total_read_cb(UA_Server *server,
                const UA_NodeId *sessionId, void *sessionContext,
                const UA_NodeId *nodeId, void *nodeContext,
                UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
                UA_DataValue *dataValue) {
    if (sessionId->identifierType == UA_NODEIDTYPE_GUID && sessionId->identifier.guid.data1 == 1)
    {
        //   proto_syslog(LOG_WARNING,
        //     "server read");
        // proto_syslog(LOG_DEBUG, "server read");
        return UA_STATUSCODE_GOOD;
    }

    total_cb_t *total_cb = nodeContext;


    dataValue->hasValue = true;
    dataValue->hasStatus = true;
    
    char name[64] = {0};
    size_t node_name_len = GET_STRING_LEN_BY_NODEID(nodeId);
    snprintf(name, sizeof(name), "%.*s", (int)((node_name_len < sizeof(name) - 1) ? node_name_len : (sizeof(name) - 1)), GET_STRING_BY_NODEID(nodeId));
    if (total_cb->onRead==NULL)
        return UA_STATUSCODE_BAD;

    char data_tmp[256] = {0};
    dataValue->status = (total_cb->onRead(name,(void *)data_tmp) == 0)?UA_STATUSCODE_GOOD:UA_STATUSCODE_BAD;
    switch(total_cb->data_type)
    {
        case 0:
        {
            UA_Variant_setScalarCopy(&dataValue->value, data_tmp,&UA_TYPES[UA_TYPES_INT32]);

        };break;
        case 2:
        { 
            UA_String ua_result = UA_String_fromChars(data_tmp);
            UA_Variant_setScalarCopy(&dataValue->value, &ua_result,&UA_TYPES[UA_TYPES_STRING]);
            UA_String_clear(&ua_result);
        };break;
        default:
            break;
    }
    return UA_STATUSCODE_GOOD;
}

int add_int_to_node(UA_Server *server,UA_NodeId parentNodeId,int readonly,UA_NodeId *NodeId,char *name,char*displayName,
               total_cb_t *total_cb)
{
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    UA_NodeId n_id;
    UA_NodeId *p_id = NodeId;
    if (NodeId == NULL)
    {
        NodeId = &n_id;
    }
    UA_NodeId_init(NodeId);
    // attr.description = UA_LOCALIZEDTEXT("zh_CN",displayName);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN",displayName);
    attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ;
    if(!readonly)
        attr.accessLevel |= UA_ACCESSLEVELMASK_WRITE;

    UA_QualifiedName mydataName = UA_QUALIFIEDNAME(1, name);
 
    UA_StatusCode ret = UA_Server_addVariableNode(server, UA_NODEID_STRING(1, name), parentNodeId,
                              parentReferenceNodeId, mydataName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, total_cb, NodeId);
    if (ret != UA_STATUSCODE_GOOD)
    {
        proto_syslog(LOG_WARNING, " UA_Server_addVariableNode err %s[%s]", name, UA_StatusCode_name(ret));
        return -1;
    }

    if((total_cb->onRead!=NULL)||total_cb->onWrite!=NULL)
    {
        total_cb->data_type = 0;
        UA_DataSource varDataSource;
        varDataSource.read = total_read_cb;
        varDataSource.write = total_write_cb;
        UA_StatusCode retval = UA_Server_setVariableNode_dataSource(server, *NodeId, varDataSource);
        if (retval != UA_STATUSCODE_GOOD) {                        
        proto_syslog(LOG_WARNING,
                " setVariableNode %s[%s]", name, UA_StatusCode_name(retval));
        return -1;
    }
        // UA_ValueCallback callback;
        // callback.onWrite = total_write_cb;//ua写缓存数据之后回调
        // UA_Server_setVariableNode_valueCallback(server, *NodeId, callback);
    }
    if (p_id == NULL)
    {
        UA_NodeId_clear(&n_id);
    }
    return 0;
}

int add_alarm_num_to_node(proto_forward_t *var, device_t *dev, char *node_name, char *node_disp_name,
               total_cb_t *total_cb)
{
    UA_SERVER_PARAM *ua_ser_para = var->ua_server_param;
    UA_Server *server = ua_ser_para->ua_server;

    UA_VariableAttributes attr = UA_VariableAttributes_default;

    char dev_tag_name[DEV_NO_LEN + TAG_NAME_LEN] = {0};
    snprintf(dev_tag_name, sizeof(dev_tag_name), "%s.%s", dev->no, node_name);

    attr.description = UA_LOCALIZEDTEXT("zh_CN", node_name);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN", node_disp_name);
    attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ;

    UA_NodeId *parent_note_id = &dev->child_nod.state_node_id;
    UA_NodeId NodeId;
    UA_NodeId_init(&NodeId);
    UA_Server_addVariableNode(server, UA_NODEID_STRING(1, dev_tag_name), *parent_note_id,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), UA_QUALIFIEDNAME(1, node_name),
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, /*timeDataSource,*/ total_cb, &NodeId);

    if((total_cb->onRead!=NULL)||total_cb->onWrite!=NULL)
    {
        total_cb->data_type = 0;
        UA_DataSource varDataSource;
        varDataSource.read = total_read_cb;
        varDataSource.write = total_write_cb;
        UA_StatusCode retval = UA_Server_setVariableNode_dataSource(server, NodeId, varDataSource);
        if (retval != UA_STATUSCODE_GOOD) {                        
        proto_syslog(LOG_WARNING,
                " setVariableNode[%s] %s", node_name, UA_StatusCode_name(retval));
                return -1;
        }
    }
    UA_NodeId_clear(&NodeId);
    return 0;
}


// typedef struct UA_DateTimeStruct {
//     UA_UInt16 nanoSec;
//     UA_UInt16 microSec;
//     UA_UInt16 milliSec;
//     UA_UInt16 sec;
//     UA_UInt16 min;
//     UA_UInt16 hour;
//     UA_UInt16 day;   /* From 1 to 31 */
//     UA_UInt16 month; /* From 1 to 12 */
//     UA_Int16 year;   /* Can be negative (BC) */
// } UA_DateTimeStruct;

// UA_DateTimeStruct UA_EXPORT UA_DateTime_toStruct(UA_DateTime t);
// UA_DateTime UA_EXPORT UA_DateTime_fromStruct(UA_DateTimeStruct ts);


#define DEFAULT_HW_RTC "/dev/rtc0"
int UA_SetHardwareRtc(UA_DateTimeStruct *t_tm)
{
    int fd, retval = 0;
    struct rtc_time rtc_tm;
    rtc_tm.tm_sec =  t_tm->sec;
    rtc_tm.tm_min =  t_tm->min;
    rtc_tm.tm_hour = t_tm->hour;
    rtc_tm.tm_mday = t_tm->day;
    rtc_tm.tm_mon =  t_tm->month-1;
    rtc_tm.tm_year = t_tm->year-1900;
    // rtc_tm.tm_wday = t_tm->tm_wday;
    // rtc_tm.tm_yday = t_tm->tm_yday;
    // rtc_tm.tm_isdst = t_tm->tm_isdst;
        proto_syslog(LOG_WARNING,"write RTC date/time is %d-%d-%d,%02d:%02d:%02d.",
            rtc_tm.tm_mday, rtc_tm.tm_mon+1, rtc_tm.tm_year+1900,
            rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
    fd = open (DEFAULT_HW_RTC, O_RDONLY);
    if (fd == -1) {
        perror(DEFAULT_HW_RTC);
        return -1;
    }
    /* Write the RTC time/date */
    retval = ioctl(fd, RTC_SET_TIME, &rtc_tm);
    if (retval == -1) {
        perror("write rtc");
        close(fd);
        return -1;
    }
    close(fd);

    fd = open (DEFAULT_HW_RTC, O_RDONLY);
    if (fd == -1) {
        perror(DEFAULT_HW_RTC);
        return -1;
    }
    /* Read the RTC time/date */
    retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
    if (retval == -1) {
        perror("read rtc");
        close(fd);
        return -1;
    }
    proto_syslog(LOG_WARNING,"Current RTC date/time is %d-%d-%d,%02d:%02d:%02d.",
            rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
            rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
    close(fd);
    struct tm rtc,rtc2;
    rtc2.tm_sec = t_tm->sec;
    rtc2.tm_min = t_tm->min;
    rtc2.tm_hour =t_tm->hour;
    rtc2.tm_mday =t_tm->day;
    rtc2.tm_mon = t_tm->month-1;
    rtc2.tm_year =t_tm->year-1900;

    rtc.tm_sec = rtc_tm.tm_sec;
    rtc.tm_min = rtc_tm.tm_min;
    rtc.tm_hour = rtc_tm.tm_hour;
    rtc.tm_mday = rtc_tm.tm_mday;
    rtc.tm_mon = rtc_tm.tm_mon;
    rtc.tm_year = rtc_tm.tm_year;
    // rtc.tm_wday = rtc_tm.tm_wday;
    // rtc.tm_yday = rtc_tm.tm_yday;
    // rtc.tm_isdst = rtc_tm.tm_isdst;
    if(abs(mktime(&rtc) - mktime(&rtc2)) > 10)//大于10S意味着有问题,拒绝自动校时
    {
        proto_syslog(LOG_WARNING,"Update failed, difference too much ,need less than 10S");
        return -1;
    }
    cmd_call("hwclock -s -u",5,NULL,0);
    return 0;
}

int UA_SetHardwareRtc2(UA_DateTimeStruct *t_tm)
{
    char command[128] = {0};

    sprintf(command,"date -s \"%d-%d-%d %d:%d:%d\"",t_tm->year,t_tm->month,t_tm->day,t_tm->hour,t_tm->min,t_tm->sec);
    proto_syslog(LOG_WARNING,"time set command :%s",command);

    cmd_call(command,5,NULL,0);
    cmd_call("hwclock -w",5,NULL,0);
    return 0;
}

// static UA_StatusCode
// time_write_cb(UA_Server *server,
//                  const UA_NodeId *sessionId, void *sessionContext,
//                  const UA_NodeId *nodeId, void *nodeContext,
//                  const UA_NumericRange *range, const UA_DataValue *data) {
//     UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
//                 "Changing the system time is not implemented");
//     return UA_STATUSCODE_BADINTERNALERROR;
// }

static UA_StatusCode
time_write_cb(UA_Server *server,
               const UA_NodeId *sessionId, void *sessionContext,
               const UA_NodeId *nodeId, void *nodeContext,
               const UA_NumericRange *range, const UA_DataValue *data) {
                
                if (sessionId->identifierType == UA_NODEIDTYPE_GUID && sessionId->identifier.guid.data1 == 1)
                {
                    //   proto_syslog(LOG_WARNING,
                    //     "server write");
                    // proto_syslog(LOG_DEBUG, "server write");
                    return UA_STATUSCODE_GOOD;
                }
                UA_DateTimeStruct now = UA_DateTime_toStruct((*(UA_DateTime*)data->value.data) - UA_DateTime_localTimeUtcOffset());
                // proto_syslog(LOG_WARNING,"timestamp:%"PRId64,now);
                //struct tm *t_tm = gmtime((const time_t *)&now);
                UA_SetHardwareRtc(&now);
                server_add_new_log(LOG_CODE_OPC_SET_TIMING,"EMS",LOG_MODE_TRIG,1,NULL);
                return UA_STATUSCODE_GOOD;
}

static UA_StatusCode
time_read_cb(UA_Server *server,
                const UA_NodeId *sessionId, void *sessionContext,
                const UA_NodeId *nodeId, void *nodeContext,
                UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
                UA_DataValue *dataValue) {

    if (sessionId->identifierType == UA_NODEIDTYPE_GUID && sessionId->identifier.guid.data1 == 1)
    {
        //   proto_syslog(LOG_WARNING,
        //     "server read");
        // proto_syslog(LOG_DEBUG, "server read");
        return UA_STATUSCODE_GOOD;
    }

    UA_DateTime now = UA_DateTime_now() + UA_DateTime_localTimeUtcOffset();
    UA_Variant_clear(&dataValue->value);
    UA_Variant_setScalarCopy(&dataValue->value, &now,
                             &UA_TYPES[UA_TYPES_DATETIME]);
    dataValue->hasValue = true;

    return UA_STATUSCODE_GOOD;
}
void add_time_to_node(UA_Server *server,UA_NodeId parentNodeId,int readonly,UA_NodeId *NodeId,char *name,char*displayName)
{
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_VariableAttributes attr = UA_VariableAttributes_default;;
    UA_NodeId_init(NodeId);
    // attr.description = UA_LOCALIZEDTEXT("zh_CN",displayName);
    attr.displayName = UA_LOCALIZEDTEXT("zh_CN",displayName);
    attr.dataType = UA_TYPES[UA_TYPES_DATETIME].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ;
    if(!readonly)
        attr.accessLevel |= UA_ACCESSLEVELMASK_WRITE;


    UA_DataSource timeDataSource;
    timeDataSource.read = time_read_cb;
    timeDataSource.write = time_write_cb;
    UA_StatusCode retval = UA_Server_addDataSourceVariableNode(server, UA_NODEID_STRING(1, name), parentNodeId,
                                        parentReferenceNodeId, UA_QUALIFIEDNAME(1, name),
                                        UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr,
                                        timeDataSource, NULL, NodeId);

        if (retval != UA_STATUSCODE_GOOD) {                        
        proto_syslog(LOG_WARNING,
                " setVariableNode %s",UA_StatusCode_name(retval));
        // UA_ValueCallback callback;
        // callback.onWrite = total_write_cb;//ua写缓存数据之后回调
        // UA_Server_setVariableNode_valueCallback(server, *NodeId, callback);
    }
}

// void *ua_test_cycle(void * arg)
// {
//     UA_SERVER_PARAM *param = (UA_SERVER_PARAM *)arg;
//     while(1)
//     {
//         isTwoStateVariableInTrueState(param->ua_server,&param->alarm_info[SOURC_TEST_1]->ConditionSource,&param->alarm_info[SOURC_TEST_1]->InstanceInfo[INSTANCE_TEST_1]->ConditionInstance);
//         sleep(2);
//     }
// }

#define MAX_ARGS_NUM 3
static UA_StatusCode MethodCallback(UA_Server *server,
               const UA_NodeId *sessionId, void *sessionHandle,
               const UA_NodeId *methodId, void *methodContext,
               const UA_NodeId *objectId, void *objectContext,
               size_t inputSize, const UA_Variant *input,
               size_t outputSize, UA_Variant *output)
{
    int ret = UA_STATUSCODE_GOOD;
    if (methodContext == NULL)
    {
        proto_syslog(LOG_NOTICE, "method cb is NULL");

        return UA_STATUSCODE_BAD;
    }
    ua_cb method = methodContext;
    function_data input_data[inputSize];
	size_t pd_size = inputSize;

    for (size_t i = 0; i < inputSize; i++) {
		UA_String *pdata = (UA_String *)(input[i].data);
		if (pdata == NULL || pdata->data == NULL || pdata->length <= 0) {
			pd_size = 0;
			break;
		}

		input_data[i].data = calloc(pdata->length + 1, 1);
		if (input_data[i].data == NULL) {
			ret = UA_STATUSCODE_BADOUTOFMEMORY;
			pd_size = 0;
			break;
		}

		memcpy(input_data[i].data, pdata->data, pdata->length);
		input_data[i].data_len = pdata->length;
    }

    function_data output_data[MAX_ARGS_NUM];
    int outputsize_t = 0;
    if ((ret == UA_STATUSCODE_GOOD) &&
		(method(input_data, pd_size, output_data, &outputsize_t) < 0))
        ret = UA_STATUSCODE_BAD;

    outputSize = outputsize_t;
    for (size_t i = 0; i < outputSize; i++)
    {
        output_data[i].data[output_data[i].data_len] = '\0';
        UA_String message = UA_STRING(output_data[i].data);
        UA_StatusCode retval = UA_Variant_setScalarCopy(&output[i], &message, &UA_TYPES[UA_TYPES_STRING]);
        if (retval != UA_STATUSCODE_GOOD)
        {
        ret = UA_STATUSCODE_BAD;
        break;
        }
        if (output_data[i].data != NULL)
        {
        free(output_data[i].data);
        output_data[i].data = NULL;
        }
    }

	for (size_t i = 0; i < inputSize; i++) {
		if (input_data[i].data) {
			free(input_data[i].data);
			input_data[i].data = NULL;
		}
	}
    return ret;
}

int addMethodToNode(UA_SERVER_PARAM *_var, UA_NodeId parent_noteid, int argInNum, char *argsNameIn[], int argOutNum, char *argsNameOut[], char *functionId, char *functionName, ua_cb method)
{

    UA_SERVER_PARAM *ua_param = _var;
    if (ua_param == NULL)
        return -1;
    UA_Server *server = ua_param->ua_server;
    if (argOutNum > MAX_ARGS_NUM)
    {
        proto_syslog(LOG_WARNING,
                     "argOutNum is to large , you can Increment macro MAX_ARGS_NUM");
        return -1;
    }

    UA_Argument inputArgs[argInNum];
    for (size_t i = 0; i < argInNum; i++)
    {
        createUA_Argment_Scalar(&inputArgs[i], argsNameIn[i], UA_TYPES_STRING, argsNameIn[i]);
    }

    UA_Argument outputArgs[argOutNum];
    for (size_t i = 0; i < argOutNum; i++)
    {
        createUA_Argment_Scalar(&outputArgs[i], argsNameOut[i], UA_TYPES_STRING, argsNameOut[i]);
    }

    UA_MethodAttributes Attr = UA_MethodAttributes_default;
    Attr.description = UA_LOCALIZEDTEXT("zh_CN", functionName);
    Attr.displayName = UA_LOCALIZEDTEXT("zh_CN", functionName);
    Attr.executable = true;
    Attr.userExecutable = true;
    UA_StatusCode retval = UA_Server_addMethodNode(server, UA_NODEID_STRING(1, functionId),
                                                   parent_noteid,
                                                   UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                                                   UA_QUALIFIEDNAME(1, functionName),
                                                   Attr, MethodCallback,
                                                   argInNum, inputArgs, argOutNum, outputArgs, method, NULL);
    if (retval != UA_STATUSCODE_GOOD)
    {
        proto_syslog(LOG_WARNING,
                     "method:%s  UA_Server_addMethodNode %s", functionName, UA_StatusCode_name(retval));
        return -1;
    }
    return 0;
}

void ua_server_thread_start(UA_SERVER_PARAM *ua_param)
{
    // pthread_mutex_init(&ua_param->mutex,NULL);
    if (ua_param&&ua_param->ua_server)
    {
        if (0 == pthread_create(&UAServer_thread_id, NULL, (void *)UAServer_thread, ua_param))
        {
            pthread_setname_np(UAServer_thread_id, "UAServerthread");
        }
    }
    //测试线程
    // pthread_t tid;
    // pthread_create(&tid, NULL, (void *)loopWriteValue, ua_param);
    
}

void ua_server_thread_stop(UA_SERVER_PARAM *ua_param)
{
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "stop Ua");
    if (ua_param && ua_param->ua_server && UAServer_thread_id)
    {
        UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "stop Ua");
        if (pthread_run == false)
            return;
        int kill_rc = pthread_kill(UAServer_thread_id, 0);
        if (kill_rc == ESRCH)
            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "the specified thread did not exists or already quit\n");
        else if (kill_rc == EINVAL)
            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "signal is invalid\n");
        else
        {
            UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "the specified thread is alive\n");
            running = false;
            pthread_join(UAServer_thread_id, NULL);
            proto_syslog(LOG_NOTICE, "ua_server thread thread kill success");
        }
    }
}
