#include "ast_XH.h"
#include "tc_tcu.h"
#include "config.h"

int ccu_AST_init(ast_xh_t* dev, const char* devname)
{
    if (dev)
    {
        pthread_mutex_init(&dev->mutex, NULL);
        dev->quit = 0;
        com_ttySx_init(&dev->tty, devname, 9600, 8, 'N', 1);
        com_ttySx_rs485_enable(&dev->tty, 1);
        dev->addr = 0x10;
        dev->stat1[0] = 0x3;
        dev->stat2[0] = 0x4;
        dev->power_allocated[0] = 0x3;

        dev->stat1[1] = 0x3;
        dev->stat2[1] = 0x4;
        dev->power_allocated[1] = 0x3;

        dev->need_send_ctrl_cmd = 0;
        dev->GUNA_ELockCtrl = 2;
        dev->GUNB_ELockCtrl = 2;
        dev->GUNA_AuxPowerCtrl = 2;
        dev->GUNB_AuxPowerCtrl = 2;

        dev->GUNA_ELockState = 1;
        dev->GUNB_ELockState = 1;
        dev->GUNA_AuxPowerState = 1;
        dev->GUNB_AuxPowerState = 1;

        dev->GUNAState = 1;
        dev->GUNBState = 1;

        dev->ast_stage[0] = 0;
        dev->ast_stage[1] = 0;
        dev->ast_type[0] = 0;
        dev->ast_type[1] = 0;
        dev->ast_check[0] = 0;
        dev->ast_check[1] = 0;
        return 0;
    }
    return -1;
}

int ccu_AST_responseA(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd, uint8_t res)
{
    dev->txbytes = 0;
    dev->txbuff[dev->txbytes++] = addr;
    dev->txbuff[dev->txbytes++] = ~addr;
    dev->txbuff[dev->txbytes++] = 4;
    dev->txbuff[dev->txbytes++] = 1;
    dev->txbuff[dev->txbytes++] = seq;
    dev->txbuff[dev->txbytes++] = cmd;
    dev->txbuff[dev->txbytes++] = res;
    uint16_t crct = modbus_crc16(dev->txbuff, 7);
    dev->txbuff[dev->txbytes++] = (crct>>0)&0xFF;
    dev->txbuff[dev->txbytes++] = (crct>>8)&0xFF;
    
    return dev->txbytes;
}

int ccu_AST_responseB(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd, uint8_t res, uint16_t data)
{
    dev->txbytes = 0;
    dev->txbuff[dev->txbytes++] = addr;
    dev->txbuff[dev->txbytes++] = ~addr;
    dev->txbuff[dev->txbytes++] = 6;
    dev->txbuff[dev->txbytes++] = 1;
    dev->txbuff[dev->txbytes++] = seq;
    dev->txbuff[dev->txbytes++] = cmd;
    dev->txbuff[dev->txbytes++] = res;
    dev->txbuff[dev->txbytes++] = (data>>8)&0xFF;
    dev->txbuff[dev->txbytes++] = (data>>0)&0xFF;
    uint16_t crct = modbus_crc16(dev->txbuff, 9);
    dev->txbuff[dev->txbytes++] = (crct>>0)&0xFF;
    dev->txbuff[dev->txbytes++] = (crct>>8)&0xFF;
    
    return dev->txbytes;
}

int ccu_AST_cmd0_NoOperation(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    // printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);
    if (dev->rxbuff[2]>=16)
    {
        uint16_t maxI = (dev->rxbuff[5]<<8)|dev->rxbuff[6];
        uint16_t currI = (dev->rxbuff[7]<<8)|dev->rxbuff[8];
        uint16_t currV = (dev->rxbuff[9]<<8)|dev->rxbuff[10];
        uint32_t totalE = (dev->rxbuff[11]<<24)|(dev->rxbuff[12]<<16)|(dev->rxbuff[13]<<8)|dev->rxbuff[14];
        uint32_t currE = (dev->rxbuff[15]<<24)|(dev->rxbuff[16]<<16)|(dev->rxbuff[17]<<8)|dev->rxbuff[18];
        printf("addr=%02x, maxI:%5hd .1A, currI:%5hd .1A, currV:%5hd .1V, currE:%9d kWh, totalE:%9d Wh\n", addr, maxI, currI, currV, currE, totalE);
        if (addr==0x11) { maxI = tcu_config.modbus_hw.maxI_GUNA*10; ccu_Update_GUNA_IVE(maxI, currI, currV, totalE, currE); }
        if (addr==0x12) { maxI = tcu_config.modbus_hw.maxI_GUNB*10; ccu_Update_GUNB_IVE(maxI, currI, currV, totalE, currE); }
    }
    if (dev->rxbuff[2]>=18)
    {
        uint8_t stage = dev->rxbuff[19];
        uint8_t type = dev->rxbuff[20];
        // printd("addr=%02x, stage:%d, type:%d\n", addr, stage, type);
        if (addr==0x11) { 
            if (dev->ast_stage[0]!=stage && stage==AST_IDLE)         {ccu_Update_GUNA_WorkState(WORKSTATE_IDLE);   ccu_Update_GUNA_LinkState(LINKSTATE_HALF); ccu_GUNA_ShuntdownRequest(SRR_NORMAL);}
            if (dev->ast_stage[0]!=stage && stage==AST_PLUGIN)       {ccu_Update_GUNA_WorkState(WORKSTATE_PLUGIN); ccu_Update_GUNA_LinkState(LINKSTATE_FULL); }
            if (dev->ast_stage[0]!=stage && stage==AST_END)          {ccu_Update_GUNA_WorkState(WORKSTATE_END);    ccu_Update_GUNA_LinkState(LINKSTATE_FULL); ccu_GUNA_ShuntdownRequest(SRR_NORMAL);}
            if (dev->ast_stage[0]!=stage && stage==AST_FAULT)        {ccu_Update_GUNA_WorkState(WORKSTATE_FAULT);  ccu_Update_GUNA_LinkState(LINKSTATE_HALF); ccu_GUNA_ShuntdownRequest(SRR_GUN_SECC_ERR); }
            if (dev->ast_stage[0]!=stage && stage==AST_PLUGIN_FAULT) {ccu_Update_GUNA_WorkState(WORKSTATE_FAULT);  ccu_Update_GUNA_LinkState(LINKSTATE_FULL); ccu_GUNA_ShuntdownRequest(SRR_GUN_SECC_ERR);}
            if (dev->ast_type[0] != type) { if (type==1) ccu_Update_GUNA_Type(4); if (type==2) ccu_Update_GUNA_Type(1); dev->ast_type[0] = type; }
            dev->ast_stage[0] = stage;
            switch (dev->ast_stage[0])
            {
            case AST_FAULT:             ccu_GUNA_LED_State(0);break;
            case AST_PLUGIN:            ccu_GUNA_LED_State(1);break;
            case AST_PLUGIN_FAULT:      ccu_GUNA_LED_State(0);break;
            case AST_STARTING:          ccu_GUNA_LED_State(2);break;
            case AST_INSULATION:        ccu_GUNA_LED_State(2);break;
            case AST_PRECHARGE:         ccu_GUNA_LED_State(2);break;
            case AST_CHARGING:          ccu_GUNA_LED_State(2);break;
            case AST_END:               ccu_GUNA_LED_State(1);break;
            default:                    ccu_GUNA_LED_State(1);break;
            }
        }
        if (addr==0x12) { 
            if (dev->ast_stage[1]!=stage && stage==AST_IDLE)         {ccu_Update_GUNB_WorkState(WORKSTATE_IDLE);   ccu_Update_GUNB_LinkState(LINKSTATE_HALF); ccu_GUNB_ShuntdownRequest(SRR_NORMAL);}
            if (dev->ast_stage[1]!=stage && stage==AST_PLUGIN)       {ccu_Update_GUNB_WorkState(WORKSTATE_PLUGIN); ccu_Update_GUNB_LinkState(LINKSTATE_FULL); }
            if (dev->ast_stage[1]!=stage && stage==AST_END)          {ccu_Update_GUNB_WorkState(WORKSTATE_END);    ccu_Update_GUNB_LinkState(LINKSTATE_FULL); ccu_GUNB_ShuntdownRequest(SRR_NORMAL);}
            if (dev->ast_stage[1]!=stage && stage==AST_FAULT)        {ccu_Update_GUNB_WorkState(WORKSTATE_FAULT);  ccu_Update_GUNB_LinkState(LINKSTATE_HALF); ccu_GUNB_ShuntdownRequest(SRR_GUN_SECC_ERR);}
            if (dev->ast_stage[1]!=stage && stage==AST_PLUGIN_FAULT) {ccu_Update_GUNB_WorkState(WORKSTATE_FAULT);  ccu_Update_GUNB_LinkState(LINKSTATE_FULL); ccu_GUNB_ShuntdownRequest(SRR_GUN_SECC_ERR);}
            if (dev->ast_type[1] != type) { if (type==1) ccu_Update_GUNB_Type(4); if (type==2) ccu_Update_GUNB_Type(1); dev->ast_type[1] = type; }
            dev->ast_stage[1] = stage;
            switch (dev->ast_stage[1])
            {
            case AST_FAULT:             ccu_GUNB_LED_State(0);break;
            case AST_PLUGIN:            ccu_GUNB_LED_State(1);break;
            case AST_PLUGIN_FAULT:      ccu_GUNB_LED_State(0);break;
            case AST_STARTING:          ccu_GUNB_LED_State(2);break;
            case AST_INSULATION:        ccu_GUNB_LED_State(2);break;
            case AST_PRECHARGE:         ccu_GUNB_LED_State(2);break;
            case AST_CHARGING:          ccu_GUNB_LED_State(2);break;
            case AST_END:               ccu_GUNB_LED_State(1);break;
            default:                    ccu_GUNB_LED_State(1);break;
            }
        }
    }

    return ccu_AST_responseA(dev, addr, seq, cmd, 1);//accept
}

int ccu_AST_cmd1_Allocate(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[7];
    uint16_t currP =  (dev->rxbuff[5]<<8)|dev->rxbuff[6];
    printd("addr=%02x, more_cmd:%d, currP:%d\n", addr, dev->more_cmd, currP);
    if (addr == 0x11) { dev->power_allocated[0] = currP;}
    if (addr == 0x12) { dev->power_allocated[1] = currP;}

    if (addr == 0x11) { ccu_GUNA_PowerRequest(PRT_ACDC_PLACEHOLDER, PRM_CONSTANT_CURRENT, 1000, 100); dev->ast_check[0]=0;}
    if (addr == 0x12) { ccu_GUNB_PowerRequest(PRT_ACDC_PLACEHOLDER, PRM_CONSTANT_CURRENT, 1000, 100); dev->ast_check[1]=0;}
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);
}

int ccu_AST_cmd2_Deallocate(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[5];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);
    if (addr == 0x11) { ccu_GUNA_PowerRequest(PRT_ACDC_PLACEHOLDER, PRM_CONSTANT_CURRENT, 0, 0); dev->ast_check[0]=0;}
    if (addr == 0x12) { ccu_GUNB_PowerRequest(PRT_ACDC_PLACEHOLDER, PRM_CONSTANT_CURRENT, 0, 0); dev->ast_check[1]=0;}
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);
}

int ccu_AST_cmd3_SetVoltCurr(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[9];
    uint16_t currV =  (dev->rxbuff[5]<<8)|dev->rxbuff[6];
    uint16_t currI =  (dev->rxbuff[7]<<8)|dev->rxbuff[8];
    
    if (addr == 0x11) { dev->currV[0] = ((float)currV)/10; dev->currI[0] = ((float)currI)/10; }
    if (addr == 0x12) { dev->currV[1] = ((float)currV)/10; dev->currI[1] = ((float)currI)/10; }
    if (addr==0x11) { 
        printd("addr=%02x, more_cmd:%d, ast_stage:%d, V:%9f V, I:%9f A\n", addr, dev->more_cmd, dev->ast_stage[0], dev->currV[0], dev->currI[0]);
        if (dev->ast_stage[0]==AST_INSULATION && dev->ast_check[0]==0){ccu_Update_GUNA_WorkState(WORKSTATE_STARTING); float maxI = (dev->currI[0] < 30? 30:dev->currI[0]); ccu_GUNA_PowerRequest(PRT_INSLUATION_DETECTION_START, PRM_CONSTANT_CURRENT, dev->currV[0], maxI); dev->ast_check[0]=1;}
        if (dev->ast_stage[0]==AST_PRECHARGE){ ccu_Update_GUNA_WorkState(WORKSTATE_STARTING); ccu_GUNA_PowerRequest(PRT_PRECHARGING, PRM_CONSTANT_CURRENT, dev->currV[0], dev->currI[0]); }
        if (dev->ast_stage[0]==AST_CHARGING ){ ccu_Update_GUNA_WorkState(WORKSTATE_CHARGING); ccu_GUNA_PowerRequest(PRT_CHARGING, PRM_CONSTANT_CURRENT, dev->currV[0], dev->currI[0]); }
    }
    if (addr==0x12) { 
        printd("addr=%02x, more_cmd:%d, ast_stage:%d, V:%9f V, I:%9f A\n", addr, dev->more_cmd, dev->ast_stage[1], dev->currV[1], dev->currI[1]);
        if (dev->ast_stage[1]==AST_INSULATION && dev->ast_check[1]==0){ ccu_Update_GUNB_WorkState(WORKSTATE_STARTING); float maxI = (dev->currI[1] < 30? 30:dev->currI[1]); ccu_GUNB_PowerRequest(PRT_INSLUATION_DETECTION_START, PRM_CONSTANT_CURRENT, dev->currV[1], maxI); dev->ast_check[1]=1;}
        if (dev->ast_stage[1]==AST_PRECHARGE){ ccu_Update_GUNA_WorkState(WORKSTATE_STARTING); ccu_GUNB_PowerRequest(PRT_PRECHARGING, PRM_CONSTANT_CURRENT, dev->currV[1], dev->currI[1]); }
        if (dev->ast_stage[1]==AST_CHARGING ){ ccu_Update_GUNB_WorkState(WORKSTATE_CHARGING); ccu_GUNB_PowerRequest(PRT_CHARGING, PRM_CONSTANT_CURRENT, dev->currV[1], dev->currI[1]); }
    } 
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);
}

int ccu_AST_cmd4_Startup(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[5];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);
    return ccu_AST_responseA(dev, addr, seq, cmd, 1); //accept
}

int ccu_AST_cmd5_Poweroff(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[5];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);
    
    if (addr == 0x11 && dev->ast_check[0]==1) { ccu_GUNA_PowerRequest(PRT_INSLUATION_DETECTION_STOP, PRM_CONSTANT_CURRENT, 0, 0); dev->ast_check[0]=0;}
    if (addr == 0x12 && dev->ast_check[1]==1) { ccu_GUNB_PowerRequest(PRT_INSLUATION_DETECTION_STOP, PRM_CONSTANT_CURRENT, 0, 0); dev->ast_check[1]=0;}
    return ccu_AST_responseA(dev, addr, seq, cmd, 1); //accept
}
int ccu_AST_cmd6_CloseOutContactor(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[5];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);
    
    if (addr == 0x11) { ccu_GUNA_Ctrl_K2(1); ccu_GUNA_Ctrl_K1(1); }
    if (addr == 0x12) { ccu_GUNB_Ctrl_K2(1); ccu_GUNB_Ctrl_K1(1); }
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);//accept
}
int ccu_AST_cmd7_DisconnectOutContactor(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[5];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);
    
    if (addr == 0x11) { ccu_GUNA_Ctrl_K1(0); ccu_GUNA_Ctrl_K2(0);}
    if (addr == 0x12) { ccu_GUNB_Ctrl_K1(0); ccu_GUNB_Ctrl_K2(0);}
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);//accept
}
int ccu_AST_cmd8_LockGun(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[5];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);
    
    if (addr == 0x11){ccu_AST_GUNA_ELock(dev, 1);}
    if (addr == 0x12){ccu_AST_GUNB_ELock(dev, 1);}
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);//accept
}
int ccu_AST_cmd9_UnlockGun(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[5];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);
    
    if (addr == 0x11){ccu_AST_GUNA_ELock(dev, 0);}
    if (addr == 0x12){ccu_AST_GUNB_ELock(dev, 0);}
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);//accept
}
int ccu_AST_cmd10_TurnOnAuxPower(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    uint8_t param = dev->rxbuff[5];
    dev->more_cmd = dev->rxbuff[6];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);

    if (addr == 0x11){ccu_AST_GUNA_AuxPower(dev, 1);}
    if (addr == 0x12){ccu_AST_GUNB_AuxPower(dev, 1);}
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);//accept
}
int ccu_AST_cmd11_TurnOffAuxPower(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    uint8_t param = dev->rxbuff[5];
    dev->more_cmd = dev->rxbuff[6];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);

    if (addr == 0x11){ccu_AST_GUNA_AuxPower(dev, 0);}
    if (addr == 0x12){ccu_AST_GUNB_AuxPower(dev, 0);}
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);//accept
}

int ccu_AST_cmd15_SetHiLowVoltMode(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    dev->more_cmd = dev->rxbuff[6];
    printd("addr=%02x, more_cmd:%d\n", addr, dev->more_cmd);
    if (addr == 0x11){ dev->LowVoltMode[0] = dev->rxbuff[5];}
    if (addr == 0x12){ dev->LowVoltMode[1] = dev->rxbuff[5];}
    
    return ccu_AST_responseA(dev, addr, seq, cmd, 1);//accept
}

//----------------------------------------------------------------------------------------
int ccu_AST_cmd100_QueryOutContactor(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    uint16_t state = 0;
    dev->more_cmd = dev->rxbuff[5];
    
    if (addr == 0x11) { state |= ccu_GUNA_Query_K1_State()<<8; state |= ccu_GUNA_Query_K2_State(); }
    if (addr == 0x12) { state |= ccu_GUNB_Query_K1_State()<<8; state |= ccu_GUNB_Query_K2_State(); }

    printd("addr=%02x, more_cmd:%d, state:%04x\n", addr, dev->more_cmd, state);
    
    return ccu_AST_responseB(dev, addr, seq, cmd, 1, state);
}

int ccu_AST_cmd101_QueryPowerAlloc(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    uint16_t state = 0;
    dev->more_cmd = dev->rxbuff[5];
    
    if (addr == 0x11) { state = dev->power_allocated[0]; }
    if (addr == 0x12) { state = dev->power_allocated[1]; }

    printd("addr=%02x, more_cmd:%d, power_allocated:%04x\n", addr, dev->more_cmd, state);
    
    return ccu_AST_responseB(dev, addr, seq, cmd, 1, state);
}

int ccu_AST_cmd102_QueryGunLockState(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    uint16_t state = 0;
    dev->more_cmd = dev->rxbuff[5];
    
    if (addr == 0x11) { state = (dev->GUNA_ELockState==0? 1:0); state |= 0xFF00; }
    if (addr == 0x12) { state = (dev->GUNB_ELockState==0? 1:0); state |= 0xFF00; }

    printd("addr=%02x, more_cmd:%d, state:%04x\n", addr, dev->more_cmd, state);
    
    return ccu_AST_responseB(dev, addr, seq, cmd, 1, state);
}

int ccu_AST_cmd103_QueryAuxPowerState(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    uint16_t state = 0;
    dev->more_cmd = dev->rxbuff[5];

    if (addr == 0x11) { state = (dev->GUNA_AuxPowerState==0? 1:0); state |= 0xFF00;}
    if (addr == 0x12) { state = (dev->GUNB_AuxPowerState==0? 1:0); state |= 0xFF00; }

    printd("addr=%02x, more_cmd:%d, state:%04x\n", addr, dev->more_cmd, state);

    return ccu_AST_responseB(dev, addr, seq, cmd, 1, state);
}

int ccu_AST_cmd104_QueryOutVoltage(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    uint16_t state = 0;
    dev->more_cmd = dev->rxbuff[5];
    
    if (addr = 0x11) { state = (short)ccu_GUNA_Query_OutputVoltage(); }
    if (addr = 0x12) { state = (short)ccu_GUNB_Query_OutputVoltage(); }

    printd("addr=%02x, more_cmd:%d, voltage:%hd\n", addr, dev->more_cmd, state);
    
    return ccu_AST_responseB(dev, addr, seq, cmd, 1, state*10);
}

int ccu_AST_cmd105_QueryHiLowVoltMode(ast_xh_t* dev, uint8_t addr, uint8_t seq, uint8_t cmd)
{
    uint16_t state = 0;
    dev->more_cmd = dev->rxbuff[5];
    
    if (addr == 0x11) { state = (dev->LowVoltMode[0]==1? 1:0); state|= 0xFF00; }
    if (addr == 0x12) { state = (dev->LowVoltMode[1]==1? 1:0); state|= 0xFF00; }

    printd("addr=%02x, more_cmd:%d, state:%04x\n", addr, dev->more_cmd, state);
    
    return ccu_AST_responseB(dev, addr, seq, cmd, 1, state);
}


int ccu_AST_recv_with_timeout(ast_xh_t* dev, int minbytes, int timeout, int try)
{
    fd_set rset;
    struct timeval tv;
    int64_t timeout_ms = systime_ms() + timeout;

    if (dev == NULL) return -1;
    com_ttySx_t *tty = &dev->tty;
    if (tty->fd < 0) return -2;

    if (minbytes < 3) minbytes = 3;
    dev->rxbytes = 0;
    dev->more_cmd = 0;
    while(dev->rxbytes < minbytes)
    {
        int64_t remain_ms = timeout_ms - systime_ms();
        FD_ZERO(&rset);
        FD_SET(tty->fd, &rset);
        tv.tv_sec = remain_ms/1000;
        tv.tv_usec = (remain_ms%1000) * 1000;
        switch(select(tty->fd + 1, &rset, NULL, NULL, &tv))
        {
            case 0:
                printe("timeout rxbytes:%d\n",dev->rxbytes);
                try--; if (try<=0) return errno;
                comm_ttySx_reset(tty);
                com_ttySx_send(tty, dev->txbuff, dev->txbytes);
                dev->rxbytes = 0;
                break;
            case -1:
                printe("tty:%s\n",tty->devname);
                return errno;
            default:
                if(FD_ISSET(tty->fd, &rset))
                {
                    int readb = read(tty->fd, &dev->rxbuff[dev->rxbytes], (minbytes - dev->rxbytes));
                    if (readb > 0)
                    {
                        dev->rxbytes += readb;
                        if (dev->rxbytes >= 3)
                        {
                            uint8_t rxbuff1 = ~dev->rxbuff[1];
                            if (dev->rxbuff[0] == rxbuff1)
                            {
                                minbytes = 3 + dev->rxbuff[2] + 2;
                            }else{
                                printe("rxbytes:%d\n",dev->rxbytes);
                                try--; if (try<=0) return errno;
                                comm_ttySx_reset(tty);
                                com_ttySx_send(tty, dev->txbuff, dev->txbytes);
                                dev->rxbytes = 0;
                            }
                        } 
                    }else{
                        printe("tty:%s\n",tty->devname);
                        return errno;
                    }
                }else{
                    printe("tty:%s\n",tty->devname);
                    return errno;
                }
        }
    }
    // if (dev->rxbytes > 0){char title[64];
    //     sprintf(title, "%s->recv",dev->tty.devname);
    //     printline(title, dev->rxbuff, dev->rxbytes);}
    return dev->rxbytes;
}



int ccu_AST_queryTemperature(ast_xh_t* dev, uint8_t addr, uint8_t rw, uint8_t ALock, uint8_t BLock, uint8_t APowerAux, uint8_t BPowerAux)
{
    int i;
    dev->txbytes = 0;
    dev->txbuff[dev->txbytes++] = addr;
    dev->txbuff[dev->txbytes++] = ~addr;
    dev->txbuff[dev->txbytes++] = 8;
    dev->txbuff[dev->txbytes++] = 0x42;
    dev->txbuff[dev->txbytes++] = 0x00;
    dev->txbuff[dev->txbytes++] = 0x04;
    dev->txbuff[dev->txbytes++] = rw;
    dev->txbuff[dev->txbytes++] = ALock;
    dev->txbuff[dev->txbytes++] = BLock;
    dev->txbuff[dev->txbytes++] = APowerAux;
    dev->txbuff[dev->txbytes++] = BPowerAux;
    uint16_t crct = modbus_crc16(dev->txbuff, 11);
    dev->txbuff[dev->txbytes++] = (crct>>0)&0xFF;
    dev->txbuff[dev->txbytes++] = (crct>>8)&0xFF;
    // com_ttySx_rs485_control(&dev->tty, 1);
    // printline("temp", dev->txbuff, dev->txbytes);
    if (com_ttySx_send(&dev->tty, dev->txbuff, dev->txbytes) == dev->txbytes)
    {
        // com_ttySx_rs485_control(&dev->tty, 0);
        dev->rxbytes = 0;
        ccu_AST_recv_with_timeout(dev, 3, 500, 3);
        if (dev->rxbytes == 17)
        {
            memmove(&dev->rxbuff[1], &dev->rxbuff[0], dev->rxbytes);
            dev->rxbuff[0] = ~dev->rxbuff[1];
            dev->rxbytes++;
        }
        // if (dev->rxbytes>0) printline("uart recv", dev->rxbuff, dev->rxbytes);
        if (dev->rxbytes >= 18)
        {
            uint8_t rxbuff1 = ~dev->rxbuff[1];
            // printd("(%d:%s-->minbytes:%d), %02x, %02x \n",dev->rxbytes, dev->rxbuff[0], rxbuff1);
            if (dev->rxbuff[0] == rxbuff1)
            {
                uint16_t crcr = (dev->rxbuff[17]<<8)|dev->rxbuff[16];
                uint16_t crcx = modbus_crc16(dev->rxbuff, 16);
                // printd("(%d:%s-->minbytes:%d)\n",minbytes);
                if (crcr != crcx)
                {
                    printe("crcr[%04x] != crcx[%04x]\n",crcr, crcx);
                    dev->rxbytes = 0;
                    return 0;
                }
                if (dev->rxbytes >= (3 + dev->rxbuff[2] + 2))
                {
                    dev->temp[0] = dev->rxbuff[3];
                    dev->temp[1] = dev->rxbuff[4];
                    dev->temp[2] = dev->rxbuff[5];
                    dev->temp[3] = dev->rxbuff[6];
                    dev->temp[4] = dev->rxbuff[7];
                    dev->temp[5] = dev->rxbuff[8];

                    // printf("---T1:%2d, T2:%2d, T3:%2d, T4:%2d, T5:%2d, T6:%2d\n", dev->temp[0], dev->temp[1], dev->temp[2], dev->temp[3], dev->temp[4], dev->temp[5]);

                    if (dev->emgstate != 0x7F && dev->rxbuff[9] == 0x7F) 
                    {
                        ccu_Update_GUNA_WorkState(WORKSTATE_FAULT);
                        ccu_Update_GUNB_WorkState(WORKSTATE_FAULT);
                        ccu_GUNA_ShuntdownRequest(SRR_GUN_EMGSTOP);
                        ccu_GUNB_ShuntdownRequest(SRR_GUN_EMGSTOP);
                    }
                    dev->emgstate = dev->rxbuff[9];

                    // if (dev->GUNAState == 1 && dev->rxbuff[10]==0 && (dev->ast_stage[0]==AST_IDLE || dev->ast_stage[0]==AST_PLUGIN || dev->ast_stage[0]==AST_FAULT || dev->ast_stage[0]==AST_PLUGIN_FAULT || dev->ast_stage[0]==AST_END))
                    // {   //PLUGIN
                    //     printd("GUNAState:%d\n ", dev->GUNAState);
                    //     ccu_Update_GUNA_WorkState(WORKSTATE_PLUGIN);
                    //     ccu_Update_GUNA_LinkState(LINKSTATE_FULL);
                    // }

                    // if (dev->GUNBState == 1 && dev->rxbuff[11]==0 && (dev->ast_stage[1]==AST_IDLE || dev->ast_stage[1]==AST_PLUGIN || dev->ast_stage[1]==AST_FAULT || dev->ast_stage[1]==AST_PLUGIN_FAULT || dev->ast_stage[1]==AST_END))
                    // {   //PLUGIN
                    //     printd("GUNBState:%d\n ", dev->GUNBState);
                    //     ccu_Update_GUNB_WorkState(WORKSTATE_PLUGIN);
                    //     ccu_Update_GUNB_LinkState(LINKSTATE_FULL);
                    // }

                    // if (dev->GUNAState == 0 && dev->rxbuff[10]==1 && (dev->ast_stage[0]==AST_IDLE || dev->ast_stage[0]==AST_PLUGIN || dev->ast_stage[0]==AST_FAULT|| dev->ast_stage[0]==AST_PLUGIN_FAULT || dev->ast_stage[0]==AST_END))
                    // {   //PLUGOUT
                    //     printd("GUNAState:%d\n ", dev->GUNAState);
                    //     ccu_Update_GUNA_WorkState(WORKSTATE_IDLE);
                    //     ccu_Update_GUNA_LinkState(LINKSTATE_HALF);
                    // }

                    // if (dev->GUNBState == 0 && dev->rxbuff[11]==1 && (dev->ast_stage[1]==AST_IDLE || dev->ast_stage[1]==AST_PLUGIN || dev->ast_stage[1]==AST_FAULT|| dev->ast_stage[1]==AST_PLUGIN_FAULT || dev->ast_stage[1]==AST_END))
                    // {   //PLUGOUT
                    //     printd("GUNBState:%d\n ", dev->GUNBState);
                    //     ccu_Update_GUNB_WorkState(WORKSTATE_IDLE);
                    //     ccu_Update_GUNB_LinkState(LINKSTATE_HALF);
                    // }

                    dev->GUNAState = dev->rxbuff[10];
                    dev->GUNBState = dev->rxbuff[11];

                    if (dev->rxbuff[12]==0 && dev->GUNA_ELockState==1)
                    {
                        ccu_Update_GUNA_ElecLockState(1);
                    }
                    if (dev->rxbuff[12]==1 && dev->GUNA_ELockState==0)
                    {
                        ccu_Update_GUNA_ElecLockState(0);
                    }

                    if (dev->rxbuff[13]==0 && dev->GUNB_ELockState==1)
                    {
                        ccu_Update_GUNB_ElecLockState(1);
                    }
                    if (dev->rxbuff[13]==1 && dev->GUNB_ELockState==0)
                    {
                        ccu_Update_GUNB_ElecLockState(0);
                    }

                    dev->GUNA_ELockState = dev->rxbuff[12];
                    dev->GUNB_ELockState = dev->rxbuff[13];

                    dev->GUNA_AuxPowerState = dev->rxbuff[14];
                    dev->GUNB_AuxPowerState = dev->rxbuff[15];

                    // printd("GUNAState:%d, GUNBState:%d, GUNA_ELockState:%d, GUNB_ELockState:%d, GUNA_AuxPowerState:%d, GUNB_AuxPowerState:%d.\n", dev->GUNAState, dev->GUNBState, dev->GUNA_ELockState, dev->GUNB_ELockState, dev->GUNA_AuxPowerState, dev->GUNB_AuxPowerState);

                    if (dev->GUNA_ELockCtrl == 0 && dev->GUNA_ELockState == 0)
                    {
                        dev->GUNA_ELockCtrl = 2;
                        dev->need_send_ctrl_cmd = 1;
                    }
                    if (dev->GUNA_ELockCtrl == 1 && dev->GUNA_ELockState == 1)
                    {
                        dev->GUNA_ELockCtrl = 2;
                        dev->need_send_ctrl_cmd = 1;
                    }
                    

                    if (dev->GUNB_ELockCtrl == 0 && dev->GUNB_ELockState == 0)
                    {
                        dev->GUNB_ELockCtrl = 2;
                        dev->need_send_ctrl_cmd = 1;
                    }
                    if (dev->GUNB_ELockCtrl == 1 && dev->GUNB_ELockState == 1)
                    {
                        dev->GUNB_ELockCtrl = 2;
                        dev->need_send_ctrl_cmd = 1;
                    }
                    

                    // if (dev->GUNA_AuxPowerCtrl == 0 && dev->GUNA_AuxPowerState == 0)
                    // {
                    //     printd("GUNA_AuxPowerCtrl:%d, GUNA_AuxPowerState:%d, Next restore.\n", dev->GUNA_AuxPowerCtrl, dev->GUNA_AuxPowerState);
                    //     dev->GUNA_AuxPowerCtrl = 2;
                    //     dev->need_send_ctrl_cmd = 1;
                    // }
                    // if (dev->GUNA_AuxPowerCtrl == 1 && dev->GUNA_AuxPowerState == 1)
                    // {
                    //     printd("GUNA_AuxPowerCtrl:%d, GUNA_AuxPowerState:%d, Next restore.\n", dev->GUNA_AuxPowerCtrl, dev->GUNA_AuxPowerState);
                    //     dev->GUNA_AuxPowerCtrl = 2;
                    //     dev->need_send_ctrl_cmd = 1;
                    // }

                    // if (dev->GUNB_AuxPowerCtrl == 0 && dev->GUNB_AuxPowerState == 0)
                    // {
                    //     printd("GUNB_AuxPowerCtrl:%d, GUNB_AuxPowerState:%d, Next restore.\n", dev->GUNB_AuxPowerCtrl, dev->GUNB_AuxPowerState);
                    //     dev->GUNB_AuxPowerCtrl = 2;
                    //     dev->need_send_ctrl_cmd = 1;
                    // }
                    // if (dev->GUNB_AuxPowerCtrl == 1 && dev->GUNB_AuxPowerState == 1)
                    // {
                    //     printd("GUNB_AuxPowerCtrl:%d, GUNB_AuxPowerState:%d, Next restore.\n", dev->GUNB_AuxPowerCtrl, dev->GUNB_AuxPowerState);
                    //     dev->GUNB_AuxPowerCtrl = 2;
                    //     dev->need_send_ctrl_cmd = 1;
                    // }
                    
                    dev->rxbytes = 0;
                    return 0;
                }
                dev->rxbytes = 0;
                return -4;
            }
            dev->rxbytes = 0;
            return -3;
        }
        return -2;
    }
    return -1;
}


int ccu_AST_queryController(ast_xh_t* dev, uint8_t addr, uint8_t stat1, uint8_t stat2, uint16_t allocated_power)
{
    int i;
    dev->txbytes = 0;
    dev->txbuff[dev->txbytes++] = addr;
    dev->txbuff[dev->txbytes++] = ~addr;
    dev->txbuff[dev->txbytes++] = 4;
    dev->txbuff[dev->txbytes++] = 0;
    dev->txbuff[dev->txbytes++] = stat1;
    dev->txbuff[dev->txbytes++] = stat2;
    dev->txbuff[dev->txbytes++] = allocated_power&0xFF;
    // printd("addr=%02x, more_cmd:%d, allocated_power:%04x\n", addr, dev->more_cmd, allocated_power);
    uint16_t crct = modbus_crc16(dev->txbuff, 7);
    dev->txbuff[dev->txbytes++] = (crct>>0)&0xFF;
    dev->txbuff[dev->txbytes++] = (crct>>8)&0xFF;
    // printline("\nuart send", dev->txbuff, dev->txbytes);
    if (com_ttySx_send(&dev->tty, dev->txbuff, dev->txbytes) == dev->txbytes)
    {
        int minbytes = 8;
        dev->rxbytes = 0;
        dev->more_cmd = 0;

        ccu_AST_recv_with_timeout(dev, 3, 500, 3);
        // if (dev->rxbytes > 0) printline("uart recv", dev->rxbuff, dev->rxbytes);
        if (dev->rxbytes >= minbytes)
        {
            uint8_t rxbuff1 = ~dev->rxbuff[1];
            // printd("(%d:%s-->minbytes:%d), %02x, %02x \n",minbytes, dev->rxbuff[0], rxbuff1);
            if (dev->rxbuff[0] == rxbuff1)
            {
                minbytes = 3 + dev->rxbuff[2] + 2;
                // printd("(%d:%s-->minbytes:%d)\n",minbytes);
                if (dev->rxbytes >= minbytes)
                {
                    uint16_t crcr = (dev->rxbuff[minbytes-1]<<8)|dev->rxbuff[minbytes-2];
                    uint16_t crcx = modbus_crc16(dev->rxbuff, minbytes-2);
                    if (crcr != crcx)
                    {
                        dev->rxbytes = 0;
                        printe("crcr[%04x] != crcx[%04x]\n",crcr, crcx);
                        return 0;
                    }
                    uint8_t addr = dev->rxbuff[0];
                    uint8_t seq = dev->rxbuff[3];
                    uint8_t cmd = dev->rxbuff[4];
                    switch(cmd)
                    {
                        case 0: ccu_AST_cmd0_NoOperation(dev, addr, seq, cmd); break;
                        case 1: ccu_AST_cmd1_Allocate(dev, addr, seq, cmd); break;
                        case 2: ccu_AST_cmd2_Deallocate(dev, addr, seq, cmd); break;
                        case 3: ccu_AST_cmd3_SetVoltCurr(dev, addr, seq, cmd); break;
                        case 4: ccu_AST_cmd4_Startup(dev, addr, seq, cmd); break;
                        case 5: ccu_AST_cmd5_Poweroff(dev, addr, seq, cmd); break;
                        case 6: ccu_AST_cmd6_CloseOutContactor(dev, addr, seq, cmd); break;
                        case 7: ccu_AST_cmd7_DisconnectOutContactor(dev, addr, seq, cmd); break;
                        case 8: ccu_AST_cmd8_LockGun(dev, addr, seq, cmd); break;
                        case 9: ccu_AST_cmd9_UnlockGun(dev, addr, seq, cmd); break;
                        case 10: ccu_AST_cmd10_TurnOnAuxPower(dev, addr, seq, cmd); break;
                        case 11: ccu_AST_cmd11_TurnOffAuxPower(dev, addr, seq, cmd); break;
                        case 14: ccu_AST_responseA(dev, addr, seq, cmd, 1); break;
                        case 15: ccu_AST_cmd15_SetHiLowVoltMode(dev, addr, seq, cmd); break;

                        case 100: ccu_AST_cmd100_QueryOutContactor(dev, addr, seq, cmd); break;
                        case 101: ccu_AST_cmd101_QueryPowerAlloc(dev, addr, seq, cmd); break;
                        case 102: ccu_AST_cmd102_QueryGunLockState(dev, addr, seq, cmd); break;
                        case 103: ccu_AST_cmd103_QueryAuxPowerState(dev, addr, seq, cmd); break;
                        case 104: ccu_AST_cmd104_QueryOutVoltage(dev, addr, seq, cmd); break;
                        case 105: ccu_AST_cmd105_QueryHiLowVoltMode(dev, addr, seq, cmd); break;
                        default: 
                            printe("unsupport cmd[%02x]\n",cmd); 
                            printline("uart recv", dev->rxbuff, dev->rxbytes);
                            break;
                    }
                    dev->rxbytes = 0;
                    if (dev->txbytes > 0)
                    {
                        return com_ttySx_send(&dev->tty, dev->txbuff, dev->txbytes);
                        // dev->txbytes = 0;
                    }
                    if (dev->more_cmd == 0)
                    {
                        return 0;
                    }
                }
                dev->rxbytes = 0;
                return -4;
            }
            dev->rxbytes = 0;
            return -3;
        }
        dev->rxbytes = 0;
        return -2;
    }
    return -1;
}

int ccu_AST_GUNA_ELock(ast_xh_t* dev, int lock)
{
    if (lock) dev->GUNA_ELockCtrl = 0; else dev->GUNA_ELockCtrl = 1;
    dev->need_send_ctrl_cmd = 1;
}

int ccu_AST_GUNB_ELock(ast_xh_t* dev, int lock)
{
    if (lock) dev->GUNB_ELockCtrl = 0; else dev->GUNB_ELockCtrl = 1;
    dev->need_send_ctrl_cmd = 1;
}

int ccu_AST_GUNA_AuxPower(ast_xh_t* dev, int close)
{
    if (close) dev->GUNA_AuxPowerCtrl = 0; else dev->GUNA_AuxPowerCtrl = 1;
    dev->need_send_ctrl_cmd = 1;
}

int ccu_AST_GUNB_AuxPower(ast_xh_t* dev, int close)
{
    if (close) dev->GUNB_AuxPowerCtrl = 0; else dev->GUNB_AuxPowerCtrl = 1;
    dev->need_send_ctrl_cmd = 1;
}

void* ccu_AST_loop(void* argv)
{
    ast_xh_t* dev = (ast_xh_t*)argv;
    if (dev)
    {
        while(dev->quit == 0)
        {
            // printd("AP:%d KW, BP:%d KW\n", dev->power_allocated[0], dev->power_allocated[1]);
            ccu_AST_queryController(dev, (dev->addr&0xF0)+1, dev->stat1[0], dev->stat2[0], dev->power_allocated[0]/5);
            
            ccu_AST_queryController(dev, (dev->addr&0xF0)+2, dev->stat1[1], dev->stat2[1], dev->power_allocated[1]/5);
            
            ccu_AST_queryTemperature(dev,(dev->addr&0xF0)+3, 0, 0x00, 0x00, 0x00, 0x00);
            
            if (dev->need_send_ctrl_cmd == 1)
            {
                dev->need_send_ctrl_cmd = 0;
                ccu_AST_queryTemperature(dev,(dev->addr&0xF0)+3, 1, dev->GUNA_ELockCtrl, dev->GUNB_ELockCtrl, dev->GUNA_AuxPowerCtrl, dev->GUNB_AuxPowerCtrl);
            }
        }
    }
    return NULL;
}

void ccu_AST_exit(ast_xh_t* dev)
{
    if(dev)
    {
        pthread_mutex_destroy(&dev->mutex);
        com_ttySx_rs485_enable(&dev->tty, 0);
        com_ttySx_exit(&dev->tty);
    }
}