#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdint.h>
#include "hal_thread.h"
#include "hal_time.h"
#include "iec104_server.h"
#include "../proto_forward.h"
#include "../ems/jsonct.h"
#include "../log_module/log_module.h"
#include "../cc_lc_com/define.h"
#include "../business_log/business_log.h"
static bool running = true;
void change_report(void *context,tag_t *tag);
typedef struct {
    volatile uint32_t request_cnt;
    volatile uint32_t opened_cnt;
    volatile uint32_t closed_cnt;
    volatile uint32_t activated_cnt;
    volatile uint32_t deactivated_cnt;
    volatile int32_t open_now;
    volatile int32_t open_peak;
} iec104_conn_stats_t;

static iec104_conn_stats_t g_conn_stats = {0};
void
printCP56Time2a(CP56Time2a time)
{
    ems_syslog(LOG_INFO,"%02i:%02i:%02i %02i/%02i/%04i", CP56Time2a_getHour(time),
                             CP56Time2a_getMinute(time),
                             CP56Time2a_getSecond(time),
                             CP56Time2a_getDayOfMonth(time),
                             CP56Time2a_getMonth(time),
                             CP56Time2a_getYear(time) + 2000);
}

/* Callback handler to log sent or received messages (optional) */
// static void
// rawMessageHandler(void* parameter, IMasterConnection conneciton, uint8_t* msg, int msgSize, bool sent)
// {
//     if (sent)
//         ems_syslog(LOG_INFO,"SEND: ");
//     else
//         ems_syslog(LOG_INFO,"RCVD: ");

//     int i;
//     for (i = 0; i < msgSize; i++) {
//         ems_syslog(LOG_INFO,"%02x ", msg[i]);
//     }

//     ems_syslog(LOG_INFO,"\n");
// }


static bool
clockSyncHandler (void* parameter, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a newTime)
{
    ems_syslog(LOG_INFO,"Process time sync command with time ");
    printCP56Time2a(newTime);

    // uint64_t newSystemTimeInMs = CP56Time2a_toMsTimestamp(newTime);

    /* Set time for ACT_CON message */
    CP56Time2a_setFromMsTimestamp(newTime, Hal_getTimeInMs());

    /* update system time here */

    return true;
}

#define IEC_TYPE_MAX 3
static bool
interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi)
{
    ems_syslog(LOG_INFO, "Received interrogation for group %i ", qoi);

    if (qoi != 20) /* only handle station interrogation */
    {
        IMasterConnection_sendACT_CON(connection, asdu, true);
        return true;
    }
    iec104_info *var = parameter;

    int singlegroup = 25;
    int currue_num[IEC_TYPE_MAX] = {0}; //0 bool 1 int 2 float
#ifdef CP56_IRG
    struct sCP56Time2a cp56;
    singlegroup = 15;
    time_t dtime;
#endif

    CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection);
    IMasterConnection_sendACT_CON(connection, asdu, false);
    
    int oaddr = CS101_ASDU_getOA(asdu);
    int caddr = CS101_ASDU_getCA(asdu);
    int caidx = -1;
    for (size_t i = 0; i < var->cacnt; i++)
    {
        if (var->param[i].ca == caddr)
            caidx = i;
    }
    if (caidx < 0)
        goto end;

    int type_index = 0;
	for (size_t m = 0; m < DATA_CALSS; m++)
	{
	    if ((strcmp(var->param[caidx].point_param_arry[m]->point_type, "RemotePoint") == 0) || (strcmp(var->param[caidx].point_param_arry[m]->point_type, "RemoteMeas") == 0))
	    {
	        CS101_ASDU newAsdu[IEC_TYPE_MAX] = {NULL};
            InformationObject io[IEC_TYPE_MAX] = {NULL};
			
	        for (size_t i = 0; i < IEC_TYPE_MAX; i++)
		    {
		        newAsdu[i] = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, oaddr, var->param[caidx].ca, false, false);
		    }
			memset(currue_num, 0, sizeof(currue_num));
			
		    for (size_t i = 0; i < var->param[caidx].point_param_arry[m]->point_num; i++)
		    {
		        int val_addr = var->param[caidx].point_param_arry[m]->point_info[i].addr;
		        for (size_t j = 0; j < IEC_TYPE_MAX; j++)
		        {
		            if (currue_num[j] > singlegroup)
		            {
		                IMasterConnection_sendASDU(connection, newAsdu[j]);
		                CS101_ASDU_destroy(newAsdu[j]);
		                newAsdu[j] = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, oaddr, var->param[caidx].ca, false, false);
		                currue_num[j] = 0;
		            }
		        }

		        tag_value_t tag_val = {0};
		        if (var->param[caidx].point_param_arry[m]->point_info[i].data_type == TYPE_BOOL)
		        {
		            type_index = 0;
		            read_data_form_tag_by_p(var->param[caidx].point_param_arry[m]->point_info[i].tag, TYPE_TAG_INT, &tag_val, var->param[caidx].point_param_arry[m]->point_info[i].coefficient);
		            bool val = (tag_val.value.to_int == 0) ? false : true;
		            if (NULL == io[type_index])
		            {
#ifdef CP56_IRG
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                io[type_index]= (InformationObject)SinglePointWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#else
		                io[type_index]= (InformationObject)SinglePointInformation_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#endif          
		            }
		            else
		            {
#ifdef CP56_IRG
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    SinglePointWithCP56Time2a_create((SinglePointInformation)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    SinglePointInformation_create((SinglePointInformation)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD));
#endif
		            }
		            currue_num[type_index]++;
		        }
		        else if (var->param[caidx].point_param_arry[m]->point_info[i].data_type == TYPE_SHORT)
		        {
		            type_index = 1;
		            read_data_form_tag_by_p(var->param[caidx].point_param_arry[m]->point_info[i].tag, TYPE_TAG_INT, &tag_val, var->param[caidx].point_param_arry[m]->point_info[i].coefficient);
		            int val = tag_val.value.to_int;
		            if (NULL == io[type_index])
		            {
#ifdef CP56_IRG 
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                io[type_index]= (InformationObject)MeasuredValueScaledWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#else
		                io[type_index]= (InformationObject)MeasuredValueScaled_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#endif
		            }
		            else
		            {
#ifdef CP56_IRG
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueScaledWithCP56Time2a_create((MeasuredValueScaled)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueScaled_create((MeasuredValueScaled)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD));
#endif
		            }
		            currue_num[type_index]++;
		        }
		        else if (var->param[caidx].point_param_arry[m]->point_info[i].data_type == TYPE_USHORT)
		        {
		            type_index = 2;
		            read_data_form_tag_by_p(var->param[caidx].point_param_arry[m]->point_info[i].tag, TYPE_TAG_INT, &tag_val, var->param[caidx].point_param_arry[m]->point_info[i].coefficient);
		            float val = (float)tag_val.value.to_int;
		            if (NULL == io[type_index])
		            {
#ifdef CP56_IRG
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                io[type_index]= (InformationObject)MeasuredValueShortWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#else
		                io[type_index]= (InformationObject)MeasuredValueShort_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#endif
		            }
		            else
		            {
#ifdef CP56_IRG
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueShortWithCP56Time2a_create((MeasuredValueShort)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueShort_create((MeasuredValueShort)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD));
#endif
		            }
		            currue_num[type_index]++;
		        }
		        else if (var->param[caidx].point_param_arry[m]->point_info[i].data_type == TYPE_FLOAT)
		        {
		            type_index = 2;
		            read_data_form_tag_by_p(var->param[caidx].point_param_arry[m]->point_info[i].tag, TYPE_TAG_FLOAT, &tag_val, var->param[caidx].point_param_arry[m]->point_info[i].coefficient);
		            float val = tag_val.value.to_float;
		            if (NULL == io[type_index])
		            {
#ifdef CP56_IRG
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                io[type_index]= (InformationObject)MeasuredValueShortWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#else
		                io[type_index]= (InformationObject)MeasuredValueShort_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#endif
		            }
		            else
		            {
#ifdef CP56_IRG
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueShortWithCP56Time2a_create((MeasuredValueShort)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueShort_create((MeasuredValueShort)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD));
#endif
		            }
		            currue_num[type_index]++;
		        }
		    }

		    for (size_t i = 0; i < IEC_TYPE_MAX; i++)
		    {
		        if (currue_num[i] > 0)
		            IMasterConnection_sendASDU(connection, newAsdu[i]);
		        CS101_ASDU_destroy(newAsdu[i]);
		        if (io[i] != NULL)
		        {
		            InformationObject_destroy(io[i]);
		        }
		    }	
	    }
    }

end:
    IMasterConnection_sendACT_TERM(connection, asdu);

    return true;
}

extern int north_control;
static bool
asduHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu)
{
    CS101_CauseOfTransmission cot;
    const char *typeIdStr;
    const char *cotStr;
    int numElements;
    IEC60870_5_TypeID typeid;
    InformationObject io;
    int caddr;
    char addtmp[256] = {0};
    typeid = CS101_ASDU_getTypeID(asdu);
    caddr = CS101_ASDU_getCA(asdu);
    cot = CS101_ASDU_getCOT(asdu);
    typeIdStr = TypeID_toString(typeid);
    cotStr = CS101_CauseOfTransmission_toString(cot);
    numElements = CS101_ASDU_getNumberOfElements(asdu);

    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "Received ASDU:");
    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "Type ID: %s (%d)", typeIdStr ? typeIdStr : "unknown", typeid);
    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "Cause of Transmission: %s (%d)", cotStr ? cotStr : "unknown", cot);
    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "Common Address: %d", caddr);
    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "Information Objects:");

    // 遍历所有信息对象
    for (int i = 0; i < numElements; i++) {
        io = CS101_ASDU_getElement(asdu, i);
        if (io) {
            int ioa = InformationObject_getObjectAddress(io);
            lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "  Object %d:", i + 1);
            lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    IOA: %d", ioa);
            
            // 根据不同类型解析值
            switch (typeid) {
                case C_SC_NA_1:
                    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    Value: %d", 
                        SingleCommand_getState((SingleCommand)io) ? 1 : 0);
                    break;
                case C_DC_NA_1:
                    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    Value: %d", 
                        DoubleCommand_getState((DoubleCommand)io));
                    break;
                case M_ME_NB_1:
                    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    Value: %d",
                        MeasuredValueScaled_getValue((MeasuredValueScaled)io));
                    break;
                case M_ME_NC_1:
                    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    Value: %.4f",
                        MeasuredValueShort_getValue((MeasuredValueShort)io));
                    break;
                case M_SP_NA_1:
                    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    Value: %d",
                        SinglePointInformation_getValue((SinglePointInformation)io) ? 1 : 0);
                    break;
                case C_SE_NA_1:
                    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    Value: %lf",
                        SetpointCommandNormalized_getValue((SetpointCommandNormalized)io));
                    break;
                case C_SE_NB_1:
                    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    Value: %d",
                        SetpointCommandScaled_getValue((SetpointCommandScaled)io));
                    break;
                case C_SE_NC_1:
                    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    Value: %lf",
                        SetpointCommandShort_getValue((SetpointCommandShort)io));
                    break;
                default:
                    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "    Value: Unknown type");
            }
            InformationObject_destroy(io);
        }
    }
    lnxall_syslog_uid(LOG_DEBUG, IEC104_LOG_UID, "-----------------------------------------------------");
    io = CS101_ASDU_getElement(asdu, 0);
    if(io == NULL) {
        ems_syslog(LOG_ERR, "ERROR: message has no valid information object");
        return true;
    }
    if(CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION)
    {
        CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);
    }
    else
    {
        CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT);
        goto END;
    }
    
    iec104_info *var = parameter;
    int val_addr = InformationObject_getObjectAddress(io);

    int caidx = -1;
    for (int i = 0; i < var->cacnt; i++)
    {
        if (var->param[i].ca == caddr)
            caidx = i;
    }
	if (caidx < 0){
        ems_syslog(LOG_INFO, "error:not support caddr:%d", caddr);
		CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_CA);
        goto END;
	}
    ems_syslog(LOG_NOTICE, "set param[%d] caddr: %d", caidx, caddr);

    int w_cnt = 0;
	for (size_t m = 0; m < DATA_CALSS; m++)
	{
        if ((strcmp(var->param[caidx].point_param_arry[m]->point_type, "RemoteCtrl") == 0) || (strcmp(var->param[caidx].point_param_arry[m]->point_type, "RemoteRegu") == 0)){
            w_cnt += var->param[caidx].point_param_arry[m]->point_num;
        }
	}
    if (w_cnt == 0){
        ems_syslog(LOG_INFO, "error:caddr not support write!!!");
        goto END;
	} 

    point_info_104 *point_info_finded = NULL;	
    if (val_addr != 0 && hash_intptr_findptr(var->param[caidx].point_map_hash, &val_addr, sizeof(int), (void **)&point_info_finded) < 0)
    {
        ems_syslog(LOG_INFO, "error:not find tag addr: %d", val_addr);
		CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);
        goto END;
    }	
    ems_syslog(LOG_NOTICE, "find tag addr:%d name:%s", val_addr, point_info_finded->tag->name);
	
    tag_value_t tag_val = {0};
    switch (typeid)
    {
        case C_SE_NA_1:
        {
            if (turn_data_type(point_info_finded->data_type) == TYPE_TAG_FLOAT)
                tag_val.value.to_float = SetpointCommandNormalized_getValue((SetpointCommandNormalized)io);
            else
                tag_val.value.to_int = SetpointCommandNormalized_getValue((SetpointCommandNormalized)io);
        }
            break;
        case C_SE_NB_1:
        {
            if (turn_data_type(point_info_finded->data_type) == TYPE_TAG_FLOAT)
                tag_val.value.to_float = SetpointCommandScaled_getValue((SetpointCommandScaled)io);
            else
                tag_val.value.to_int = SetpointCommandScaled_getValue((SetpointCommandScaled)io);
        }
            break;
        case C_SE_NC_1:
        {
            if (turn_data_type(point_info_finded->data_type) == TYPE_TAG_FLOAT)
                tag_val.value.to_float = SetpointCommandShort_getValue((SetpointCommandShort)io);
            else
                tag_val.value.to_int = SetpointCommandShort_getValue((SetpointCommandShort)io);
			tag_val.type = TYPE_TAG_FLOAT;
        }
            break;
        case C_SC_NA_1:
        {
            if (turn_data_type(point_info_finded->data_type) == TYPE_TAG_FLOAT)
                tag_val.value.to_float = SingleCommand_getState((SingleCommand)io) ? 1 : 0;
            else
                tag_val.value.to_int = SingleCommand_getState((SingleCommand)io) ? 1 : 0;
        }
            break;
        default:
        {
            if(turn_data_type(point_info_finded->data_type) == TYPE_TAG_INT)
                sprintf(addtmp, "%s/:%s/%d", IEC104, point_info_finded->tag->name, (int)(tag_val.value.to_int/point_info_finded->coefficient));
            else
                snprintf(addtmp, sizeof(addtmp), "%s/:%s/%f", IEC104, point_info_finded->tag->name, tag_val.value.to_float/point_info_finded->coefficient);
            server_add_new_log(LOG_CODE_NORTH_MQTT_WRITE_POINT, "EMS", LOG_MODE_TRIG, 2, addtmp);
            CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_TYPE_ID);
			goto END;
        }
            break;
    }
	InformationObject_destroy(io);
    ems_syslog(LOG_NOTICE, "tag_val type:%d to_int:%ld to_float:%f", tag_val.type, tag_val.value.to_int, tag_val.value.to_float);
	
    if(north_control == 0)
    {
        write_data_to_tag_by_p(point_info_finded->tag, &tag_val, point_info_finded->coefficient);
        if(turn_data_type(point_info_finded->data_type) == TYPE_TAG_INT)
            sprintf(addtmp, "%s/:%s/%d", IEC104, point_info_finded->tag->name, (int)(tag_val.value.to_int/point_info_finded->coefficient));
        else
            snprintf(addtmp, sizeof(addtmp), "%s/:%s/%f", IEC104, point_info_finded->tag->name, tag_val.value.to_float/point_info_finded->coefficient);
        if((strcmp(point_info_finded->tag->name,IEC104_HEARTBEAT) == 0) || (strcmp(point_info_finded->tag->name,MODBUSTCP_HEARTBEAT) == 0)) //心跳切换不记录
        {
            //不处理
        }
        else
            server_add_new_log(LOG_CODE_NORTH_MQTT_WRITE_POINT, ((device_t*)point_info_finded->tag->dev_ptr)->no, LOG_MODE_TRIG, 1, addtmp);
    }

END:
    IMasterConnection_sendASDU(connection, asdu);
    return true;
}

static bool
connectionRequestHandler(void* parameter, const char* ipAddress)
{
    __sync_add_and_fetch(&g_conn_stats.request_cnt, 1);
    ems_syslog(LOG_NOTICE,"New connection request from %s\n", ipAddress);

#if 0
    if (strcmp(ipAddress, "127.0.0.1") == 0) {
        ems_syslog(LOG_INFO,"Accept connection\n");
        return true;
    }
    else {
        ems_syslog(LOG_INFO,"Deny connection\n");
        return false;
    }
#else
    return true;
#endif
}

static void
connectionEventHandler(void* parameter, IMasterConnection con, CS104_PeerConnectionEvent event)
{
    if (event == CS104_CON_EVENT_CONNECTION_OPENED) {
        int32_t open_now = __sync_add_and_fetch(&g_conn_stats.open_now, 1);
        int32_t peak = g_conn_stats.open_peak;
        while ((open_now > peak) &&
               !__sync_bool_compare_and_swap(&g_conn_stats.open_peak, peak, open_now)) {
            peak = g_conn_stats.open_peak;
        }
        __sync_add_and_fetch(&g_conn_stats.opened_cnt, 1);
        ems_syslog(LOG_NOTICE,"Connection opened (%p)\n", con);
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0034, NULL, "[北向]:IEC104 Connection opened");
    }
    else if (event == CS104_CON_EVENT_CONNECTION_CLOSED) {
        int32_t open_now = __sync_sub_and_fetch(&g_conn_stats.open_now, 1);
        if (open_now < 0) {
            __sync_lock_test_and_set(&g_conn_stats.open_now, 0);
        }
        __sync_add_and_fetch(&g_conn_stats.closed_cnt, 1);
        ems_syslog(LOG_NOTICE,"Connection closed (%p)\n", con);
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0034, NULL, "[北向]:IEC104 Connection closed");
    }
    else if (event == CS104_CON_EVENT_ACTIVATED) {
        __sync_add_and_fetch(&g_conn_stats.activated_cnt, 1);
        ems_syslog(LOG_NOTICE,"Connection activated (%p)\n", con);
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0034, NULL, "[北向]:IEC104 Connection activated");
    }
    else if (event == CS104_CON_EVENT_DEACTIVATED) {
        __sync_add_and_fetch(&g_conn_stats.deactivated_cnt, 1);
        ems_syslog(LOG_NOTICE,"Connection deactivated (%p)\n", con);
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0034, NULL, "[北向]:IEC104 Connection deactivated");
    }
    ems_syslog(LOG_ERR,
        "ConnStats req=%u open=%u close=%u act=%u deact=%u open_now=%d peak=%d",
        (unsigned int)g_conn_stats.request_cnt,
        (unsigned int)g_conn_stats.opened_cnt,
        (unsigned int)g_conn_stats.closed_cnt,
        (unsigned int)g_conn_stats.activated_cnt,
        (unsigned int)g_conn_stats.deactivated_cnt,
        (int)g_conn_stats.open_now,
        (int)g_conn_stats.open_peak);
}

void register_iec_change_report_cb(iec104_info *var)
{
    proto_forward_t *proto_forward_var = get_proto_forward_var();
    for (int i = 0; i < var->cacnt; i++)
    {
    	for (size_t j = 0; j < DATA_CALSS; j++)
	    {
		    if ((strcmp(var->param[i].point_param_arry[j]->point_type, "RemotePoint") == 0) || (strcmp(var->param[i].point_param_arry[j]->point_type, "RemoteMeas") == 0))
		    {
		        for (size_t m = 0; m < var->param[i].point_param_arry[j]->point_num; m++)
		        {
		            if (var->param[i].point_param_arry[j]->point_info[m].changeReport)
		            {
		                if(register_data_collect_cb_by_tag(proto_forward_var, var->param[i].point_param_arry[j]->point_info[m].tag, change_report, var) < 0)
		                    ems_syslog(LOG_ERR, "iec 104 regist connect cb err tag name:%s\n", var->param[i].point_param_arry[j]->point_info[m].tag->name);
		            }
		        }
		    }
    	}
    }
}


bool check_change(tag_t *data_value)
{
    if(data_value->data_type == TYPE_TAG_INT)
    {
        if(data_value->read_cache.to_int == data_value->last_read_cache.to_int)
            return false;
    }
    else 
    {
        if(data_value->read_cache.to_float == data_value->last_read_cache.to_float)
            return false;
    }
    return true;
}

void change_report(void *context, tag_t *tag)
{
    iec104_info *var = context;
    if (!check_change(tag)){
        return;
    }
	if (var->slave == NULL){
		return;
    }
    if (CS104_Slave_isRunning(var->slave) == false){
        return;
    }
    if (CS104_Slave_getOpenConnections(var->slave) <= 0){
        return;
    }
	
    ems_syslog(LOG_NOTICE,"take change report!");
	
    CS101_ASDU newAsdu = NULL;
    InformationObject io = NULL;
    CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(var->slave);

#ifdef CP56_SPON
    struct sCP56Time2a cp56;
#endif
    
    point_info_104 *pointInfo = tag->some_pr[TAG_EX_PR_INDEX];
    if(pointInfo != NULL)
    {
        struct point_param_t *point_class = (struct point_param_t *)pointInfo->ptr;
	    struct iec104_param_t *iec104_param	= (struct iec104_param_t *)point_class->ptr;

        newAsdu = CS101_ASDU_create(alParams, false,
                                       CS101_COT_SPONTANEOUS, 0, iec104_param->ca, false, false);
        tag_value_t tag_val = {0};
        int val_addr = pointInfo->addr;
        if (pointInfo->data_type == TYPE_BOOL)
        {
            read_data_form_tag_by_p(pointInfo->tag, TYPE_TAG_INT, &tag_val, pointInfo->coefficient);
            bool val = (tag_val.value.to_int == 0) ? false : true;
            if (io == NULL)
            {
#ifdef CP56_IRG
                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
                io= (InformationObject)SinglePointWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
                CS101_ASDU_addInformationObject(newAsdu, io);
#else
                io= (InformationObject)SinglePointInformation_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
                CS101_ASDU_addInformationObject(newAsdu, io);
#endif          
            }
            else
            {
#ifdef CP56_IRG
                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
                CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
                    SinglePointWithCP56Time2a_create((SinglePointInformation)io, val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
                CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
                    SinglePointInformation_create((SinglePointInformation)io, val_addr, val, IEC60870_QUALITY_GOOD));
#endif
            }
        }
        else if (pointInfo->data_type == TYPE_SHORT || pointInfo->data_type == TYPE_USHORT)
        {
            read_data_form_tag_by_p(pointInfo->tag, TYPE_TAG_INT, &tag_val, pointInfo->coefficient);
            int val = tag_val.value.to_int;
            if (io == NULL)
            {
#ifdef CP56_IRG
                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
                io= (InformationObject)MeasuredValueScaledWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
                CS101_ASDU_addInformationObject(newAsdu, io);
#else
                io= (InformationObject)MeasuredValueScaled_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
                CS101_ASDU_addInformationObject(newAsdu, io);
#endif
            }
            else
            {
#ifdef CP56_IRG
                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
                CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
                    MeasuredValueScaledWithCP56Time2a_create((MeasuredValueScaled)io, val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
                CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
                    MeasuredValueScaled_create((MeasuredValueScaled)io, val_addr, val, IEC60870_QUALITY_GOOD));
#endif
            }
        }
        else if (pointInfo->data_type == TYPE_FLOAT)
        {
            read_data_form_tag_by_p(pointInfo->tag, TYPE_TAG_FLOAT, &tag_val, pointInfo->coefficient);
            float val = tag_val.value.to_float;
            if (io == NULL)
            {
#ifdef CP56_IRG
                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
                io= (InformationObject)MeasuredValueShortWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
                CS101_ASDU_addInformationObject(newAsdu, io);
#else
                io= (InformationObject)MeasuredValueShort_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
                CS101_ASDU_addInformationObject(newAsdu, io);
#endif
            }
            else
            {
#ifdef CP56_IRG
                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
                CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
                    MeasuredValueShortWithCP56Time2a_create((MeasuredValueShort)io, val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
                CS101_ASDU_addInformationObject(newAsdu, (InformationObject)
                    MeasuredValueShort_create((MeasuredValueShort)io, val_addr, val, IEC60870_QUALITY_GOOD));
#endif
            }
        }
        CS104_Slave_enqueueASDU(var->slave, newAsdu);
        CS101_ASDU_destroy(newAsdu);
        InformationObject_destroy(io);
        io = NULL;
    }
    else
    {
        ems_syslog(LOG_ERR, "pointInfo pr is NULL!");
    }

}



static void report_all_data(iec104_info *var, int ptsidx)
{
    int singlegroup = 25;
    int currue_num[IEC_TYPE_MAX] = {0}; //0 bool 1 int 2 float
    
#ifdef CP56_IRG_INV
    struct sCP56Time2a cp56;
    singlegroup = 15;
    time_t dtime;
#endif

    CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(var->slave);

    int type_index = 0;
	for (size_t m = 0; m < DATA_CALSS; m++)
	{
	    if ((strcmp(var->param[ptsidx].point_param_arry[m]->point_type, "RemotePoint") == 0) || (strcmp(var->param[ptsidx].point_param_arry[m]->point_type, "RemoteMeas") == 0))
	    {	
	        CS101_ASDU newAsdu[IEC_TYPE_MAX] = {NULL};
		    InformationObject io[IEC_TYPE_MAX] = {NULL};

		    for (size_t i = 0; i < IEC_TYPE_MAX; i++)
		    {
		        newAsdu[i] = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, var->param[ptsidx].ca, false, false);
		    }
			memset(currue_num, 0, sizeof(currue_num));
			
		    for (size_t i = 0; i < var->param[ptsidx].point_param_arry[m]->point_num; i++)
		    {
		        int val_addr = var->param[ptsidx].point_param_arry[m]->point_info[i].addr;
		        for (size_t j = 0; j < IEC_TYPE_MAX; j++)
		        {
		            if (currue_num[j] > singlegroup)
		            {
		                CS104_Slave_enqueueASDU(var->slave, newAsdu[j]);
		                CS101_ASDU_destroy(newAsdu[j]);
		                newAsdu[j] = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, var->param[ptsidx].ca, false, false);
		                currue_num[j] = 0;
		            }
		        }
		        tag_value_t tag_val = {0};
		        if (var->param[ptsidx].point_param_arry[m]->point_info[i].data_type == TYPE_BOOL)
		        {
		            type_index = 0;
		            read_data_form_tag_by_p(var->param[ptsidx].point_param_arry[m]->point_info[i].tag, TYPE_TAG_INT, &tag_val, var->param[ptsidx].point_param_arry[m]->point_info[i].coefficient);
		            bool val = (tag_val.value.to_int == 0) ? false : true;
		            if (NULL == io[type_index])
		            {
#ifdef CP56_IRG_INV
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                io[type_index]= (InformationObject)SinglePointWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#else
		                io[type_index]= (InformationObject)SinglePointInformation_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#endif          
		            }
		            else
		            {
#ifdef CP56_IRG_INV
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    SinglePointWithCP56Time2a_create((SinglePointInformation)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    SinglePointInformation_create((SinglePointInformation)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD));
#endif
		            }
		            currue_num[type_index]++;
		        }
		        else if (var->param[ptsidx].point_param_arry[m]->point_info[i].data_type == TYPE_SHORT)
		        {
		            type_index = 1;
		            read_data_form_tag_by_p(var->param[ptsidx].point_param_arry[m]->point_info[i].tag, TYPE_TAG_INT, &tag_val, var->param[ptsidx].point_param_arry[m]->point_info[i].coefficient);
		            int val = tag_val.value.to_int;
		            if (NULL == io[type_index])
		            {
#ifdef CP56_IRG_INV 
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                io[type_index]= (InformationObject)MeasuredValueScaledWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#else
		                io[type_index]= (InformationObject)MeasuredValueScaled_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#endif
		            }
		            else
		            {
#ifdef CP56_IRG_INV
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueScaledWithCP56Time2a_create((MeasuredValueScaled)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueScaled_create((MeasuredValueScaled)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD));
#endif
		            }
		            currue_num[type_index]++;
		        }
		        else if (var->param[ptsidx].point_param_arry[m]->point_info[i].data_type == TYPE_USHORT)
		        {
		            type_index = 2;
		            read_data_form_tag_by_p(var->param[ptsidx].point_param_arry[m]->point_info[i].tag, TYPE_TAG_INT, &tag_val, var->param[ptsidx].point_param_arry[m]->point_info[i].coefficient);
		            float val = (float)tag_val.value.to_int;
		            if (NULL == io[type_index])
		            {
#ifdef CP56_IRG_INV
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                io[type_index]= (InformationObject)MeasuredValueShortWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#else
		                io[type_index]= (InformationObject)MeasuredValueShort_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#endif
		            }
		            else
		            {
#ifdef CP56_IRG_INV
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueShortWithCP56Time2a_create((MeasuredValueShort)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueShort_create((MeasuredValueShort)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD));
#endif
		            }
		            currue_num[type_index]++;
		        }
		        else if (var->param[ptsidx].point_param_arry[m]->point_info[i].data_type == TYPE_FLOAT)
		        {
		            type_index = 2;
		            read_data_form_tag_by_p(var->param[ptsidx].point_param_arry[m]->point_info[i].tag, TYPE_TAG_FLOAT, &tag_val, var->param[ptsidx].point_param_arry[m]->point_info[i].coefficient);
		            float val = tag_val.value.to_float;
		            if (NULL == io[type_index])
		            {
#ifdef CP56_IRG_INV
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                io[type_index]= (InformationObject)MeasuredValueShortWithCP56Time2a_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD, &cp56);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#else
		                io[type_index]= (InformationObject)MeasuredValueShort_create(NULL, val_addr, val, IEC60870_QUALITY_GOOD);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], io[type_index]);
#endif
		            }
		            else
		            {
#ifdef CP56_IRG_INV
		                CP56Time2a_createFromMsTimestamp(&cp56, dtime * 1000);
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueShortWithCP56Time2a_create((MeasuredValueShort)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD, &cp56));
#else
		                CS101_ASDU_addInformationObject(newAsdu[type_index], (InformationObject)
		                    MeasuredValueShort_create((MeasuredValueShort)io[type_index], val_addr, val, IEC60870_QUALITY_GOOD));
#endif
		            }
		            currue_num[type_index]++;
		        }
		    }
			
			for (size_t i = 0; i < IEC_TYPE_MAX; i++)
		    {
		        if (currue_num[i] > 0)
		            CS104_Slave_enqueueASDU(var->slave, newAsdu[i]);
		        CS101_ASDU_destroy(newAsdu[i]);
		        if (io[i] != NULL)
		        {
		            InformationObject_destroy(io[i]);
		        }
		    }
	    }
	}
}


void *iec104_task(void *args)
{
	int errcnt = 0;
    if (args == NULL)
    {
        ems_syslog(LOG_ERR, "104 var is NULL");
        pthread_detach(pthread_self());
        return NULL;
    }
    time_t last_report = 0;
    time_t now;

    iec104_info *var = (iec104_info *)args;
    if (var->param == NULL)
    {
        ems_syslog(LOG_ERR, "104 param is NULL");
        pthread_detach(pthread_self());
        return NULL;
    }

    CS104_Slave slave = var->slave;
    CS104_Slave_start(slave);

/*
    if (CS104_Slave_isRunning(slave) == false) {
        ems_syslog(LOG_ERR, "Starting server failed!\n");
        goto exit_program;
    }
*/
	while (CS104_Slave_isRunning(slave) == false) {
	    struct timespec tspec;
	    ems_syslog(LOG_ERR, "IEC104 Starting server failed!\n");
        BUSINESS_LOG(BLOG_ERR, NORTH_ID_0031, NULL, "[北向]:IEC104 Starting server failed");
	    tspec.tv_sec = 1;
	    tspec.tv_nsec = 0;
	    errcnt++;
	    if (errcnt >= 4) {
	        var->slave = NULL;
	        tspec.tv_nsec = 500 * 1000000;
	        nanosleep(&tspec, NULL);
	        goto exit_program;
	    }
	    nanosleep(&tspec, NULL);
	}

    BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0031, NULL, "[北向]:IEC104 Starting server success");
	
    while (running) {
        now = time(NULL);
        if ((var->periodRepEn) && (var->reportInterval != 0) && (check_timeout_second(now, &last_report, var->reportInterval)) && (CS104_Slave_getOpenConnections(var->slave) > 0))
        {
            ems_syslog(LOG_NOTICE, "report all data!\n");
            for (int i = 0; i < var->cacnt; i++)
            {
                report_all_data(var, i);
            }
        }
        Thread_sleep(500);
    }

exit_program:
    pthread_detach(pthread_self());

    CS104_Slave_stop(slave);

    CS104_Slave_destroy(slave);

    Thread_sleep(500);

    return NULL;
}

static int paser_iec104_info_file(iec104_info *var, const char *pdata)
{
    int ret = -1;
    cJSON *root = NULL;
    cJSON *pts = NULL;
	
    root = json_parse_string_with_comments(pdata);
    if (!root)
    {
        ems_syslog(LOG_ERR,"parse cfg file error");
        goto out;
    }

    int ver = 1;
    if (cJSON_HasObjectItem(root, "ver"))
    {
        GET_JSON_VALUE_INT(root, "ver", ver);
    }
    int ptscnt = 1;
    if (ver > 1)
    {
        pts = cJSON_GetObjectItem(root, "pts");
        ptscnt = cJSON_GetArraySize(pts);
    }
    var->cacnt = ptscnt;
	ems_syslog(LOG_NOTICE, "north iec104 cacnt::: [%d]", var->cacnt);
	
    GET_JSON_VALUE_INT(root, "reportInterval", var->reportInterval);
    var->periodRepEn = 0;
    if (cJSON_HasObjectItem(root, "periodReportEnable"))
    {
        GET_JSON_VALUE_INT(root, "periodReportEnable", var->periodRepEn);
    }

    proto_forward_t *proto_forward_var = get_proto_forward_var();
    var->param = calloc(sizeof(iec104_param), ptscnt);
    for (int i = 0; i < ptscnt; i++)
    {
        cJSON *ptsobj = NULL;
        if (ver > 1)
        {
            ptsobj = cJSON_GetArrayItem(pts, i);
        }
        else
        {
            ptsobj = root;
        }
		GET_JSON_VALUE_INT(ptsobj, "commonAddr", var->param[i].ca);
        cJSON *pointInfo_list = cJSON_GetObjectItemCaseSensitive(ptsobj, "pointInfo");
        if (!pointInfo_list)
        {
            ems_syslog(LOG_ERR, "pointInfo error");
            goto out;
        }
		
        int pointInfo_list_cnt = cJSON_GetArraySize(pointInfo_list); // 遥信、遥测、遥控、遥调，pointInfo_list_cnt=4
		for (int j = 0; j < pointInfo_list_cnt; j++)
        {
            cJSON *array_item = cJSON_GetArrayItem(pointInfo_list, j);

	        cJSON *points_list = cJSON_GetObjectItemCaseSensitive(array_item, "points");
	        if (!points_list)
	        {
	            ems_syslog(LOG_ERR, "points_list error");
	            goto out;
	        }

			int points_list_cnt = cJSON_GetArraySize(points_list);
			if (var->param[i].point_param_arry[j] == NULL)
	        {
	            var->param[i].point_param_arry[j] = calloc(1, sizeof(point_param) + sizeof(point_info_104) * points_list_cnt);
	            if(var->param[i].point_param_arry[j] == NULL)
	            {
	                ems_syslog(LOG_ERR, "calloc error for data");
	                goto out;
	            }
	        }
			GET_JSON_VALUE_DY_STRING(array_item, "type", var->param[i].point_param_arry[j]->point_type);
			ems_syslog(LOG_NOTICE, "param[%d] point_type: %s num: %d", i, var->param[i].point_param_arry[j]->point_type, points_list_cnt);
			var->param[i].point_param_arry[j]->ptr = &var->param[i]; // "类"所属域    ca
            var->param[i].point_param_arry[j]->point_num  = points_list_cnt;
	        for (int m = 0; m < points_list_cnt; m++)
	        {
 				cJSON *item = cJSON_GetArrayItem(points_list, m);             
	            int data_num = cJSON_GetArraySize(item);
	            if (data_num < 5)
	            {
	                ems_syslog(LOG_ERR, "invalid number of params");
	                continue;
	            }
	            cJSON *p0 = cJSON_GetArrayItem(item, 0);
	            cJSON *p1 = cJSON_GetArrayItem(item, 1);
	            cJSON *p2 = cJSON_GetArrayItem(item, 2);
	            cJSON *p3 = cJSON_GetArrayItem(item, 3);
	            cJSON *p4 = cJSON_GetArrayItem(item, 4);
	            if (!p0 || !p1 || !p2|| !p3 || !p4)
	            {
	                ems_syslog(LOG_ERR, "invalid params list:%d", i);
	                continue;
	            }
				
	            var->param[i].point_param_arry[j]->point_info[m].tag = find_tag_by_name(proto_forward_var, p0->valuestring);
				if ((strcmp(var->param[i].point_param_arry[j]->point_type, "RemotePoint") == 0) || (strcmp(var->param[i].point_param_arry[j]->point_type, "RemoteMeas") == 0)){
		            if (var->param[i].point_param_arry[j]->point_info[m].tag != NULL)
		            {
		                var->param[i].point_param_arry[j]->point_info[m].tag->some_pr[TAG_EX_PR_INDEX] = &var->param[i].point_param_arry[j]->point_info[m];
		            }
		            else
		            {
		                ems_syslog(LOG_ERR, "no find:[%s]'s tag pr ", p0->valuestring);
		                //exit(0);
		                continue;
		            }
				}
	            var->param[i].point_param_arry[j]->point_info[m].addr          = analysis_addr(p1->valuestring);
	            var->param[i].point_param_arry[j]->point_info[m].data_type     = get_type_by_string(p2->valuestring);
	            var->param[i].point_param_arry[j]->point_info[m].changeReport  = p3->valueint;
	            var->param[i].point_param_arry[j]->point_info[m].coefficient   = p4->valuedouble;
                var->param[i].point_param_arry[j]->point_info[m].ptr = var->param[i].point_param_arry[j]; //点位所属"类"--遥信、遥测、遥控、遥调
					
                if ((strcmp(var->param[i].point_param_arry[j]->point_type, "RemoteCtrl") == 0) || (strcmp(var->param[i].point_param_arry[j]->point_type, "RemoteRegu") == 0)){
		            int ret = hash_intptr_addptr(&var->param[i].point_map_hash, &var->param[i].point_param_arry[j]->point_info[m].addr, sizeof(int), &var->param[i].point_param_arry[j]->point_info[m]);
		            if (ret < 0)
		            {
		                ems_syslog(LOG_ERR, "hash_intptr_addptr failed, 104 add: %d", var->param[i].point_param_arry[j]->point_info[m].addr);
		                continue;
		            }
                }
				
	            ems_syslog(LOG_NOTICE, "get num:%d, name:%s, addr:%d, data_type:%d, changereprot:%d, coefficient:%f", m,
	            p0->valuestring                                              ,
	            var->param[i].point_param_arry[j]->point_info[m].addr        ,
	            var->param[i].point_param_arry[j]->point_info[m].data_type   ,
	            var->param[i].point_param_arry[j]->point_info[m].changeReport,
	            var->param[i].point_param_arry[j]->point_info[m].coefficient );
				
	            // var->param[i].point_param_arry[j]->point_num++;
	        }
		}
    }
    ret = 0;
    
out:
    cJSON_Delete(root);
    return ret;
}


int load_iec104_config_file(iec104_info *var)
{
    int ret = -1;
    if (var == NULL)
    {
        return ret;
    }

    char *pdata = NULL;
    ems_syslog(LOG_NOTICE,"read cfg file %s ...\n", IEC104_CONFIG_FILE);
    pdata = read_file_data(IEC104_CONFIG_FILE);
    if (pdata == NULL)
    {
        ems_syslog(LOG_CRIT,"[IEC104标准北向]:加载北向设备信息失败,文件名:%s请生成IEC104北向点表后重新启动程序",IEC104_CONFIG_FILE);
        BUSINESS_LOG(BLOG_ERR, NORTH_ID_0032, NULL, "[北向]:IEC104标准北向 加载北向设备信息失败,文件名:%s请生成IEC104北向点表后重新启动程序",IEC104_CONFIG_FILE);
        goto ERR;
    }
    else
    {
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0032, NULL, "[北向]:IEC104标准北向 加载北向设备信息成功");
    }
    if(paser_iec104_info_file(var, pdata) < 0)
    {
        ems_syslog(LOG_ERR, "paser error\n");
        BUSINESS_LOG(BLOG_ERR, NORTH_ID_0033, NULL, "[北向]:IEC104标准北向 解析北向设备信息失败");
        goto ERR;
    }
    else
    {
        BUSINESS_LOG(BLOG_NOTICE, NORTH_ID_0033, NULL, "[北向]:IEC104标准北向 解析北向设备信息成功");
    }
    ret = 0;
ERR:
    if (pdata != NULL)
    {
        free(pdata);
    }
    return ret;    
}

iec104_info *iec104_server_init(void)
{
    /* create a new slave/server instance with default connection parameters and
     * default message queue size */
    iec104_info *var = calloc(1,sizeof(iec104_info));
    
    if(load_iec104_config_file(var) < 0)
    {
        if (var != NULL)
            free(var);
        return NULL;
    }

    register_iec_change_report_cb(var);

    CS104_Slave slave = CS104_Slave_create(100, 100);  
    var->slave = slave;

    CS104_Slave_setLocalAddress(slave, "0.0.0.0");
    
    /* Allow multiple concurrent links from the same peer.
     * In MULTIPLE_REDUNDANCY_GROUPS mode, same-IP links fall into the same
     * redundancy group and will deactivate each other on STARTDT.
     */
    CS104_Slave_setServerMode(slave, CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP);

    /* get the connection parameters - we need them to create correct ASDUs -
     * you can also modify the parameters here when default parameters are not to be used */
    // CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(slave);

    /* when you have to tweak the APCI parameters (t0-t3, k, w) you can access them here */
    CS104_APCIParameters apciParams = CS104_Slave_getConnectionParameters(slave);

    ems_syslog(LOG_NOTICE,"APCI parameters:\n");
    ems_syslog(LOG_NOTICE,"  t0: %i\n", apciParams->t0);
    ems_syslog(LOG_NOTICE,"  t1: %i\n", apciParams->t1);
    ems_syslog(LOG_NOTICE,"  t2: %i\n", apciParams->t2);
    ems_syslog(LOG_NOTICE,"  t3: %i\n", apciParams->t3);
    ems_syslog(LOG_NOTICE,"  k: %i\n", apciParams->k);
    ems_syslog(LOG_NOTICE,"  w: %i\n", apciParams->w);

    /* set the callback handler for the clock synchronization command */
    CS104_Slave_setClockSyncHandler(slave, clockSyncHandler, NULL);

    /* set the callback handler for the interrogation command */
    CS104_Slave_setInterrogationHandler(slave, interrogationHandler, var);

    /* set the callback handler for the counter interrogation command */
    //CS104_Slave_setCounterInterrogationHandler(var->slave, counterinterrogationhandler, var);

    /* set handler for other message types */
    CS104_Slave_setASDUHandler(slave, asduHandler, var);

    /* set handler to handle connection requests (optional) */
    CS104_Slave_setConnectionRequestHandler(slave, connectionRequestHandler, NULL);

    /* set handler to track connection events (optional) */
    CS104_Slave_setConnectionEventHandler(slave, connectionEventHandler, NULL);

    /* uncomment to log messages */
    // CS104_Slave_setRawMessageHandler(slave, rawMessageHandler, NULL);
    return var;
}

void iec104_server_start(iec104_info *var)
{
    pthread_t tid;
    if ((var == NULL) || (var->param == NULL) || (var->slave == NULL))
    {
        ems_syslog(LOG_ERR,"param is NULL or server init err task return!");
        return;
    }
    
    if(pthread_create(&tid, NULL, iec104_task, var) != 0)
    {
        ems_syslog(LOG_ERR,"iec104 server start error!!");
    }
    else
    {
        pthread_setname_np(tid, "Iec104Task");
    }
}
