/*
 * Module_CabinetParallel.c
 *
 *  Created on: 2021年9月28日
 *      Author: 7978
 */

#include    <sys/time.h>
#include    <sys/timeb.h>
#include    <sys/types.h>
#include    <sys/stat.h>
#include    <sys/types.h>
#include    <sys/ioctl.h>
#include    <sys/socket.h>
#include    <sys/ipc.h>
#include    <sys/shm.h>
#include    <sys/shm.h>
#include    <sys/mman.h>
#include    <linux/can.h>
#include    <linux/can/raw.h>
#include    <linux/wireless.h>
#include    <arpa/inet.h>
#include    <netinet/in.h>

#include    <unistd.h>
#include    <stdarg.h>
#include    <stdio.h>      /*標準輸入輸出定義*/
#include    <stdlib.h>     /*標準函數庫定義*/
#include    <unistd.h>     /*Unix 標準函數定義*/
#include    <fcntl.h>      /*檔控制定義*/
#include    <termios.h>    /*PPSIX 終端控制定義*/
#include    <errno.h>      /*錯誤號定義*/
#include    <errno.h>
#include    <string.h>
#include    <time.h>
#include    <ctype.h>
#include    <ifaddrs.h>
#include    <signal.h>
#include    <net/if_arp.h>
#include    "../../define.h"
#include    "Module_EvComm.h"
#include    "Module_CabinetParallel.h"
#include    "Config.h"
#include    "Common.h"

//==========================================
// Share Memory
//==========================================
struct SysConfigAndInfo             *ShmSysConfigAndInfo;
ChargerInfoData                     *ShmChargerInfo;
struct ChargingInfoData             *chargingInfo[CONNECTOR_QUANTITY];
PsuGroupCollectionData              *ShmGroupCollection;
struct PsuData                      *ShmPsuData;

//==========================================
// Local Variable
//==========================================
struct WARNING_CODE_INFO SlaveWarningInfo[MAX_SLAVE_CABINET_QUANTITY];
unsigned char _SCabinetUpdateState = 0;
unsigned char PCabinetUpdateRequest[MAX_SLAVE_CABINET_QUANTITY];

int _regReqLen[PARALLEL_CABINET_REG_QUANTITY] =
{
    REG_REQ_LEN_NONE,                   // reg: 0x00
    REG_REQ_LEN_MODELNAME,              // reg: 0x01
    REG_REQ_LEN_ID,                     // reg: 0x02
    REG_REQ_LEN_REG03,                  // reg: 0x03
    REG_REQ_LEN_STATUS,                 // reg: 0x04
    REG_REQ_LEN_OUTPUT_RELAY,           // reg: 0x05
    REG_REQ_LEN_AC_CONTACTOR,           // reg: 0x06
    REG_REQ_LEN_UPDATE,                 // reg: 0x07
    REG_REQ_LEN_PARALLEL_RELAY,         // reg: 0x08
};

int _regResLen[PARALLEL_CABINET_REG_QUANTITY] =
{
    REG_RES_LEN_NONE,                   // reg: 0x00
    REG_RES_LEN_MODELNAME,              // reg: 0x01
    REG_RES_LEN_ID,                     // reg: 0x02
    REG_RES_LEN_REG03,                  // reg: 0x03
    REG_RES_LEN_STATUS,                 // reg: 0x04
    REG_RES_LEN_OUTPUT_RELAY,           // reg: 0x05
    REG_RES_LEN_AC_CONTACTOR,           // reg: 0x06
    REG_RES_LEN_UPDATE,                 // reg: 0x07
    REG_RES_LEN_PARALLEL_RELAY,         // reg: 0x08
};

void ShowSocketData(struct PACKET_STRUCTURE *packet)
{
    printf("se: %02X, id: %02X, op: %d, len: %3d, reg: %02X",
        packet->Header.se, packet->Header.id, packet->Header.op, packet->Header.len, packet->Payload.reg);

    if(packet->Header.len > 1)
    {
        printf(", Data:");
        for(int i = 0; i < packet->Header.len - 1; i++)
        {
            printf(" %02X", packet->Payload.data[i]);
        }
    }
    printf("\n");
}

//==========================================
// Init all share memory
//==========================================
int InitShareMemory(void)
{
    int result = PASS;
    int MeterSMId;

    //creat ShmSysConfigAndInfo
    if ((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo), 0777)) < 0)
    {
        #ifdef SystemLogMessage
        LOG_ERROR("shmget ShmSysConfigAndInfo NG");
        #endif
        result = FAIL;
    }
    else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
        #ifdef SystemLogMessage
        LOG_ERROR("shmat ShmSysConfigAndInfo NG");
        #endif
        result = FAIL;
     }
    else
    {

    }

    if ((MeterSMId = shmget(SM_ChargerInfoKey, sizeof(ChargerInfoData), 0777)) < 0)
    {
        #ifdef SystemLogMessage
        LOG_ERROR("shmat ChargerInfoData NG");
        #endif
        result = FAIL;
    }
    else if ((ShmChargerInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
        #ifdef SystemLogMessage
        LOG_ERROR("shmat ChargerInfoData NG");
        #endif
        result = FAIL;
    }

    if ((MeterSMId = shmget(ShmPsuKey, sizeof(struct PsuData),  0777)) < 0)
    {
        #ifdef SystemLogMessage
        LOG_ERROR("shmget ShmPsuData NG");
        #endif
        result = FAIL;
    }
    else if ((ShmPsuData = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
        #ifdef SystemLogMessage
        LOG_ERROR("shmat ShmPsuData NG");
        #endif
        result = FAIL;
    }

    if(result == PASS)
    {
        ShmGroupCollection = &ShmChargerInfo->PsuGrouping.GroupCollection[0];
    }

    return result;
}

void InitialConnector(void)
{
    for(int i = 0; i < CONNECTOR_QUANTITY; i++)
    {
        chargingInfo[i] = &ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].GeneralChargingData;
    }
}

void SlaveCabinetCheckInUpdate(void)
{
    unsigned char slaveCnt = 0;

    for(int i = 0; i < MAX_SLAVE_CABINET_QUANTITY; i++)
    {
        if(ShmChargerInfo->ParallelCabinet.PCabinet[i].LocalStatus != _DS_None &&
            ShmChargerInfo->ParallelCabinet.PCabinet[i].LocalStatus != _DS_Timeout)
        {
            slaveCnt++;
        }
    }
    ShmChargerInfo->ParallelCabinet.PresentParallelCabinetQuantity = slaveCnt;
    if(slaveCnt > ShmChargerInfo->ParallelCabinet.ParallelCabinetQuantity)
    {
        ShmChargerInfo->ParallelCabinet.ParallelCabinetQuantity = slaveCnt;
    }
}

void SetSlaveCabinetStatus(unsigned char DeviceIndex, unsigned char status)
{
    if(DeviceIndex < MAX_SLAVE_CABINET_QUANTITY)
    {
        switch(status)
        {
            case _DS_Identification:
            case _DS_Alarm:
            case _DS_Charging:
                ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].LocalStatus = status;
                break;

            case _DS_Idle:
                if(ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].LocalStatus != status)
                {
                    ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].ParallelRequest = YES;
                }
                ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].LocalStatus = status;
                break;

            case _DS_Timeout:
                memset(&ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex], 0x00, sizeof(SlaveCabinetInfoData));
                ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].LocalStatus = status;
                break;

            case _DS_None:
            default:
                memset(&ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex], 0x00, sizeof(SlaveCabinetInfoData));
                break;
        }
    }
}

void SlaveCabinetIpConflictedCheck(void)
{
    unsigned char DuplicatedConfirm[MAX_SLAVE_CABINET_QUANTITY];
    struct timespec _Conflicted_Time[MAX_SLAVE_CABINET_QUANTITY];

    memset(DuplicatedConfirm, 0x00, MAX_SLAVE_CABINET_QUANTITY);

    while(1)
    {
        for(int i = 0; i < MAX_SLAVE_CABINET_QUANTITY; i++)
        {
            if(ShmChargerInfo->ParallelCabinet.PCabinet[i].IpDuplicatedFlag == true)
            {
                if(DuplicatedConfirm[i] == false)
                {
                    DuplicatedConfirm[i] = true;
                    GetClockTime(&_Conflicted_Time[i]);
                    LOG_INFO("Slave Cabinet %d IP Conflicted Confirm", i + 1);
                }
                else
                {
                    if((GetTimeoutValue(_Conflicted_Time[i]) / mSEC_VAL) >= IP_CONFLICTED_TIME)
                    {
                        // reach here only when MCabinetCommProcess is stopped
                        if(ShmChargerInfo->ParallelCabinet.PCabinet[i].LocalStatus != _DS_None &&
                            ShmChargerInfo->ParallelCabinet.PCabinet[i].LocalStatus != _DS_Timeout)
                        {
                            unsigned char channel = ShmChargerInfo->ParallelCabinet.PCabinet[i].SocketChannel;

                            if(ShmChargerInfo->CabinetConnInfo[channel].Status != _SCONN_FREE)
                            {
                                SetSlaveCabinetStatus(i, _DS_Timeout);
                                SlaveCabinetCheckInUpdate();
                                memset(&ShmChargerInfo->CabinetConnInfo[channel], 0x00, sizeof(SocketConnInfoData));
                                LOG_INFO("Cabinet ConnInfo Channel %d Clean", channel);
                            }
                        }
                        ShmChargerInfo->ParallelCabinet.PCabinet[i].IpDuplicatedFlag = false;
                        DuplicatedConfirm[i] = false;
                        LOG_INFO("Slave Cabinet %d IP Conflicted Stop", i + 1);
                    }
                }
            }
        }
        usleep(500000);
    }
}

int ConflictedCabinetCheck(void)
{
    pid_t forkId;

    forkId = fork();
    if(forkId == 0)
    {
        SlaveCabinetIpConflictedCheck();
        return forkId;
    }
    else if(forkId == -1)
    {
        LOG_INFO("fork fail");
    }

    return forkId;
}

int GetFreeSocketChannel(SocketConnInfoData *SocketConn, int Len)
{
    for(int i = 0; i < Len; i++)
    {
        if(SocketConn[i].Status == _SCONN_FREE)
        {
            return i;
        }
    }

    return FAIL;
}

bool IsConflictCabinetIp(uint32_t ipAddress)
{
    for(int i = 0; i < ARRAY_SIZE(ShmChargerInfo->CabinetConnInfo); i++)
    {
        if(ShmChargerInfo->CabinetConnInfo[i].IpAddress == ipAddress)
        {
            int slaveCabinet = ShmChargerInfo->CabinetConnInfo[i].DeviceIndex;
            if(slaveCabinet < MAX_SLAVE_CABINET_QUANTITY)
            {
                if(ShmChargerInfo->ParallelCabinet.PCabinet[slaveCabinet].IpDuplicatedFlag == false)
                {
                    ShmChargerInfo->ParallelCabinet.PCabinet[slaveCabinet].IpDuplicatedFlag = true;
                }
                return true;
            }
        }
    }
    return false;
}

bool FindSlaveCabinet(unsigned char DeviceIndex, unsigned char ID)
{
    bool find = false;

    if(ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].SlaveID == ID)
    {
        find = true;
    }

    return find;
}

void SendPacket(int socket, struct PACKET_STRUCTURE *packet)
{
    //ShowSocketData(packet);
    //send(socket, packet, packet->Header.len + 4, 0);
    send(socket, packet, packet->Header.len + 4, MSG_NOSIGNAL);
}

// Reg: 0x01, Slave Cabinet Model Name Handler
// allow slave cabinet switch value: 2 ~ 5
// allow slave cabinet ip address: 31 ~ 34
// return legal id: 1 ~ 4
unsigned char MCabinetModelNameHandler(unsigned char connIndex, struct PACKET_STRUCTURE *packet)
{
    unsigned char modelName[64];
    unsigned char cabinetId = 0;

    memset(modelName, 0x00, 64);
    memcpy(modelName, packet->Payload.data, packet->Header.len - 1);

    cabinetId = (ShmChargerInfo->CabinetConnInfo[connIndex].IpAddress >> 24) & 0xFF;
    if(cabinetId <= 30 || cabinetId > (30 + MAX_SLAVE_CABINET_QUANTITY))
    {
        LOG_INFO("Slave Cabinet DeviceID Out Of Range(%d), Model Name: %s", cabinetId, modelName);
        cabinetId = 0;
    }
    else
    {
        cabinetId -= 30;
        LOG_INFO("Slave Cabinet Check In, Device ID: %d, Model Name: %s", cabinetId, modelName);
        memcpy(ShmChargerInfo->ParallelCabinet.PCabinet[cabinetId - 1].ModelName, modelName, packet->Header.len - 1);
        ShmChargerInfo->ParallelCabinet.PCabinet[cabinetId - 1].SocketChannel = connIndex;
    }

    return cabinetId;
}

// Reg: 0x01, Slave Cabinet Model Name Response
void MCabinetModelNameResponse(int socket, struct PACKET_STRUCTURE *packet, unsigned char result)
{
    struct PACKET_STRUCTURE sendBuffer;

    memset(&sendBuffer, 0x00, sizeof(sendBuffer));
    sendBuffer.Header.se = packet->Header.se;
    sendBuffer.Header.id = 0xFF;
    sendBuffer.Header.op = _Header_Response;
    sendBuffer.Header.len = 2;
    sendBuffer.Payload.reg = _Reg_Dispenser_Model_Name;
    sendBuffer.Payload.data[0] = result;

    SendPacket(socket, &sendBuffer);
}

// Reg: 0x02, Slave Cabinet ID Response
void MCabinetIDResponse(int socket, struct PACKET_STRUCTURE *packet, unsigned char result, unsigned char DeviceIndex)
{
    struct PACKET_STRUCTURE sendBuffer;

    memset(&sendBuffer, 0x00, sizeof(sendBuffer));
    sendBuffer.Header.se = packet->Header.se;
    sendBuffer.Header.id = 0xFF;
    sendBuffer.Header.op = _Header_Response;
    sendBuffer.Header.len = 3;
    sendBuffer.Payload.reg = _Reg_SCabinet_ID;
    sendBuffer.Payload.data[0] = result;
    sendBuffer.Payload.data[1] = ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].SlaveID;

    SendPacket(socket, &sendBuffer);
}

// Reg: 0x04, Slave Cabinet Status Handler
bool MCabinetStatusHandler(struct PACKET_STRUCTURE *packet, unsigned char DeviceIndex)
{
    bool find = FindSlaveCabinet(DeviceIndex, packet->Header.id);
    bool chagne = false;
    struct WARNING_CODE_INFO warning;

    if(find)
    {
        memset(&warning, 0x00, sizeof(struct WARNING_CODE_INFO));

        warning.WarningCount = (packet->Header.len - 1) / 6;

        if(warning.WarningCount > 10)
        {
            warning.WarningCount = 10;
        }

        for(int i = 0; i < warning.WarningCount; i++)
        {
            memcpy(&warning.WarningCode[i][0], &packet->Payload.data[i * 6], 6);
        }

        if(SlaveWarningInfo[DeviceIndex].WarningCount != warning.WarningCount)
        {
            chagne = true;
        }
        else
        {
            if(warning.WarningCount != 0)
            {
                for(int i = 0; i < warning.WarningCount; i++)
                {
                    if(strcmp((char *)&SlaveWarningInfo[DeviceIndex].WarningCode[i][0], (char *)&warning.WarningCode[i][0]) != EQUAL)
                    {
                        chagne = true;
                        break;
                    }
                }
            }
        }

        if(chagne)
        {
            memcpy(&SlaveWarningInfo[DeviceIndex], &warning, sizeof(struct WARNING_CODE_INFO));

            char strSlaveDeviceStatus[128];
            sprintf(strSlaveDeviceStatus, "PCabinet %d, Status Code Len: %d",
                ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].SlaveID, SlaveWarningInfo[DeviceIndex].WarningCount);

            if(SlaveWarningInfo[DeviceIndex].WarningCount > 0)
            {
                for(int i = 0; i < SlaveWarningInfo[DeviceIndex].WarningCount; i++)
                {
                    char strTemp[16];
                    sprintf(strTemp, ", %s", SlaveWarningInfo[DeviceIndex].WarningCode[i]);
                    strcat(strSlaveDeviceStatus, strTemp);
                }
            }
            LOG_INFO("%s", strSlaveDeviceStatus);
        }
    }
    else
    {
        // do nothing
    }

    return find;
}

// Reg: 0x04, Slave Cabinet Status Response
void MCabinetStatusResponse(int socket, struct PACKET_STRUCTURE *packet, unsigned char result, unsigned char DeviceIndex)
{
    struct PACKET_STRUCTURE sendBuffer;

    memset(&sendBuffer, 0x00, sizeof(sendBuffer));
    sendBuffer.Header.se = packet->Header.se;
    sendBuffer.Header.id = packet->Header.id;
    sendBuffer.Header.op = _Header_Response;
    sendBuffer.Header.len = 2;
    sendBuffer.Payload.reg = _Reg_SCabinet_Status;
    sendBuffer.Payload.data[0] = result;

    SendPacket(socket, &sendBuffer);
}

// Reg: 0x05, Output Relay Status Handler
bool MCabinetOutputRelayHandler(struct PACKET_STRUCTURE *packet, unsigned char DeviceIndex)
{
    bool find = FindSlaveCabinet(DeviceIndex, packet->Header.id);
    bool chagne = false;
    unsigned char relayState[MAX_GROUP_QUANTITY];
    unsigned char *slaveRelayState;

    if(find)
    {
        if((packet->Header.len - 1) >= MAX_GROUP_QUANTITY)
        {
            slaveRelayState = &ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].GunOutputRelayState[0];
            memcpy(relayState, &packet->Payload.data, MAX_GROUP_QUANTITY);

            for(int i = 0; i < MAX_GROUP_QUANTITY; i++)
            {
                if(slaveRelayState[i] != relayState[i])
                {
                    chagne = true;
                    break;
                }
            }

            if(chagne)
            {
                memcpy(slaveRelayState, relayState, MAX_GROUP_QUANTITY);

                char strRelayStatus[128];
                sprintf(strRelayStatus, "PCabinet %d Output Relay", DeviceIndex + 1);
                for(int i = 0; i < MAX_GROUP_QUANTITY; i++)
                {
                    char strTemp[16];
                    sprintf(strTemp, " Gun %d %3s", i + 1, slaveRelayState[i] > 0 ? "ON" : "OFF");
                    strcat(strRelayStatus, strTemp);
                }
                LOG_INFO("%s", strRelayStatus);
            }
        }
    }
    else
    {
        // do nothing
    }

    return find;
}

// Reg: 0x05, Output Relay Status Response
void MCabinetOutputRelayResponse(int socket, struct PACKET_STRUCTURE *packet, unsigned char result, unsigned char DeviceIndex)
{
    struct PACKET_STRUCTURE sendBuffer;
    unsigned char *slaveRelayState;
    slaveRelayState = &ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].OutputRelaySetting[0];

    memset(&sendBuffer, 0x00, sizeof(sendBuffer));
    sendBuffer.Header.se = packet->Header.se;
    sendBuffer.Header.id = packet->Header.id;
    sendBuffer.Header.op = _Header_Response;
    sendBuffer.Header.len = 6;
    sendBuffer.Payload.reg = _Reg_OutputRelay_Status;
    sendBuffer.Payload.data[0] = result;
    sendBuffer.Payload.data[1] = result == _R_OK ? slaveRelayState[0] : Relay_OFF;
    sendBuffer.Payload.data[2] = result == _R_OK ? slaveRelayState[1] : Relay_OFF;
    sendBuffer.Payload.data[3] = result == _R_OK ? slaveRelayState[2] : Relay_OFF;
    sendBuffer.Payload.data[4] = result == _R_OK ? slaveRelayState[3] : Relay_OFF;

    SendPacket(socket, &sendBuffer);
}

// Reg: 0x06, Ac Contactor Status Handler
bool MCabinetAcContactorHandler(struct PACKET_STRUCTURE *packet, unsigned char DeviceIndex)
{
    bool find = FindSlaveCabinet(DeviceIndex, packet->Header.id);
    bool chagne = false;
    unsigned char contactorState;
    unsigned char *slaveContactorState;

    if(find)
    {
        if((packet->Header.len - 1) >= 1)
        {
            slaveContactorState = &ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].AcContactorState;
            contactorState = packet->Payload.data[0];

            if(*slaveContactorState != contactorState)
            {
                chagne = true;
            }

            if(chagne)
            {
                *slaveContactorState = contactorState;
                LOG_INFO("PCabinet %d AC Contactor State: %s", DeviceIndex + 1, *slaveContactorState > 0 ? "ON" : "OFF");
            }
        }
    }
    else
    {
        // do nothing
    }

    return find;
}

// Reg: 0x06, Ac Contactor Status Response
void MCabinetAcContactorResponse(int socket, struct PACKET_STRUCTURE *packet, unsigned char result, unsigned char DeviceIndex)
{
    struct PACKET_STRUCTURE sendBuffer;
    unsigned char slaveContactorState = ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].AcContactorSetting;

    memset(&sendBuffer, 0x00, sizeof(sendBuffer));
    sendBuffer.Header.se = packet->Header.se;
    sendBuffer.Header.id = packet->Header.id;
    sendBuffer.Header.op = _Header_Response;
    sendBuffer.Header.len = 3;
    sendBuffer.Payload.reg = _Reg_AcContactor_Status;
    sendBuffer.Payload.data[0] = result;
    sendBuffer.Payload.data[1] = result == _R_OK ? slaveContactorState : Relay_OFF;

    SendPacket(socket, &sendBuffer);
}

// Reg: 0x07, Slave Cabinet Firmware Upgrade Handler
bool MCabinetFwUpgradeHandler(struct PACKET_STRUCTURE *packet, unsigned char DeviceIndex)
{
    bool find = FindSlaveCabinet(DeviceIndex, packet->Header.id);
    bool chagne = false;
    unsigned char updateState;
    unsigned char *slaveUpdateState;
    char *strUpdate[] = {"Not Update", "Updating", "Updated"};

    if(find)
    {
        if((packet->Header.len - 1) >= 1)
        {
            slaveUpdateState = &ShmChargerInfo->ParallelCabinet.PCUpdateState[DeviceIndex];

            updateState = packet->Payload.data[0];

            if(*slaveUpdateState != updateState)
            {
                chagne = true;
            }

            if(chagne)
            {
                switch(updateState)
                {
                    case Updated:
                        ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].UpdateRequest = NO;
                        break;
                }
                *slaveUpdateState = updateState;
                LOG_INFO("PCabinet %d Update State: %s", DeviceIndex + 1, *slaveUpdateState <= Updated ? strUpdate[*slaveUpdateState] : "Unknown");
            }
        }
    }
    else
    {

    }

    return find;
}

// Reg: 0x07, Slave Cabinet Firmware Upgrade Response
void MCabinetUpgradeResponse(int socket, struct PACKET_STRUCTURE *packet, unsigned char result, unsigned char DeviceIndex)
{
    struct PACKET_STRUCTURE sendBuffer;
    unsigned char slaveUpdateRequest = ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].UpdateRequest;

    memset(&sendBuffer, 0x00, sizeof(sendBuffer));
    sendBuffer.Header.se = packet->Header.se;
    sendBuffer.Header.id = packet->Header.id;
    sendBuffer.Header.op = _Header_Response;
    sendBuffer.Header.len = 3;
    sendBuffer.Payload.reg = _Reg_SCabinet_FwUpdate;
    sendBuffer.Payload.data[0] = result;
    if(result == _R_OK)
    {
        sendBuffer.Payload.data[1] = slaveUpdateRequest > 0 ? _R_NeedUpgrade : _R_NoUpgrade;
        if(slaveUpdateRequest > 0)
        {
            int length = strlen(ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].FwFileName);
            memcpy(&sendBuffer.Payload.data[2], ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].FwFileName, length);
            sendBuffer.Header.len += length;
            if(PCabinetUpdateRequest[DeviceIndex] != slaveUpdateRequest)
            {
                LOG_INFO("MCabinet >> PCabinet %d Start Upgrade: %s", DeviceIndex + 1, ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].FwFileName);
            }
        }
        PCabinetUpdateRequest[DeviceIndex] = slaveUpdateRequest;
    }

    SendPacket(socket, &sendBuffer);
}

// Reg: 0x08, Parallel Relay Status Handler
bool MCabinetParallelRelayHandler(struct PACKET_STRUCTURE *packet, unsigned char DeviceIndex)
{
    bool find = FindSlaveCabinet(DeviceIndex, packet->Header.id);
    bool chagne = false;
    unsigned char relayState[MAX_GROUP_QUANTITY];
    unsigned char *slaveRelayState;

    if(find)
    {
        if((packet->Header.len - 1) >= MAX_GROUP_QUANTITY)
        {
            slaveRelayState = &ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].ParallelRelayState[0];
            memcpy(relayState, &packet->Payload.data, MAX_GROUP_QUANTITY);

            for(int i = 0; i < MAX_GROUP_QUANTITY; i++)
            {
                if(slaveRelayState[i] != relayState[i])
                {
                    chagne = true;
                    break;
                }
            }

            if(chagne)
            {
                memcpy(slaveRelayState, relayState, MAX_GROUP_QUANTITY);

                char strRelayStatus[128];
                sprintf(strRelayStatus, "PCabinet %d Parallel Relay", DeviceIndex + 1);
                for(int i = 0; i < MAX_GROUP_QUANTITY; i++)
                {
                    char strTemp[16];
                    sprintf(strTemp, " Location %d %3s", i + 1, slaveRelayState[i] > 0 ? "ON" : "OFF");
                    strcat(strRelayStatus, strTemp);
                }
                LOG_INFO("%s", strRelayStatus);
            }
        }
    }
    else
    {
        // do nothing
    }

    return find;
}

// Reg: 0x08, Parallel Relay Status Response
void MCabinetParallelRelayResponse(int socket, struct PACKET_STRUCTURE *packet, unsigned char result, unsigned char DeviceIndex)
{
    struct PACKET_STRUCTURE sendBuffer;
    unsigned char *slaveRelayState;
    slaveRelayState = &ShmChargerInfo->ParallelCabinet.PCabinet[DeviceIndex].ParallelRelaySetting[0];

    memset(&sendBuffer, 0x00, sizeof(sendBuffer));
    sendBuffer.Header.se = packet->Header.se;
    sendBuffer.Header.id = packet->Header.id;
    sendBuffer.Header.op = _Header_Response;
    sendBuffer.Header.len = 6;
    sendBuffer.Payload.reg = _Reg_ParallelRelay_Status;
    sendBuffer.Payload.data[0] = result;
    sendBuffer.Payload.data[1] = result == _R_OK ? slaveRelayState[0] : Relay_OFF;
    sendBuffer.Payload.data[2] = result == _R_OK ? slaveRelayState[1] : Relay_OFF;
    sendBuffer.Payload.data[3] = result == _R_OK ? slaveRelayState[2] : Relay_OFF;
    sendBuffer.Payload.data[4] = result == _R_OK ? slaveRelayState[3] : Relay_OFF;

    SendPacket(socket, &sendBuffer);
}

int BufferRePacket(int dataLength, unsigned char *buffer, struct PACKET_STRUCTURE *packet)
{
    int totalLength = 0, packetCnt = 0, singleLength = 0;
    struct PACKET_STRUCTURE *tempPacket;

    while(dataLength > 0)
    {
        tempPacket = (struct PACKET_STRUCTURE *)&buffer[totalLength];
        singleLength = tempPacket->Header.len + PACKET_HEADER_LENGTH;
        if(dataLength >= singleLength)
        {
            totalLength += singleLength;
            dataLength -= singleLength;
            memset(&packet[packetCnt], 0x00, sizeof(struct PACKET_STRUCTURE));
            memcpy(&packet[packetCnt], tempPacket, singleLength);
            packetCnt++;
        }
        else
        {
            LOG_INFO("Packet[%d] is incomplete(%d/%d)", packetCnt, dataLength, singleLength);
            dataLength = 0;
        }
    }

    return packetCnt;
}

void MCabinetPacketHandler(int socketFd, unsigned char socketIndex, struct PACKET_STRUCTURE *packet)
{
    unsigned char ackResult = _R_NG;
    unsigned char deviceIndex = 0;

    if(ShmChargerInfo->CabinetConnInfo[socketIndex].Status == _SCONN_WAIT)
    {
        // Reg: 0x01, Dispenser model name
        if(packet->Header.op == _Header_Write && packet->Payload.reg == _Reg_SCabinet_Model_Name)
        {
            unsigned char deviceID = MCabinetModelNameHandler(socketIndex, packet);

            if(deviceID > 0)
            {
                ackResult = _R_OK;
                deviceIndex = deviceID - 1;
            }

            MCabinetModelNameResponse(socketFd, packet, ackResult);
            ShmChargerInfo->CabinetConnInfo[socketIndex].Status = ackResult == _R_OK ? _SCONN_MATCHED : _SCONN_WAIT;
            ShmChargerInfo->CabinetConnInfo[socketIndex].DeviceIndex = deviceIndex;
            if(ackResult == _R_OK)
            {
                ShmChargerInfo->ParallelCabinet.PCabinet[deviceIndex].SlaveID = deviceID;
                SetSlaveCabinetStatus(deviceIndex, _DS_Identification);
                SlaveCabinetCheckInUpdate();
            }
        }
        else
        {
            // do nothing
        }

        // do not clear timeout value, then close socket when timeout value exceed SOCKET_RECEIVE_INTERVAL
    }
    else if(ShmChargerInfo->CabinetConnInfo[socketIndex].Status == _SCONN_MATCHED)
    {
        deviceIndex = ShmChargerInfo->CabinetConnInfo[socketIndex].DeviceIndex;

        // Reg: 0x01, Dispenser model name
        if(packet->Header.op == _Header_Write && packet->Payload.reg == _Reg_SCabinet_Model_Name)
        {
            ackResult = _R_OK;
            MCabinetModelNameResponse(socketFd, packet, ackResult);
        }

        // Reg: 0x02, Slave Cabinet ID
        if(packet->Header.op == _Header_Read && packet->Payload.reg == _Reg_SCabinet_ID)
        {
            ackResult = _R_OK;
            SetSlaveCabinetStatus(deviceIndex, _DS_Idle);
            MCabinetIDResponse(socketFd, packet, ackResult, deviceIndex);
        }

        // Reg: 0x04, Slave Cabinet Status
        if(packet->Header.op == _Header_Write && packet->Payload.reg == _Reg_SCabinet_Status)
        {
            if(MCabinetStatusHandler(packet, deviceIndex))
            {
                ackResult = _R_OK;
            }
            MCabinetStatusResponse(socketFd, packet, ackResult, deviceIndex);
        }

        // Reg: 0x05, Sync Output Relay Status
        if(packet->Header.op == _Header_Read && packet->Payload.reg == _Reg_OutputRelay_Status)
        {
            if(MCabinetOutputRelayHandler(packet, deviceIndex))
            {
                ackResult = _R_OK;
            }
            MCabinetOutputRelayResponse(socketFd, packet, ackResult, deviceIndex);
        }

        // Reg: 0x06, Sync Ac Contactor Status
        if(packet->Header.op == _Header_Read && packet->Payload.reg == _Reg_AcContactor_Status)
        {
            if(MCabinetAcContactorHandler(packet, deviceIndex))
            {
                ackResult = _R_OK;
            }
            MCabinetAcContactorResponse(socketFd, packet, ackResult, deviceIndex);
        }

        // Reg: 0x07, Fw Update
        if(packet->Header.op == _Header_Read && packet->Payload.reg == _Reg_SCabinet_FwUpdate)
        {
            if(MCabinetFwUpgradeHandler(packet, deviceIndex))
            {
                ackResult = _R_OK;
            }
            MCabinetUpgradeResponse(socketFd, packet, ackResult, deviceIndex);
        }

        // Reg: 0x08, Sync Parallel Relay Status
        if(packet->Header.op == _Header_Read && packet->Payload.reg == _Reg_ParallelRelay_Status)
        {
            if(MCabinetParallelRelayHandler(packet, deviceIndex))
            {
                ackResult = _R_OK;
            }
            MCabinetParallelRelayResponse(socketFd, packet, ackResult, deviceIndex);
        }
    }
}

void MCabinetCommProcess(int socketFd, struct sockaddr_in clientInfo, unsigned char socketIndex)
{
    int rxLen = 0, packetCnt = 0;
    unsigned char dataBuffer[MAX_DATA_BUFFER_LEN];
    struct PACKET_STRUCTURE packetBuffer[MAX_PACKET_BUFFER_LEN];
    struct timespec _MCabinet_timeout;

    LOG_INFO("Start Slave Cabinet Communication Process, IP %s connection(%d), PID = %d", (inet_ntoa(clientInfo.sin_addr)), socketIndex, getpid());

    GetClockTime(&_MCabinet_timeout);
    while(1)
    {
        if((rxLen = recv(socketFd, &dataBuffer[0], MAX_DATA_BUFFER_LEN, MSG_DONTWAIT )) > 0)
        {
            packetCnt = BufferRePacket(rxLen, &dataBuffer[0], &packetBuffer[0]);

            if(packetCnt > 0)
            {
                //LOG_INFO("Socket[%d] %d packet received", socketIndex, packetCnt);
                for(int i = 0; i < packetCnt; i++)
                {
                    //ShowSocketData(&packetBuffer[i]);
                    MCabinetPacketHandler(socketFd, socketIndex, &packetBuffer[i]);
                }

                if(ShmChargerInfo->CabinetConnInfo[socketIndex].Status == _SCONN_MATCHED)
                {
                    // clear timeout value
                    GetClockTime(&_MCabinet_timeout);
                }
            }
        }
        else
        {
            usleep((SOCKET_RECEIVE_INTERVAL * 1000));
        }

        if(GetTimeoutValue(_MCabinet_timeout) / uSEC_VAL >= MASTER_SLAVE_CABINET_TIMEOUT)
        {
            //timeout
            LOG_INFO("Slave Cabinet IP: %s, Socket %d Communication Timeout", (inet_ntoa(clientInfo.sin_addr)), socketIndex);
            if(ShmChargerInfo->CabinetConnInfo[socketIndex].Status == _SCONN_MATCHED)
            {
                SetSlaveCabinetStatus(ShmChargerInfo->CabinetConnInfo[socketIndex].DeviceIndex, _DS_Timeout);
                SlaveCabinetCheckInUpdate();
            }
            break;
        }

        if(ShmChargerInfo->CabinetConnInfo[socketIndex].Status == _SCONN_MATCHED)
        {
            if(ShmChargerInfo->ParallelCabinet.PCabinet[ShmChargerInfo->CabinetConnInfo[socketIndex].DeviceIndex].IpDuplicatedFlag == true)
            {
                LOG_INFO("Slave Cabinet %d has been kick", ShmChargerInfo->CabinetConnInfo[socketIndex].DeviceIndex + 1);
                SetSlaveCabinetStatus(ShmChargerInfo->CabinetConnInfo[socketIndex].DeviceIndex, _DS_Timeout);
                SlaveCabinetCheckInUpdate();
                break;
            }
        }
    }
}

void InitMasterCabinetInfo(void)
{
    for(int i = 0; i < MAX_SLAVE_CABINET_QUANTITY; i++)
    {
        memset(&SlaveWarningInfo[i], 0x00, sizeof(struct WARNING_CODE_INFO));
    }
}

bool IsReadyAcceptSlaveCabinet(void)
{
    bool ready = true;

    for(int i = 0; i < ShmChargerInfo->Control.MaxConnector; i++)
    {
        if (chargingInfo[i]->SystemStatus != S_IDLE && chargingInfo[i]->SystemStatus != S_MAINTAIN)
        {
            ready = false;
            break;
        }
    }

    return ready;
}

int MasterCabinetProcess(void)
{
    int sockFd = 0;
    int clientSockFd = 0;
    int connIndex = 0;
    int bindStatus = 0;
    int reuseaddr = 1;
    pid_t forkId;
    bool isConflict = false, isReady = false, preReady = false;

    struct sockaddr_in  serverInfo, clientInfo;
    socklen_t           addrlen = sizeof(clientInfo);

    sockFd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockFd == -1)
    {
        LOG_INFO("InitSocketServer NG\n");
        sleep(5);
        return FAIL;
    }

    setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));

    InitMasterCabinetInfo();

    bzero(&serverInfo,sizeof(serverInfo));
    serverInfo.sin_family = PF_INET;
    serverInfo.sin_addr.s_addr = htonl(INADDR_ANY);
    serverInfo.sin_port = htons(MASTER_TCP_LISTEN_PORT);
    bindStatus = bind(sockFd, (struct sockaddr *)&serverInfo, sizeof(serverInfo));
    if(bindStatus < 0)
    {
        LOG_INFO("Master Cabinet socket bind fail");
        return FAIL;
    }
    listen(sockFd, CONNECTION_LIMIT);

    LOG_INFO("Master Cabinet Server Start");

    signal(SIGCHLD, SIG_IGN);

    if(ConflictedCabinetCheck() <= 0)
    {
        // child process return here
        return 0;
    }

    // Main loop
    while(1)
    {
        clientSockFd = accept(sockFd, (struct sockaddr*) &clientInfo, &addrlen);

        isReady = IsReadyAcceptSlaveCabinet();
        if(isReady)
        {
            LOG_INFO("Slave Cabinet ip: %s is accepted at port: %d", inet_ntoa(clientInfo.sin_addr), clientInfo.sin_port);
        }
        else
        {
            // not ready
            if(preReady != isReady)
            {
                LOG_INFO("MCabinet Is Not Ready To Paralleling");
            }
            close(clientSockFd);
            preReady = isReady;
            continue;
        }
        preReady = isReady;

        connIndex = GetFreeSocketChannel(ShmChargerInfo->CabinetConnInfo, ARRAY_SIZE(ShmChargerInfo->CabinetConnInfo));
        if(connIndex >= 0)
        {
            isConflict = IsConflictCabinetIp(clientInfo.sin_addr.s_addr);

            if(isConflict == false)
            {
                ShmChargerInfo->CabinetConnInfo[connIndex].Status = _SCONN_WAIT;
                ShmChargerInfo->CabinetConnInfo[connIndex].IpAddress = clientInfo.sin_addr.s_addr;

                forkId = fork();
                if(forkId == 0)
                {
                    // child process
                    MCabinetCommProcess(clientSockFd, clientInfo, connIndex);
                    close(clientSockFd);
                    memset(&ShmChargerInfo->CabinetConnInfo[connIndex], 0x00, sizeof(SocketConnInfoData));
                    return 0;
                }
                else if(forkId == -1)
                {
                    LOG_INFO("Slave Cabinet Process Fork Fail");
                }
            }
            else
            {
                if(isConflict)
                {
                    // conflict ip address
                    LOG_INFO("Conflict Slave Cabinet IP address, Close Socket");
                }
                close(clientSockFd);
                memset(&ShmChargerInfo->CabinetConnInfo[connIndex], 0x00, sizeof(SocketConnInfoData));
            }
        }
        else
        {
            // no free channel
            LOG_INFO("No Free Cabinet Channel, Close Socket");
            close(clientSockFd);
        }

        usleep(10000);
    }

    return 0;
}

// ****************************** Slave ****************************** //
unsigned char _modelNameAck = 0;
unsigned char _idAck = 0;
unsigned char _header_SE = 0;

struct timespec _regQreuyTime[PARALLEL_CABINET_REG_QUANTITY];

int _regTimeout[PARALLEL_CABINET_REG_QUANTITY] =
{
    REG_RSEND_TIME_NONE,                // reg: 0x00
    REG_RSEND_TIME_MODELNAME,           // reg: 0x01
    REG_RSEND_TIME_ID,                  // reg: 0x02
    REG_RSEND_TIME_REG03,               // reg: 0x03
    REG_RSEND_TIME_STATUS,              // reg: 0x04
    REG_RSEND_TIME_OUTPUT_RELAY,        // reg: 0x05
    REG_RSEND_TIME_AC_CONTACTOR,        // reg: 0x06
    REG_RSEND_TIME_UPDATE,              // reg: 0x07
    REG_RSEND_TIME_PARALLEL_RELAY,      // reg: 0x08
};

int ConnectToMasterCabinet(void)
{
    int sockFd = 0;
    struct sockaddr_in serverInfo;
    struct timeval tv;

    sockFd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockFd < 0)
    {
        LOG_INFO("Create Slave Cabinet Socket Fail");
        return -1;
    }

    tv.tv_sec = 3;
    tv.tv_usec = 0;
    setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)); //設定等待3s
    setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(struct timeval));  //設定傳送3s

    bzero(&serverInfo,sizeof(serverInfo));
    serverInfo.sin_family = AF_INET;
    serverInfo.sin_addr.s_addr = inet_addr(MASTER_ETH1_IP);
    serverInfo.sin_port = htons(MASTER_TCP_LISTEN_PORT);

    if(connect(sockFd, (struct sockaddr *)&serverInfo, sizeof(serverInfo)) < 0)
    {
        //LOG_INFO("Connect To Master Cabinet Error[%d]", errno);
        close(sockFd);
        return -1;
    }

    return sockFd;
}

void SendParallelPacket(int fd, unsigned char opCode, unsigned char length, unsigned char reg, unsigned char *data)
{
    struct PACKET_STRUCTURE packet;

    packet.Header.se = _header_SE++;
    packet.Header.id = ShmChargerInfo->SCabinetControl.SCabinetID;
    packet.Header.op = opCode;
    packet.Header.len = length;
    packet.Payload.reg = reg;

    if(length > 1)
    {
        memcpy(packet.Payload.data, data, length - 1);
    }

    SendPacket(fd, &packet);
}

// reg: 0x01, Slave Cabinet Write Model Name
void SCabinetWriteModelName(int fd)
{
    if(GetTimeoutValue(_regQreuyTime[_Reg_SCabinet_Model_Name]) / mSEC_VAL >= _regTimeout[_Reg_SCabinet_Model_Name])
    {
        SendParallelPacket(
            fd,
            _Header_Write,
            strlen((char *)ShmSysConfigAndInfo->SysConfig.ModelName) + 1,
            _Reg_SCabinet_Model_Name,
            ShmSysConfigAndInfo->SysConfig.ModelName);

        GetClockTime(&_regQreuyTime[_Reg_SCabinet_Model_Name]);
        //LOG_INFO("SCabinet Write Model Nname...");
    }
}

// reg: 0x01, Slave Cabinet Model Name Handler
unsigned char SCabinetModelNameHandler(struct PACKET_STRUCTURE *packet)
{
    unsigned char ack = _R_NG;

    if(packet->Header.len >= _regResLen[_Reg_SCabinet_Model_Name])
    {
        ack = packet->Payload.data[0] == _R_OK ? _R_OK : _R_NG;

        if(_modelNameAck != ack)
        {
            LOG_INFO("SCabinet Write Model Name %s", ack == _R_OK ? "OK" : "NG");
        }
        _modelNameAck = ack;
    }

    return ack;
}

// reg: 0x02, Slave Cabinet Read ID
void SCabinetReadID(int fd)
{
    if(GetTimeoutValue(_regQreuyTime[_Reg_SCabinet_ID]) / mSEC_VAL >= _regTimeout[_Reg_SCabinet_ID])
    {
        SendParallelPacket(
            fd,
            _Header_Read,
            _regReqLen[_Reg_SCabinet_ID],
            _Reg_SCabinet_ID,
            0);

        GetClockTime(&_regQreuyTime[_Reg_SCabinet_ID]);
        //LOG_INFO("SCabinet Read ID...");
    }
}

// reg: 0x02, Slave Cabinet ID Handler
unsigned char SCabinetIDHandler(struct PACKET_STRUCTURE *packet)
{
    unsigned char ack = _R_NG;
    unsigned char id = 0;

    if(packet->Header.len >= _regResLen[_Reg_SCabinet_ID])
    {
        ack = packet->Payload.data[0] == _R_OK ? _R_OK : _R_NG;

        if(ack == _R_OK)
        {
            id = packet->Payload.data[1];

            if(ShmChargerInfo->SCabinetControl.SCabinetID != id && id != 0)
            {
                ShmChargerInfo->SCabinetControl.SCabinetID = id;
                LOG_INFO("Get SCabinet ID: %d", ShmChargerInfo->SCabinetControl.SCabinetID);
            }
        }

        if(_idAck != ack)
        {
            LOG_INFO("SCabinet Read ID %s", ack == _R_OK ? "OK" : "NG");
        }
    }

    return ack;
}

// reg: 0x04
void SCabinetWriteStatus(int fd)
{
    bool chagne = false;
    unsigned char statusCode[256];

    if(ShmSysConfigAndInfo->SysWarningInfo.WarningCount != SlaveWarningInfo[0].WarningCount)
    {
        chagne = true;
    }
    else
    {
        if(ShmSysConfigAndInfo->SysWarningInfo.WarningCount != 0)
        {
            for(int i = 0; i < ShmSysConfigAndInfo->SysWarningInfo.WarningCount; i++)
            {
                if(strcmp((char *)&SlaveWarningInfo[0].WarningCode[i][0], (char *)&ShmSysConfigAndInfo->SysWarningInfo.WarningCode[i][0]) != EQUAL)
                {
                    chagne = true;
                    break;
                }
            }
        }
    }

    if(chagne)
    {
        memcpy(&SlaveWarningInfo[0], &ShmSysConfigAndInfo->SysWarningInfo, sizeof(struct WARNING_CODE_INFO));
    }

    if(GetTimeoutValue(_regQreuyTime[_Reg_SCabinet_Status]) / mSEC_VAL >= _regTimeout[_Reg_SCabinet_Status] || chagne)
    {
        for(int i = 0; i < ShmSysConfigAndInfo->SysWarningInfo.WarningCount; i++)
        {
            memcpy(&statusCode[i * 6], &ShmSysConfigAndInfo->SysWarningInfo.WarningCode[i][0], 6);
        }

        SendParallelPacket(
            fd,
            _Header_Write,
            (ShmSysConfigAndInfo->SysWarningInfo.WarningCount * 6) + 1,
            _Reg_SCabinet_Status,
            &statusCode[0]);

        GetClockTime(&_regQreuyTime[_Reg_SCabinet_Status]);
        //LOG_INFO("SCabinet Write Status...");
    }
}

// reg: 0x04, Slave Cabinet Status Handler
unsigned char SCabinetStatusHandler(struct PACKET_STRUCTURE *packet)
{
    unsigned char ack = _R_NG;

    if(packet->Header.id != ShmChargerInfo->SCabinetControl.SCabinetID)
    {
        return ack;
    }
    if(packet->Header.len >= _regResLen[_Reg_SCabinet_Status])
    {
        ack = packet->Payload.data[0] == _R_OK ? _R_OK : _R_NG;
    }

    return ack;
}

// reg: 0x05
void SCabinetReadOutputRelay(int fd)
{
    unsigned char outputRelay[GENERAL_GUN_QUANTITY] = {0};

    if(GetTimeoutValue(_regQreuyTime[_Reg_OutputRelay_Status]) / mSEC_VAL >= _regTimeout[_Reg_OutputRelay_Status])
    {
        memcpy(&outputRelay[0], &ShmChargerInfo->SCabinetControl.SOutputRelay[0], GENERAL_GUN_QUANTITY);

        SendParallelPacket(
            fd,
            _Header_Read,
            _regReqLen[_Reg_OutputRelay_Status],
            _Reg_OutputRelay_Status,
            &outputRelay[0]);

        GetClockTime(&_regQreuyTime[_Reg_OutputRelay_Status]);
        //LOG_INFO("SCabinet Read OutputRelay...");
    }
}

// reg: 0x05, Slave Cabinet Output Relay Handler
unsigned char SCabinetOutputRelayHandler(struct PACKET_STRUCTURE *packet)
{
    bool chagne = false;
    unsigned char ack = _R_NG;

    if(packet->Header.id != ShmChargerInfo->SCabinetControl.SCabinetID)
    {
        return ack;
    }
    if(packet->Header.len >= _regResLen[_Reg_OutputRelay_Status])
    {
        ack = packet->Payload.data[0] == _R_OK ? _R_OK : _R_NG;

        if(ack == _R_OK)
        {
            for(int i = 0; i < GENERAL_GUN_QUANTITY; i++)
            {
                if(packet->Payload.data[i + 1] != ShmChargerInfo->SCabinetControl.SOutputRelay[i])
                {
                    chagne = true;
                }
            }

            memcpy(&ShmChargerInfo->SCabinetControl.SOutputRelay[0], &packet->Payload.data[1], GENERAL_GUN_QUANTITY);

            if(chagne)
            {
                LOG_INFO("SCabinet[%d] Need Set OutputRelay: %3s %3s %3s %3s",
                    ShmChargerInfo->SCabinetControl.SCabinetID,
                    ShmChargerInfo->SCabinetControl.SOutputRelay[0] > 0 ? "ON" : "OFF",
                    ShmChargerInfo->SCabinetControl.SOutputRelay[1] > 0 ? "ON" : "OFF",
                    ShmChargerInfo->SCabinetControl.SOutputRelay[2] > 0 ? "ON" : "OFF",
                    ShmChargerInfo->SCabinetControl.SOutputRelay[3] > 0 ? "ON" : "OFF");
            }
        }
    }

    return ack;
}

// reg: 0x06
void SCabinetReadAcContactor(int fd)
{
    unsigned char acContactor = 0;
    if(GetTimeoutValue(_regQreuyTime[_Reg_AcContactor_Status]) / mSEC_VAL >= _regTimeout[_Reg_AcContactor_Status])
    {
        acContactor = ShmChargerInfo->SCabinetControl.SAcContactor;

        SendParallelPacket(
            fd,
            _Header_Read,
            _regReqLen[_Reg_AcContactor_Status],
            _Reg_AcContactor_Status,
            &acContactor);

        GetClockTime(&_regQreuyTime[_Reg_AcContactor_Status]);
        //LOG_INFO("SCabinet Read AcContactor...");
    }
}

// reg: 0x06, Slave Cabinet Ac Contactor Handler
unsigned char SCabinetAcContactorHandler(struct PACKET_STRUCTURE *packet)
{
    bool chagne = false;
    unsigned char ack = _R_NG;

    if(packet->Header.id != ShmChargerInfo->SCabinetControl.SCabinetID)
    {
        return ack;
    }
    if(packet->Header.len >= _regResLen[_Reg_AcContactor_Status])
    {
        ack = packet->Payload.data[0] == _R_OK ? _R_OK : _R_NG;

        if(ack == _R_OK)
        {
            if(packet->Payload.data[1] != ShmChargerInfo->SCabinetControl.SAcContactor)
            {
                chagne = true;
            }

            ShmChargerInfo->SCabinetControl.SAcContactor = packet->Payload.data[1];

            if(chagne)
            {
                LOG_INFO("SCabinet[%d] Need Set AC Contactor: %3s",
                    ShmChargerInfo->SCabinetControl.SCabinetID,
                    ShmChargerInfo->SCabinetControl.SAcContactor > 0 ? "ON" : "OFF");
            }
        }
    }

    return ack;
}

// reg: 0x07
void SCabinetReadFwUpdate(int fd)
{
    bool chagne = false;

    if(_SCabinetUpdateState != ShmChargerInfo->SCabinetControl.UpgradeState)
    {
        chagne = true;
    }
    _SCabinetUpdateState = ShmChargerInfo->SCabinetControl.UpgradeState;

    if(GetTimeoutValue(_regQreuyTime[_Reg_SCabinet_FwUpdate]) / mSEC_VAL >= _regTimeout[_Reg_SCabinet_FwUpdate] || chagne)
    {
        SendParallelPacket(
            fd,
            _Header_Read,
            _regReqLen[_Reg_SCabinet_FwUpdate],
            _Reg_SCabinet_FwUpdate,
            &_SCabinetUpdateState);

        GetClockTime(&_regQreuyTime[_Reg_SCabinet_FwUpdate]);
    }
}

// reg: 0x07, Slave Cabinet FW Update Handler
unsigned char SCabinetFwUpdateHandler(struct PACKET_STRUCTURE *packet)
{
    bool chagne = false;
    unsigned char update = _R_NoUpgrade;
    unsigned char ack = _R_NG;
    unsigned char image[128];

    if(packet->Header.id != ShmChargerInfo->SCabinetControl.SCabinetID)
    {
        return ack;
    }
    if(packet->Header.len >= _regResLen[_Reg_SCabinet_FwUpdate])
    {
        ack = packet->Payload.data[0] == _R_OK ? _R_OK : _R_NG;

        if(ack == _R_OK)
        {
            update = packet->Payload.data[1] == _R_NeedUpgrade ? _R_NeedUpgrade : _R_NoUpgrade;

            if(update == _R_NeedUpgrade && ShmChargerInfo->SCabinetControl.UpgradeState == Not_Update)
            {
                if(packet->Header.len > _regResLen[_Reg_SCabinet_FwUpdate])
                {
                    memset(&image[0], 0x00, sizeof(image));
                    memcpy(&image[0], &packet->Payload.data[2], packet->Header.len - _regResLen[_Reg_SCabinet_FwUpdate]);
                }
                else
                {
                    LOG_INFO("SCabinet[%d] %s image length fail: %d", ShmChargerInfo->SCabinetControl.SCabinetID, packet->Header.len);
                    update = _R_NoUpgrade;
                }
            }

            if(update != ShmChargerInfo->SCabinetControl.NeedUpgrade)
            {
                chagne = true;
            }
            ShmChargerInfo->SCabinetControl.NeedUpgrade = update;

            if(chagne)
            {
                LOG_INFO("SCabinet[%d] Update Indicator [%s]", ShmChargerInfo->SCabinetControl.SCabinetID,
                    ShmChargerInfo->SCabinetControl.NeedUpgrade == _R_NeedUpgrade ? "Need Upgrade" : "No Update");

                if(ShmChargerInfo->SCabinetControl.UpgradeState == Not_Update &&
                    ShmChargerInfo->SCabinetControl.NeedUpgrade == _R_NeedUpgrade)
                {
                    LOG_INFO("SCabinet[%d] Get Image: [%s]", ShmChargerInfo->SCabinetControl.SCabinetID, image);
                    memcpy(ShmChargerInfo->SCabinetControl.FwFileName, image, sizeof(image));
                    ShmSysConfigAndInfo->SysInfo.FirmwareUpdate = YES;
                }

                if(ShmChargerInfo->SCabinetControl.UpgradeState == Updated &&
                    ShmChargerInfo->SCabinetControl.NeedUpgrade == _R_NoUpgrade)
                {
                    LOG_INFO("MCabinet & SCabinet[%d] upgrade completed", ShmChargerInfo->SCabinetControl.SCabinetID);
                }
            }
        }
    }

    return ack;
}

// reg: 0x08
void SCabinetReadParallelRelay(int fd)
{
    unsigned char parallelRelay[GENERAL_GUN_QUANTITY] = {0};

    if(GetTimeoutValue(_regQreuyTime[_Reg_ParallelRelay_Status]) / mSEC_VAL >= _regTimeout[_Reg_ParallelRelay_Status])
    {
        memcpy(&parallelRelay[0], &ShmChargerInfo->SCabinetControl.SParallelRelay[0], GENERAL_GUN_QUANTITY);

        SendParallelPacket(
            fd,
            _Header_Read,
            _regReqLen[_Reg_ParallelRelay_Status],
            _Reg_ParallelRelay_Status,
            &parallelRelay[0]);

        GetClockTime(&_regQreuyTime[_Reg_ParallelRelay_Status]);
        //LOG_INFO("SCabinet Read ParallelRelay...");
    }
}

// reg: 0x08, Slave Cabinet Parallel Relay Handler
unsigned char SCabinetParallelRelayHandler(struct PACKET_STRUCTURE *packet)
{
    bool chagne = false;
    unsigned char ack = _R_NG;

    if(packet->Header.id != ShmChargerInfo->SCabinetControl.SCabinetID)
    {
        return ack;
    }
    if(packet->Header.len >= _regResLen[_Reg_ParallelRelay_Status])
    {
        ack = packet->Payload.data[0] == _R_OK ? _R_OK : _R_NG;

        if(ack == _R_OK)
        {
            for(int i = 0; i < GENERAL_GUN_QUANTITY; i++)
            {
                if(packet->Payload.data[i + 1] != ShmChargerInfo->SCabinetControl.SParallelRelay[i])
                {
                    chagne = true;
                }
            }

            memcpy(&ShmChargerInfo->SCabinetControl.SParallelRelay[0], &packet->Payload.data[1], GENERAL_GUN_QUANTITY);

            if(chagne)
            {
                LOG_INFO("SCabinet[%d] Need Set ParallelRelay: %3s %3s %3s %3s",
                    ShmChargerInfo->SCabinetControl.SCabinetID,
                    ShmChargerInfo->SCabinetControl.SParallelRelay[0] > 0 ? "ON" : "OFF",
                    ShmChargerInfo->SCabinetControl.SParallelRelay[1] > 0 ? "ON" : "OFF",
                    ShmChargerInfo->SCabinetControl.SParallelRelay[2] > 0 ? "ON" : "OFF",
                    ShmChargerInfo->SCabinetControl.SParallelRelay[3] > 0 ? "ON" : "OFF");
            }
        }
    }

    return ack;
}

void SCabinetPacketHandler(struct PACKET_STRUCTURE *packet)
{
    // Reg: 0x01, Dispenser model name
    if(packet->Header.op == _Header_Response && packet->Payload.reg == _Reg_SCabinet_Model_Name)
    {
        SCabinetModelNameHandler(packet);
    }
    // Reg: 0x02, Slave Cabinet ID
    if(packet->Header.op == _Header_Response && packet->Payload.reg == _Reg_SCabinet_ID)
    {
        SCabinetIDHandler(packet);
    }

    // Reg: 0x04, Slave Cabinet Status
    if(packet->Header.op == _Header_Response && packet->Payload.reg == _Reg_SCabinet_Status)
    {
        SCabinetStatusHandler(packet);
    }

    // Reg: 0x05, Sync Output Relay Status
    if(packet->Header.op == _Header_Response && packet->Payload.reg == _Reg_OutputRelay_Status)
    {
        SCabinetOutputRelayHandler(packet);
    }

    // Reg: 0x06, Sync Ac Contactor Status
    if(packet->Header.op == _Header_Response && packet->Payload.reg == _Reg_AcContactor_Status)
    {
        SCabinetAcContactorHandler(packet);
    }

    // Reg: 0x07, Fw Update
    if(packet->Header.op == _Header_Response && packet->Payload.reg == _Reg_SCabinet_FwUpdate)
    {
        SCabinetFwUpdateHandler(packet);
    }

    // Reg: 0x08, Sync Parallel Relay Status
    if(packet->Header.op == _Header_Response && packet->Payload.reg == _Reg_ParallelRelay_Status)
    {
        SCabinetParallelRelayHandler(packet);
    }
}

int SlaveCabinetProcess(void)
{
    int slavefd = 0, rxLen = 0, packetCnt = 0;
    //struct PACKET_STRUCTURE receiveBuffer;
    unsigned char dataBuffer[MAX_DATA_BUFFER_LEN];
    struct PACKET_STRUCTURE packetBuffer[MAX_PACKET_BUFFER_LEN];

    unsigned char Status = 0xFF, preStatus = 0xFF;
    struct timespec _slaveCabinetStatus_time;
    struct timespec _SCabinet_timeout;

    ShmChargerInfo->SCabinetControl.SCabinetStatus = _DeviceStatus_DisConnect;
    ShmChargerInfo->SCabinetControl.SCabinetID = 0xFF;
    _modelNameAck = 0;
    _idAck = 0;
    _header_SE = 0;
    ShmChargerInfo->SCabinetControl.SCabinetID = 0xFF;

    slavefd = socket(AF_INET, SOCK_STREAM, 0);
    if (slavefd == -1)
    {
        LOG_INFO("Create Slave Cabinet Socket Fail");
        return -1;
    }
    LOG_INFO("Slave Cabinet Client Start, PID = %d", getpid());

    while(1)
    {
        // **************************************** transmit ****************************************
        Status = ShmChargerInfo->SCabinetControl.SCabinetStatus;

        //LOG_INFO("SCabinet Status: %d", Status);
        switch(Status)
        {
            case _DeviceStatus_DisConnect:
                if(preStatus != Status)
                {
                    _modelNameAck = 0;
                    _idAck = 0;
                    _header_SE = 0x00;
                    ShmChargerInfo->SCabinetControl.SCabinetID = 0xFF;
                    memset(&SlaveWarningInfo[0], 0x00, sizeof(struct WARNING_CODE_INFO));

                    LOG_INFO("Slave Cabinet [Disconnect]");
                    GetClockTime(&_slaveCabinetStatus_time);
                }
                slavefd = ConnectToMasterCabinet();
                if(slavefd > 0)
                {
                    GetClockTime(&_SCabinet_timeout);
                    ShmChargerInfo->SCabinetControl.SCabinetStatus = _DeviceStatus_Identification;
                    LOG_INFO("Connecting to MCabinet...");
                    break;
                }

                if(GetTimeoutValue(_slaveCabinetStatus_time) / uSEC_VAL >= DISCONNECT_PROMPT)
                {
                    LOG_INFO("SCabinet Connecting...");
                    GetClockTime(&_slaveCabinetStatus_time);
                }
                break;
            case _DeviceStatus_Identification:
                if(preStatus != Status)
                {
                    _modelNameAck = 0;
                    _idAck = 0;
                    _header_SE = 0x00;
                    ShmChargerInfo->SCabinetControl.SCabinetID = 0xFF;
                    memset(&SlaveWarningInfo[0], 0x00, sizeof(struct WARNING_CODE_INFO));
                    GetClockTime(&_SCabinet_timeout);

                    LOG_INFO("Slave Cabinet [Identification]");
                    GetClockTime(&_slaveCabinetStatus_time);
                }

                if(_modelNameAck != _R_OK)
                {
                    SCabinetWriteModelName(slavefd);
                }
                if(ShmChargerInfo->SCabinetControl.SCabinetID == 0xFF || ShmChargerInfo->SCabinetControl.SCabinetID == 0)
                {
                    SCabinetReadID(slavefd);
                }

                if(_modelNameAck == _R_OK && ShmChargerInfo->SCabinetControl.SCabinetID != 0xFF && ShmChargerInfo->SCabinetControl.SCabinetID != 0)
                {
                    ShmChargerInfo->SCabinetControl.SCabinetStatus = _DeviceStatus_Idle;
                }
                break;
            case _DeviceStatus_Idle:
                if(preStatus != Status)
                {
                    LOG_INFO("Slave Cabinet [Idle]");
                    GetClockTime(&_slaveCabinetStatus_time);
                }

                SCabinetWriteStatus(slavefd);
                SCabinetReadOutputRelay(slavefd);
                SCabinetReadAcContactor(slavefd);
                SCabinetReadFwUpdate(slavefd);
                SCabinetReadParallelRelay(slavefd);
                break;
            case _DeviceStatus_Alarm:
                if(preStatus != Status)
                {
                    LOG_INFO("Slave Cabinet [Alarm]");
                    GetClockTime(&_slaveCabinetStatus_time);
                }
                break;
            case _DeviceStatus_Charging:
                break;
            case _DeviceStatus_Timeout:
                if(preStatus != Status)
                {
                    LOG_INFO("Slave Cabinet Retry [Timeout]");
                    GetClockTime(&_slaveCabinetStatus_time);
                }
                ShmChargerInfo->SCabinetControl.SCabinetStatus = _DeviceStatus_RetryWait;
                break;
            case _DeviceStatus_RetryWait:
                if(preStatus != Status)
                {
                    LOG_INFO("Slave Cabinet Retry [Wait]");
                    GetClockTime(&_slaveCabinetStatus_time);
                }

                if(GetTimeoutValue(_slaveCabinetStatus_time) / uSEC_VAL >= 3)
                {
                    ShmChargerInfo->SCabinetControl.SCabinetStatus = _DeviceStatus_DisConnect;
                }
                break;
            default:
                break;
        }
        preStatus = Status;

        // **************************************** receive  ****************************************
        if(ShmChargerInfo->SCabinetControl.SCabinetStatus != _DeviceStatus_DisConnect &&
            ShmChargerInfo->SCabinetControl.SCabinetStatus != _DeviceStatus_Timeout &&
            ShmChargerInfo->SCabinetControl.SCabinetStatus != _DeviceStatus_RetryWait)
        {
            if((rxLen = recv(slavefd, &dataBuffer[0], MAX_DATA_BUFFER_LEN, MSG_DONTWAIT )) > 0)
            {
                packetCnt = BufferRePacket(rxLen, &dataBuffer[0], &packetBuffer[0]);

                if(packetCnt > 0)
                {
                    //LOG_INFO("SCabinet %d packet received", packetCnt);
                    for(int i = 0; i < packetCnt; i++)
                    {
                        //ShowSocketData(&packetBuffer[i]);
                        SCabinetPacketHandler(&packetBuffer[i]);
                    }

                    // clear timeout value
                    GetClockTime(&_SCabinet_timeout);
                }
            }
        }

        // **************************************** timeout  ****************************************
        if(ShmChargerInfo->SCabinetControl.SCabinetStatus != _DeviceStatus_DisConnect &&
            ShmChargerInfo->SCabinetControl.SCabinetStatus != _DeviceStatus_Timeout &&
            ShmChargerInfo->SCabinetControl.SCabinetStatus != _DeviceStatus_RetryWait)
        {
            if(GetTimeoutValue(_SCabinet_timeout) / uSEC_VAL >= MASTER_SLAVE_CABINET_TIMEOUT)
            {
                if(slavefd > 0)
                {
                    close(slavefd);
                }
                LOG_INFO("SCabinet timeout, close socket...");
                ShmChargerInfo->SCabinetControl.SCabinetStatus = _DeviceStatus_Timeout;
            }
        }
        usleep((SOCKET_RECEIVE_INTERVAL * 1000));
    }

    return 0;
}

int main(void)
{
    if(InitShareMemory() == FAIL)
    {
        #ifdef SystemLogMessage
        LOG_ERROR("InitShareMemory NG");
        #endif

        sleep(5);
        return 0;
    }

    InitialConnector();

    // wait for self test completed
    while(ShmSysConfigAndInfo->SysInfo.BootingStatus == BOOTTING ||
        ShmSysConfigAndInfo->SysInfo.SelfTestSeq != _STEST_COMPLETE ||
        ShmChargerInfo->Control.CabinetRole == _CROLE_SINGLE)
    {
        sleep(1);
    }

    if(ShmChargerInfo->Control.CabinetRole == _CROLE_MASTER)
    {
        MasterCabinetProcess();
    }
    else if(ShmChargerInfo->Control.CabinetRole == _CROLE_SLAVE)
    {
        SlaveCabinetProcess();
    }

    return 0;
}