
#include "ocpp16_client.h"
#include "ocpp16_encode.h"
#include "ocpp16_decode.h"
#include "cJSON.h"
#include "ocpp16.h"
#include "mlogger.h"
#include "ocpp16_configkey.h"
#include "ocpp16_authorization.h"
#include "ocpp16_OfflineChargingRecords.h"
#include "ocpp16_ChargingProfiles.h"
#include "ocpp16_websocket.h"
#include "modbusTCP_contex.h"
#include "ccu_port.h"
#include "secc_port.h"

ocpp_client_t ocpp0 = {
    .quit = 1,
    .HeartbeatInterval = 240,
    .ccu = NULL,
    .need_updatefirmware = 0,
    .need_diagnostics = 0,
    .need_reset = 0,
};
ocpp_client_t ocpp1 = {
    .quit = 1,
    .HeartbeatInterval = 240,
    .ccu = NULL,
    .need_updatefirmware = 0,
    .need_diagnostics = 0,
    .need_reset = 0,
};

static int ocpp16_pipe_send(ocpp_client_t* dev, uint8_t *data, int length)
{
    if (dev->fp[0] < 0) return -1;
    uint8_t header[16];
    int len = encode_HEADER(header, 0x1234, 0x55, 0xAA, NULL, length);
    int ret0 = send(dev->fp[0], header, 8, 0);
    int ret1 = send(dev->fp[0], data, length, 0);
    pthread_cond_signal(&dev->cond_rx);
    // printd("ret0:%d, ret1:%d", ret0, ret1);
    return ret0+ret1;
}

static int ocpp16_pipe_wait(ocpp_client_t* dev, uint8_t *msgUUID, int timeout)
{
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);
    ts.tv_sec += timeout/1000;
    pthread_mutex_lock(&dev->mutex_rx);
    int ret = pthread_cond_timedwait(&dev->cond_rx, &dev->mutex_rx, &ts);
    if (ret == 0)
    {
        while(dev->fp[1] > 0)
        {
            modbusTCP_frame_t frame;
            modbusTCP_frame_t* pkg = (modbusTCP_frame_t*)&frame;
            int FRAME_LENGTH = 8;
            dev->pipe_rxbyte = 0;
            while(dev->pipe_rxbyte < FRAME_LENGTH)
            {
                int rv = recv(dev->fp[1], &dev->pipe_rxbuff[dev->pipe_rxbyte], (FRAME_LENGTH - dev->pipe_rxbyte), 0);
                if (rv <= 0) { pthread_mutex_unlock(&dev->mutex_rx); printe("rv:%d, errno:%d\n", rv, errno); return -1; }
                if (rv > 0) dev->pipe_rxbyte += rv;
                if (dev->pipe_rxbyte >= FRAME_LENGTH)
                {
                    decode_FRAME(dev->pipe_rxbuff, pkg);
                    if (pkg->protoID == 0x0000)
                    {
                        FRAME_LENGTH = 8 + pkg->datalen;
                        break;
                    }
                    memmove(&dev->pipe_rxbuff[0], &dev->pipe_rxbuff[1], dev->pipe_rxbyte - 1);
                    dev->pipe_rxbyte = dev->pipe_rxbyte - 1;
                }
            }
            while(dev->pipe_rxbyte < FRAME_LENGTH)
            {
                int rv = recv(dev->fp[1], &dev->pipe_rxbuff[dev->pipe_rxbyte], (FRAME_LENGTH - dev->pipe_rxbyte), 0);
                if (rv <= 0) { pthread_mutex_unlock(&dev->mutex_rx); printe("rv:%d, errno:%d\n", rv, errno); return -1; }
                if (rv > 0) dev->pipe_rxbyte += rv;
            }
            if (dev->pipe_rxbyte >= FRAME_LENGTH)
            {
                dev->pipe_rxbuff[FRAME_LENGTH] = '\0';
                mlog_v(LOG_OCPP, "[%s]:pipe[%02x, %02x]:%d, %s\n", dev->online, dev->rxtype, dev->rxmask, dev->pipe_rxbyte, &dev->pipe_rxbuff[8]);
                if (strstr(&dev->pipe_rxbuff[8], msgUUID) != NULL)
                {
                    ret = 0;
                    break;
                }
            }
        }
    }
    pthread_mutex_unlock(&dev->mutex_rx);
    return ret;
}

int ocpp16_Authorize_request(ocpp_client_t* dev, AuthorizeReq_t* req, AuthorizeConf_t* conf)
{
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;
    pthread_mutex_lock(&dev->mutex_tx);
    dev->txbyte = ocpp16_encode_AuthorizeReq(dev->txbuff, msgUUID, req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);

    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);

        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    ret = ocpp16_decode_AuthorizeConf(conf, msgUUID, root);
                    printd("ret = %d\n", ret);
                }
            }
            cJSON_Delete(root);
        }

        pthread_mutex_unlock(&dev->mutex_rx);
    }

    return ret;
}

int ocpp16_BootNotification_request(ocpp_client_t* dev, BootNotificationReq_t* req, BootNotificationConf_t* conf)
{
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;
    pthread_mutex_lock(&dev->mutex_tx);
    dev->txbyte = ocpp16_encode_BootNotificationReq(dev->txbuff, msgUUID, req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);

    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);
        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    ret = ocpp16_decode_BootNotificationConf(conf, msgUUID, root);
                }
            }
            cJSON_Delete(root);
        }
        pthread_mutex_unlock(&dev->mutex_rx);
    }


    return ret;
}

int ocpp16_DataTransfer_request(ocpp_client_t* dev, DataTransferReq_t* req, DataTransferConf_t* conf)
{
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;
    pthread_mutex_lock(&dev->mutex_tx);
    dev->txbyte = ocpp16_encode_DataTransferReq(dev->txbuff, msgUUID, req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);
        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    ret = ocpp16_decode_DataTransferConf(conf, msgUUID, root);
                }
            }
            cJSON_Delete(root);
        }        
        pthread_mutex_unlock(&dev->mutex_rx);
    }

    return ret;
}

int ocpp16_DiagnosticsStatusNotification_request(ocpp_client_t* dev, DiagnosticsStatus_t status)
{
    DiagnosticsStatusNotificationReq_t req;
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;
    pthread_mutex_lock(&dev->mutex_tx);
    req.status = status;
    dev->txbyte = ocpp16_encode_DiagnosticsStatusNotificationReq(dev->txbuff, msgUUID, &req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);

    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);
        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    DiagnosticsStatusNotificationConf_t conf;
                    ret = ocpp16_decode_DiagnosticsStatusNotificationConf(&conf, msgUUID, root);
                }
            
            }
            cJSON_Delete(root);
        }
        pthread_mutex_lock(&dev->mutex_rx);
    }
   
    return ret;
}

int ocpp16_FirmwareStatusNotification_request(ocpp_client_t* dev, FirmwareStatus_t status)
{
    FirmwareStatusNotificationReq_t req;
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;
    pthread_mutex_lock(&dev->mutex_tx);
    req.status = status;
    dev->txbyte = ocpp16_encode_FirmwareStatusNotificationReq(dev->txbuff, msgUUID, &req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);

    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);
        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    FirmwareStatusNotificationConf_t conf;
                    ret = ocpp16_decode_FirmwareStatusNotificationConf(&conf, msgUUID, root);
                }
            
            }
            cJSON_Delete(root);
        }
        pthread_mutex_unlock(&dev->mutex_rx);
    }

    return ret;
}

int ocpp16_Heartbeat_request(ocpp_client_t* dev, HeartbeatReq_t* req, HeartbeatConf_t* conf)
{
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;
    pthread_mutex_lock(&dev->mutex_tx);
    dev->txbyte = ocpp16_encode_HeartbeatReq(dev->txbuff, msgUUID, req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);
        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    ret = ocpp16_decode_HeartbeatConf(conf, msgUUID, root);
                }
            }
            cJSON_Delete(root);
        }
        pthread_mutex_unlock(&dev->mutex_rx);
    }

    return ret;
}

int ocpp16_MeterValues_request(ocpp_client_t* dev, MeterValuesReq_t* req)
{
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;
    pthread_mutex_lock(&dev->mutex_tx);
    dev->txbyte = ocpp16_encode_MeterValuesReq(dev->txbuff, msgUUID, req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);

    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);
        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    MeterValuesConf_t conf;
                    ret = ocpp16_decode_MeterValuesConf(&conf, msgUUID, root);
                }
            }
            cJSON_Delete(root);
        }
        pthread_mutex_unlock(&dev->mutex_rx);
    }

    return ret;
}

int ocpp16_StartTransaction_request(ocpp_client_t* dev, StartTransactionReq_t* req, StartTransactionConf_t* conf)
{
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;

    pthread_mutex_lock(&dev->mutex_tx);
    dev->txbyte = ocpp16_encode_StartTransactionReq(dev->txbuff, msgUUID, req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);

    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);
        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    ret = ocpp16_decode_StartTransactionConf(conf, msgUUID, root);
                }
            }
            cJSON_Delete(root);
        }
        pthread_mutex_unlock(&dev->mutex_rx);
    }

    printd("step, ret:%d\n", ret);

    return ret;
}

int ocpp16_StatusNotification_request(ocpp_client_t* dev, StatusNotificationReq_t* req)
{
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;
    pthread_mutex_lock(&dev->mutex_tx);
    dev->txbyte = ocpp16_encode_StatusNotificationReq(dev->txbuff, msgUUID, req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);

    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);
        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    StatusNotificationConf_t conf;
                    ret = ocpp16_decode_StatusNotificationConf(&conf, msgUUID, root);
                }
            }
            cJSON_Delete(root);
        }
        pthread_mutex_unlock(&dev->mutex_rx);
    }


    return ret;
}

int ocpp16_StopTransaction_request(ocpp_client_t* dev, StopTransactionReq_t* req, StopTransactionConf_t* conf)
{
    int ret = -ErrorCode_GenericError;
    CidString37_t msgUUID;
    pthread_mutex_lock(&dev->mutex_tx);
    dev->txbyte = ocpp16_encode_StopTransactionReq(dev->txbuff, msgUUID, req);
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);

    if (ocpp16_pipe_wait(dev, msgUUID,  OCPP_SEMPARE_TIMEOUT_MS) == 0)
    {
        pthread_mutex_lock(&dev->mutex_rx);
        cJSON *root = cJSON_ParseWithLength(&dev->pipe_rxbuff[8], dev->pipe_rxbyte - 8);
        if (root)
        {
            if (cJSON_IsArray(root))
            {
                int size = cJSON_GetArraySize(root);
                uint32_t msgType = (uint32_t)cJSON_GetNumberValue(cJSON_GetArrayItem(root, 0));
                if ((size == 3 && msgType == 3) || (size == 5 && msgType == 4))
                {
                    ret = ocpp16_decode_StopTransactionConf(conf, msgUUID, root);
                }
            }
            cJSON_Delete(root);
        }
        pthread_mutex_unlock(&dev->mutex_rx);
    }

    return ret;
}
//---------------------------------------------------------------------------------------------------------
static int ocpp16_CancelReservation_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    CancelReservationReq_t req;
    CancelReservationConf_t conf;

    int ret = ocpp16_decode_CancelReservationReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_CancelReservation_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_CancelReservationConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}

static int ocpp16_ChangeAvailability_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    ChangeAvailabilityReq_t req;
    ChangeAvailabilityConf_t conf;

    int ret = ocpp16_decode_ChangeAvailabilityReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_ChangeAvailability_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_ChangeAvailabilityConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}

static int ocpp16_ChangeConfiguration_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    ChangeConfigurationReq_t req;
    ChangeConfigurationConf_t conf;

    int ret = ocpp16_decode_ChangeConfigurationReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_ChangeConfiguration_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_ChangeConfigurationConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}

static int ocpp16_ClearCache_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    ClearCacheReq_t req;
    ClearCacheConf_t conf;

    int ret = ocpp16_decode_ClearCacheReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_ClearCache_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_ClearCacheConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_ClearChargingProfile_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    ClearChargingProfileReq_t req;
    ClearChargingProfileConf_t conf;

    int ret = ocpp16_decode_ClearChargingProfileReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_ClearChargingProfile_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_ClearChargingProfileConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_DataTransfer_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    DataTransferReq_t req;
    DataTransferConf_t conf;

    int ret = ocpp16_decode_DataTransferReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_DataTransfer_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_DataTransferConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
//---------------------------------------------------------------------------------------------------------
static int ocpp16_GetCompositeSchedule_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    GetCompositeScheduleReq_t req;
    GetCompositeScheduleConf_t conf;

    int ret = ocpp16_decode_GetCompositeScheduleReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_GetCompositeSchedule_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_GetCompositeScheduleConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_GetConfiguration_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    GetConfigurationReq_t req;
    GetConfigurationConf_t conf;

    int ret = ocpp16_decode_GetConfigurationReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_GetConfiguration_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_GetConfigurationConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_GetDiagnostics_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    GetDiagnosticsReq_t req;
    GetDiagnosticsConf_t conf;
    int ret = ocpp16_decode_GetDiagnosticsReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_GetDiagnostics_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_GetDiagnosticsConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_GetLocalListVersion_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    GetLocalListVersionReq_t req;
    GetLocalListVersionConf_t conf;

    int ret = ocpp16_decode_GetLocalListVersionReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_GetLocalListVersion_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_GetLocalListVersionConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_RemoteStartTransaction_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    RemoteStartTransactionReq_t req;
    RemoteStartTransactionConf_t conf;
    int ret = ocpp16_decode_RemoteStartTransactionReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_RemoteStartTransaction_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_RemoteStartTransactionConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_RemoteStopTransaction_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    RemoteStopTransactionReq_t req;
    RemoteStopTransactionConf_t conf;

    int ret = ocpp16_decode_RemoteStopTransactionReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_RemoteStopTransaction_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_RemoteStopTransactionConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
//---------------------------------------------------------------------------------------------------------
static int ocpp16_ReserveNow_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    ReserveNowReq_t req;
    ReserveNowConf_t conf;

    int ret = ocpp16_decode_ReserveNowReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_ReserveNow_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_ReserveNowConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_Reset_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    ResetReq_t req;
    ResetConf_t conf;

    int ret = ocpp16_decode_ResetReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_Reset_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_ResetConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_SendLocalList_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    SendLocalListReq_t req;
    SendLocalListConf_t conf;

    int ret = ocpp16_decode_SendLocalListReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_SendLocalList_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_SendLocalListConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_SetChargingProfile_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    SetChargingProfileReq_t req;
    SetChargingProfileConf_t conf;

    int ret = ocpp16_decode_SetChargingProfileReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_SetChargingProfile_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_SetChargingProfileConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_TriggerMessage_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    TriggerMessageReq_t req;
    TriggerMessageConf_t conf;

    int ret = ocpp16_decode_TriggerMessageReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_TriggerMessage_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_TriggerMessageConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_UnlockConnector_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    UnlockConnectorReq_t req;
    UnlockConnectorConf_t conf;

    int ret = ocpp16_decode_UnlockConnectorReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_UnlockConnector_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_UnlockConnectorConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}
static int ocpp16_UpdateFirmware_callback(ocpp_client_t* dev, uint32_t msgType, CidString37_t msgUUID, cJSON* payload)
{
    UpdateFirmwareReq_t req;
    UpdateFirmwareConf_t conf;

    int ret = ocpp16_decode_UpdateFirmwareReq(&req, payload);
    if (ret == 0)
    {
        ret = ocpp16_UpdateFirmware_confirm(dev, &req, &conf);
    }
    
    pthread_mutex_lock(&dev->mutex_tx);
    if (ret == 0)
    {
        dev->txbyte = ocpp16_encode_UpdateFirmwareConf(dev->txbuff, msgUUID, &conf);
    }else{
        dev->txbyte = ocpp16_encode_ErrorResult(dev->txbuff, msgUUID, ret, NULL);
    }
    ocpp16_websocket_send(dev);
    pthread_mutex_unlock(&dev->mutex_tx);
    
    return ret;
}

static int ocpp16_client_callback(void* priv, uint8_t* data, int length, uint8_t type)
{
    static ocpp_method_t ocpp_action[]=
    {
        {.name = "CancelReservation",                   .callback = ocpp16_CancelReservation_callback},
        {.name = "ChangeAvailability",                  .callback = ocpp16_ChangeAvailability_callback},
        {.name = "ChangeConfiguration",                 .callback = ocpp16_ChangeConfiguration_callback},
        {.name = "ClearCache",                          .callback = ocpp16_ClearCache_callback},
        {.name = "ClearChargingProfile",                .callback = ocpp16_ClearChargingProfile_callback}, 
        {.name = "DataTransfer",                        .callback = ocpp16_DataTransfer_callback},
    
        {.name = "GetCompositeSchedule",                .callback = ocpp16_GetCompositeSchedule_callback},
        {.name = "GetConfiguration",                    .callback = ocpp16_GetConfiguration_callback},
        {.name = "GetDiagnostics",                      .callback = ocpp16_GetDiagnostics_callback},
        {.name = "GetLocalListVersion",                 .callback = ocpp16_GetLocalListVersion_callback},
        {.name = "RemoteStartTransaction",              .callback = ocpp16_RemoteStartTransaction_callback},
        {.name = "RemoteStopTransaction",               .callback = ocpp16_RemoteStopTransaction_callback},
    
        {.name = "ReserveNow",                          .callback = ocpp16_ReserveNow_callback},
        {.name = "Reset",                               .callback = ocpp16_Reset_callback},
        {.name = "SendLocalList",                       .callback = ocpp16_SendLocalList_callback},
        {.name = "SetChargingProfile",                  .callback = ocpp16_SetChargingProfile_callback},
        {.name = "TriggerMessage",                      .callback = ocpp16_TriggerMessage_callback},
        {.name = "UnlockConnector",                     .callback = ocpp16_UnlockConnector_callback},
        {.name = "UpdateFirmware",                      .callback = ocpp16_UpdateFirmware_callback},
        
    };
    ocpp_client_t* dev = (ocpp_client_t*)priv;
    
    if (type == WS_DATATYPE_TXTDATA)
    {
        int msgType;
        sscanf(data, "[ %d, \"", &msgType);
        // printd("step:msgType:%d\n", msgType);
        switch(msgType)
        {
            case 2:{
                //server.req, client must send *.conf
                cJSON *root = cJSON_ParseWithLength(data, length);
                if (root)
                {
                    if (cJSON_IsArray(root))
                    {
                        int size = cJSON_GetArraySize(root);
                        if (size == 4 && msgType == 2)
                        { //server.req, client must send *.conf
                            char* msgUUID = cJSON_GetStringValue(cJSON_GetArrayItem(root, 1));
                            char *method = cJSON_GetStringValue(cJSON_GetArrayItem(root, 2));
                            cJSON *payload = cJSON_GetArrayItem(root, 3);
                            int i;
                            for (i=0; i<sizeof(ocpp_action)/sizeof(ocpp_method_t); i++)
                            {
                                if (strncmp(method, ocpp_action[i].name, strlen(ocpp_action[i].name))==0){
                                    ocpp_action[i].callback(dev, msgType, msgUUID, payload);
                                }
                            }
                        }
                    }
                    cJSON_Delete(root);
                }
            }break;
            case 3:
            case 4:{
                ocpp16_pipe_send(dev, data, length);
            }break;
            
        }

    }
    return 0;  
}

static void ocpp16_TryReset(ocpp_client_t* dev)
{
    if (dev->need_reset == 0) return;
    char cmdbuff[128];
    extern void af_command_app(const char* appname);

    switch(dev->need_reset)
    {
        case ResetType_Hard:{ af_command_app("reset app_TCU"); af_command_app("reset app_CCU"); af_command_app("reset app_PCU"); }break;
        case ResetType_Soft:{ af_command_app("reset app_CCU"); }break;
        case ResetType_UpdateTCU:
        case ResetType_UpdateCCU:
        case ResetType_UpdatePCU: {
            // dev->updateFirmware.location = "http://47.102.124.18/download//app_CCU_V1R2B300"
            char *app_name = strstr(dev->updateFirmware.location, "app_"); 
            if (app_name) {
                sprintf(cmdbuff, "update %s", app_name);
                af_command_app(cmdbuff);
            }
        }break;
    }
    dev->need_reset = 0;
}

static void ocpp16_Diagnostics(ocpp_client_t* dev)
{
    if (dev->need_diagnostics == 0) return;
    ccu_config_t* config = &ccu_config;
    extern int af_download_app(const char* url, const char* username, const char* password);
    extern int af_upload_log(const char* localfile, char* url, const char* username, const char* password);

    extern mlog_t clogger;
    mlog_t *mlog = (mlog_t*)&clogger;
    char url[256];
    if (dev->diagnosticsReq.startTime <= dev->diagnosticsReq.stopTime && dev->diagnosticsReq.retries > 0)
    {
        int i;
        char* pusername = config->username;
        char* ppassword = config->password;
        int len = strlen(dev->diagnosticsReq.location);
        if (dev->diagnosticsReq.location[len-1]!='/'){
            dev->diagnosticsReq.location[len]='/'; 
            dev->diagnosticsReq.location[len+1]='\0';
        }
        sprintf(url, "%s\n", dev->diagnosticsReq.location);
        char* phost = strchr(dev->diagnosticsReq.location, '@'); 
        if (phost) {
            phost[0] = '\0'; 
            phost++;
            sprintf(url, "ftp://%s", phost);
            pusername = &dev->diagnosticsReq.location[6];
            ppassword = strchr(pusername, ':');
            if (ppassword) {
                ppassword[0] = '\0';
                ppassword++;
            }
        }

        for (i = 0; i < mlog->fnumb; i++)
        {
            if (dev->diagnosticsReq.startTime <= mlog->flutc[i] && mlog->flutc[i] <= dev->diagnosticsReq.stopTime)
            {
                char fullname[512];
                snprintf(fullname, 511, "%s/%s", mlog->log_pathname, mlog->flist[i]);
                mlog_i(LOG_OCPP, "mlog->flutc[%d]:%d, dev->diagnosticsReq.location:%s, fullname:%s\n", i, mlog->flutc[i], url, fullname);
                af_upload_log(fullname, url, pusername, ppassword);
                ocpp16_DiagnosticsStatusNotification_request(dev, DiagnosticsStatus_Uploading);
            }
        }
        ocpp16_DiagnosticsStatusNotification_request(dev, DiagnosticsStatus_Uploaded);
        dev->diagnosticsReq.startTime = 0;
        dev->diagnosticsReq.stopTime = 0;
        dev->diagnosticsReq.retries--;
    }
    dev->need_diagnostics = 0;
}

static void ocpp16_UpdateFirmware(ocpp_client_t* dev)
{
    if (dev->need_updatefirmware == 0) return;
    ccu_config_t* config = &ccu_config;
    extern int af_download_app(const char* url, const char* username, const char* password);
    extern int af_upload_log(const char* localfile, char* url, const char* username, const char* password);

    dev->need_updatefirmware == 0;
    char *app_name = strstr(dev->updateFirmware.location, "app_");
    if (app_name)
    {
        // if (dev->updateFirmware.retries > 0)
        {
            ocpp16_FirmwareStatusNotification_request(dev, FirmwareStatus_Downloading);
            af_download_app(dev->updateFirmware.location, config->username, config->password);
            ocpp16_FirmwareStatusNotification_request(dev, FirmwareStatus_Downloaded);
            dev->updateFirmware.retrieveDate = 0;
            // dev->updateFirmware.retries--;
        }
        if (strncmp(app_name, "app_TCU", 7)==0) dev->need_reset = ResetType_UpdateTCU;
        if (strncmp(app_name, "app_CCU", 7)==0) dev->need_reset = ResetType_UpdateCCU;
        if (strncmp(app_name, "app_PCU", 7)==0) dev->need_reset = ResetType_UpdatePCU;
    }
}

static void ocpp16_TriggerMessage(ocpp_client_t *dev)
{
    ccu_port_t *ccu = (ccu_port_t*)dev->ccu;
    secc_port_t *seccA = (secc_port_t*)ccu->seccA;
    secc_port_t *seccB = (secc_port_t*)ccu->seccB;
    if (dev->trigger_flag & (1<<MessageTrigger_BootNotification))
    {
        dev->trigger_flag &= ~(1<<MessageTrigger_BootNotification);
        BootNotificationReq_t req ;
        sprintf(req.chargeBoxSerialNumber, "%s", ccu_config.CCU_SN);
        sprintf(req.chargePointSerialNumber, "%s", ccu_config.CCU_SN);
        sprintf(req.chargePointVendor, "%s", "Tcharge");
        sprintf(req.chargePointModel, "%s", "Charge_Point_DC");
        sprintf(req.firmwareVersion, "%s", formatAppVersion());
        sprintf(req.iccid, "%s", "NULL");
        sprintf(req.imsi, "%s", "NULL");
        sprintf(req.meterSerialNumber, "%s", "NULL");
        sprintf(req.meterType, "%s", "DCMeter");
        
        BootNotificationConf_t conf;
        ocpp16_BootNotification_request(dev, &req, &conf);
        if (conf.status == RegistrationStatus_Accepted)
        {
            dev->HeartbeatInterval = conf.interval;
            printd("conf.interval:%d\n", conf.interval);
            mlog_i(LOG_OCPP, "CS.datetime:%s", conf.currentTime);
            dev->HeartbeatInterval = 10; //服务器端65秒没有心跳会关闭TCP连接，65秒是抓包分析的时间
        }else{
            dev->HeartbeatInterval = ocpp16_configkeys.coreProfile.HeartbeatInterval;
        }
    }
    if (dev->trigger_flag & (1<<MessageTrigger_DiagnosticsStatusNotification))
    {
        dev->trigger_flag &= ~(1<<MessageTrigger_DiagnosticsStatusNotification);
        if (dev->need_diagnostics == 1)
        {
            ocpp16_DiagnosticsStatusNotification_request(dev, DiagnosticsStatus_Uploading);
        }else{
            ocpp16_DiagnosticsStatusNotification_request(dev, DiagnosticsStatus_Uploaded);
        }
    }
    if (dev->trigger_flag & (1<<MessageTrigger_FirmwareStatusNotification))
    {
        dev->trigger_flag &= ~(1<<MessageTrigger_FirmwareStatusNotification);
        if (dev->need_updatefirmware == 1)
        {
            ocpp16_FirmwareStatusNotification_request(dev, FirmwareStatus_Downloading);
        }else{
            if (dev->need_reset !=0)
            {
                ocpp16_FirmwareStatusNotification_request(dev, FirmwareStatus_Installing);
            }else{
                ocpp16_FirmwareStatusNotification_request(dev, FirmwareStatus_Downloaded); 
            }
        }
    }
    if (dev->trigger_flag & (1<<MessageTrigger_Heartbeat))
    {
        dev->trigger_flag &= ~(1<<MessageTrigger_Heartbeat);
        HeartbeatReq_t req;
        HeartbeatConf_t conf;
        ocpp16_Heartbeat_request(dev, &req, &conf);
    }

    if (dev->trigger_flag & (1<<MessageTrigger_MeterValues))
    {
        dev->trigger_flag &= ~(1<<MessageTrigger_MeterValues);
        ocpp16_report_MeterValues(dev, seccA);
    }
    if (dev->trigger_flag & (1<<MessageTrigger_StatusNotification))
    {
        dev->trigger_flag &= ~(1<<MessageTrigger_StatusNotification);
        ocpp16_report_StatusNotification(dev, seccA->config->connectorId, seccA->GUN_Error, seccA->GUN_State);
    }
    if (dev->trigger_flag & (1<<MessageTrigger_MeterValues7))
    {
        dev->trigger_flag &= ~(1<<MessageTrigger_MeterValues7);
        ocpp16_report_MeterValues(dev, seccB);
    }
    if (dev->trigger_flag & (1<<MessageTrigger_StatusNotification8))
    {
        dev->trigger_flag &= ~(1<<MessageTrigger_StatusNotification8);
        ocpp16_report_StatusNotification(dev, seccB->config->connectorId, seccB->GUN_Error, seccB->GUN_State);
    }
}

static void ocpp16_Heartbeat(ocpp_client_t *dev)
{
    time_t now = time(NULL);
    if ((now - dev->HeartbeatTime) > dev->HeartbeatInterval)
    {
        dev->HeartbeatTime = now;
        HeartbeatReq_t req;
        HeartbeatConf_t conf;
        ocpp16_Heartbeat_request(dev, &req, &conf);
    }
}

static void ocpp16_BootNotification(ocpp_client_t *dev)
{
    if (dev->boot_notification_readay == 1) 
        return;
    dev->boot_notification_readay = 1;
    {
        BootNotificationReq_t req;
        memset(&req, 0, sizeof(req));
        sprintf(req.chargePointSerialNumber, "%s", ccu_config.CCU_SN);
        sprintf(req.chargePointVendor, "%s", "Tcharge");
        sprintf(req.chargePointModel, "%s", "Charge_Point_DC");
        sprintf(req.firmwareVersion, "%s", formatAppVersion());
        
        BootNotificationConf_t conf;
        ocpp16_BootNotification_request(dev, &req, &conf);
        if (conf.status == RegistrationStatus_Accepted)
        {
            dev->HeartbeatInterval = conf.interval;
            printd("conf.interval:%d\n", conf.interval);
            mlog_i(LOG_OCPP, "CS.datetime:%s", conf.currentTime);
            dev->HeartbeatInterval = 30; //服务器端65秒没有心跳会关闭TCP连接，65秒是抓包分析的时间
        }else{
            dev->HeartbeatInterval = ocpp16_configkeys.coreProfile.HeartbeatInterval;
        }
    }
    {
        StatusNotificationReq_t req;
        memset(&req, 0, sizeof(req));
        req.connectorId = 0;
        req.errorCode = ChargePointErrorCode_NoError;
        req.status = ChargePointStatus_Preparing;
        sprintf_UTCTime(req.timestamp, time(NULL));
        ocpp16_StatusNotification_request(dev, &req);

        ccu_port_t *ccu = (ccu_port_t*)dev->ccu;
        secc_port_t *seccA = (secc_port_t*)ccu->seccA;
        secc_port_t *seccB = (secc_port_t*)ccu->seccB;

        memset(&req, 0, sizeof(req));
        req.connectorId = 1;
        req.errorCode = ChargePointErrorCode_NoError;
        req.status = ChargePointStatus_Available;
        if (seccA->config->GUN_Available != true) req.status = ChargePointStatus_Unavailable;
        sprintf_UTCTime(req.timestamp, time(NULL));
        ocpp16_StatusNotification_request(dev, &req);

        memset(&req, 0, sizeof(req));
        req.connectorId = 2;
        req.errorCode = ChargePointErrorCode_NoError;
        req.status = ChargePointStatus_Available;
        if (seccB->config->GUN_Available != true) req.status = ChargePointStatus_Unavailable;
        sprintf_UTCTime(req.timestamp, time(NULL));
        ocpp16_StatusNotification_request(dev, &req);
    }
}

static void ocpp16_Upload_ChargingRecords(ocpp_client_t* dev)
{
    if (dev->ocpp16_offlineChargingRecords_uploaded == 1) return;
    ocpp16_OfflineChargingRecords_upload();
    dev->ocpp16_offlineChargingRecords_uploaded = 1;
}

void* ocpp16_TxLoop(void* argv)
{
    ocpp_client_t *dev = (ocpp_client_t *)argv;
    time_t now;
    ocpp16_config_t* config = (ocpp16_config_t*)dev->config;
    if (config->enable != 1) return NULL;

    while (dev->quit == 0)
    {
        sleep(1);
        now = time(NULL);
        if (dev->fd > 0 && dev->handshaked == 1)
        {
            ocpp16_BootNotification(dev);
            if (dev->boot_notification_readay == 1)
            {
                ocpp16_Heartbeat(dev);

                ocpp16_TryReset(dev);

                ocpp16_Diagnostics(dev);

                ocpp16_UpdateFirmware(dev);

                ocpp16_TriggerMessage(dev);

                ocpp16_Upload_ChargingRecords(dev);
            }
        }
        // printd("once.UTC:%lld\n", time(NULL));
    }
    return NULL;
}

void* ocpp16_RxLoop(void* args)
{
    ocpp_client_t* dev = (ocpp_client_t*)args;
    ocpp16_config_t* config = (ocpp16_config_t*)dev->config;
    while (dev->quit == 0)
    {
        if (dev->fd < 0)
        {
            if (ocpp16_websocket_client_connect(dev, config->connect_ip_string, config->connect_port) == 0)
            {
                if (ocpp16_websocket_client_handshake(dev) == 0)
                {
                }else{
                    ocpp16_websocket_exit(dev);
                }
            }else{
                sleep(ocpp16_configkeys.coreProfile.ConnectionTimeOut);
            }
        }
        if (ocpp16_websocket_recv_timeout(dev, 1000) > 0)
        {
            ocpp16_client_callback(dev, dev->rxbuff, dev->rxbyte, dev->rxtype);
        }
    }
    return NULL;
}

int ocpp16_client_online(ocpp_client_t* dev)
{
    if (dev)
    {
        if (dev->config->enable)
        {
            if (dev->connected)
            {
                if (dev->handshaked)
                {
                    return 1;
                }
            }
        }
    }
    return 0;
}

void ocpp16_client_init(ocpp_client_t* dev, ocpp16_config_t *config)
{
    printd("dev:%p, host:%s:%d, SN:%s, ssl:%d\n", dev, config->connect_ip_string, config->connect_port, ccu_config.CCU_SN, config->enable_ssl);
    if (dev)
    {
        dev->fd = -1;
        dev->debug = 1;
        dev->connected = 0;
        dev->handshaked = 0;
        dev->boot_notification_readay = 0;
        dev->ocpp16_offlineChargingRecords_uploaded = 0;
        dev->HeartbeatInterval = 2;

        dev->config = config;
        pthread_mutex_init(&dev->mutex_tx, NULL);
        pthread_mutex_init(&dev->mutex_rx, NULL);
        pthread_cond_init(&dev->cond_rx, NULL);
        sem_init(&dev->rxroot_sem, 0, 0);

        dev->quit = 0;
        mthread_init(&dev->threadRx, ocpp16_RxLoop, dev);
        mthread_init(&dev->threadTx, ocpp16_TxLoop, dev);
    }
}

void ocpp16_client_exit(ocpp_client_t* dev)
{
    if (dev)
    {
        dev->quit = 1;
        mthread_exit(&dev->threadRx);
        mthread_exit(&dev->threadTx);
        ocpp16_websocket_exit(dev);

        pthread_cond_destroy(&dev->cond_rx);
        pthread_mutex_destroy(&dev->mutex_tx);
        pthread_mutex_destroy(&dev->mutex_rx);
        sem_destroy(&dev->rxroot_sem);
    }
}
