#include "Module_PsuComm.h" #include "Config.h" #include "Common.h" #define DERATING_COUNT 30 #define DERATING_GAP 30 #define ELEMENT_NOT_FIND 255 #define CHK_VOL_RANGE 50 #define CHK_CUR_RANGE 10 #define DERATING_RANGE 100 #define ZERO_CURRENT 10 // 該值須保持最小為 1A #define ZERO_VOLTAGE 50 #define STOP_CURRENT 30 #define PSU_MAX_CUR 1000 #define PSU_MIN_VOL 1500 #define PRE_CHARG_STEP_CUR 30 #define PRE_CHARG_RANGE 50 #define CMD_DELAY_TIME 25000 // 25us #define PSU_TASK_CHECK_TIME 1 #define GET_PSU_COUNT_INTERVAL 1 // unit: second #define GET_PSU_COUNT_TIME 13 // unit: second #define GET_PSU_LOCATION_INTERVAL 200 // unit: millisecond #define PSU_COUNT_CONFIRM_INTERVAL 200 // unit: millisecond #define PSU_COUNT_CONFIRM_TIME 1 // unit: second #define GET_PSU_VERSION_INTERVAL 200 // unit: millisecond #define GET_PSU_CAP_INTERVAL 200 // unit: millisecond #define GET_PSU_CAP_TIME 1 // unit: second #define STABLE_CURRENT_TOLERANCE 10 // unit: 0.1A, 1A #define MAX_STABLE_COUNT 24 #define WAIT_EV_DERATING_TIMEOUT 5 // unit: second #define WAIT_SLAVE_POWER_OFF_TIMEOUT 5 // unit: second #define WAIT_PARALLEL_RELAY_DELAY 1 // unit: second #define WAIT_RELAY_CONFIRMED_TIME 3 // unit: second #define MAX_PREPARE_SWITCH_OFF_TIME 10 // unit: second #define MIN_POWER_OFF_TIME 1 // unit: second #define WAIT_SLAVE_TO_CHARGING 1 // unit: second #define WAIT_SLAVE_TIMEOUT 5 // unit: second #define WAIT_GRAB_TIME 15 // unit: second #define MAX_PSU_POWER_OFF_CURRENT 50 // unit: 0.1A, 5A #define MAX_PSU_POWER_OFF_VOLTAGE 100 // unit: 0.1A, 10V #define PRECHARGE_OFFSET_VOLTAGE 20 // unit: 0.1V, 2V #define PRECHARGE_RANGE_VOLTAGE 50 // unit: 0.1V, 5V #define WAIT_PRECHARGE_TIME 10 // unit: second #define EXTEND_CAPABILITY_DELAY 60 // unit: second #define EXTEND_LOADING 8000 // unit: 0.01%, 80% #define RELEASE_LOADING 5000 // unit: 0.01%, 50% #define RELEASE_LOADING_OFFSET 1000 // unit: 0.01%, 10% #define BALANCE_CURRENT_INTERVAL 100 // unit: 1ms, 100ms #define CURRENT_REACH_TARGET_TIME 5 // unit: 1s, 5s #define CURRENT_STABLE_TIME 10 // unit: 1s, 10s #define REACH_CURRENT_TOLERANCE 10 // unit: 0.1A, 1A #define MAX_ADJ_BALANCE_CURRENT 30 // unit: 0.1A, 3A #define START_BALANCE_CRITERIA 100 // unit: 0.01%, 1% #define STOP_BALANCE_CRITERIA 10 // unit: 0.01%, 0.1% #define EV_MAX_CURRENT_DELAY 120 // unit: second #define WAIT_SLAVE_READY_TIME 15 // unit: second #define WAIT_SLAVE_DELAY 1 // unit: second #define SHOW_OUTPUT_DELAY 1 #define CURRENT_BALANCE_CRITERIA 400 // unit: 0.1A, 40A #define SAFETY_PRECHARGE_OFFSET 200 // unit: 0.1V, 20V #define CHARGING_DELAY_INTERVAL 200 // unit: 1ms, 400ms #define MAX_CHARGING_DELAY_COUNT 15 #define VOLTAGE_RESUME_STEP 10 // unit: 0.1V, 1V #define POWER_ONOFF_RESEND_INTERVAL 1 // unit: 1s, 1s #if SAFETY_TEST_ENABLE #define PRECHARGE_OFFSET 1 // 0: normal output, 1: precharge voltage offset enable #define ONE_MODULE_OUTPUT 1 // 0: normal output, 1: only one module output enable #define MASTER_OUTPUT_FIRST 0 // 0: normal output, 1: master output first enable #else #define PRECHARGE_OFFSET 0 // 0: normal output, 1: precharge voltage offset enable #define ONE_MODULE_OUTPUT 0 // 0: normal output, 1: only one module output enable #define MASTER_OUTPUT_FIRST 0 // 0: normal output, 1: master output first enable #endif struct SysConfigAndInfo *ShmSysConfigAndInfo; struct StatusCodeData *ShmStatusCodeData; struct PsuData *ShmPsuData; ChargerInfoData *ShmChargerInfo; PsuPositionInfoData *ShmPsuPosition; PsuGroupingInfoData *ShmPsuGrouping; bool libInitialize = false; byte _gunCount = 0; byte _maxGroupCount = 0; byte getAvailableCapOffset = 5; byte deratingKeepCount = 0; byte psuCmdSeq = _PSU_CMD_CAP; byte startModuleFlag = false; bool psuReceiveRecovery = false; unsigned short evseOutVol[CONNECTOR_QUANTITY] = {0, 0, 0, 0}; unsigned short evseOutCur[CONNECTOR_QUANTITY] = {0, 0, 0, 0}; unsigned short evseOutputDelay[CONNECTOR_QUANTITY] = {0, 0, 0, 0}; struct timespec _PsuReceiveRecoveryCheck_time; struct timespec _PsuWorkStep_time; struct timespec _cmdSubPriority_time; struct timespec _PsuGroupRole_time[CONNECTOR_QUANTITY]; struct timespec _ChargingRequest_time[CONNECTOR_QUANTITY]; struct timespec _PsuGroupDerating_time[CONNECTOR_QUANTITY]; struct timespec _StopCharging_time[CONNECTOR_QUANTITY]; struct timespec _ExtendCapability_time[CONNECTOR_QUANTITY]; struct timespec _ReachCurrent_time[CONNECTOR_QUANTITY]; struct timespec _BalanceCurrent_time[CONNECTOR_QUANTITY]; struct timespec _MaxCurrent_time[CONNECTOR_QUANTITY]; struct timespec _StageCurrent_time[CONNECTOR_QUANTITY]; struct timespec _CheckSlaveReady_time[CONNECTOR_QUANTITY]; struct timespec _ChargingDelay_time[CONNECTOR_QUANTITY]; struct timespec _PoweOnOff_time[CONNECTOR_QUANTITY]; unsigned short GCTargetVoltage[CONNECTOR_QUANTITY]; unsigned short GCTargetCurrent[CONNECTOR_QUANTITY]; unsigned short StableOutputCurrent[CONNECTOR_QUANTITY]; unsigned short MaxCurrentDemand[CONNECTOR_QUANTITY]; unsigned short StageMaxCurrent[CONNECTOR_QUANTITY]; GroupOutputConfigInfo PreGroupOutput[MAX_GROUP_QUANTITY]; unsigned char OutputConfigStep[MAX_GROUP_QUANTITY]; unsigned char _preOutputConfigStep[MAX_GROUP_QUANTITY]; unsigned char _GfdStep[MAX_GROUP_QUANTITY]; unsigned char _VoltageResumeCnt[MAX_GROUP_QUANTITY]; //================================= // Common routine //================================= // return psu group number // return -1 when out of TotalPsuQuantity int FindTargetGroup(byte address) { return address < ShmPsuPosition->TotalPsuQuantity ? ShmPsuPosition->PsuAddressInfo[address].GroupNo : -1; } // return psu index in the group // return -1 when out of TotalPsuQuantity int FindGroupIndex(byte address) { return address < ShmPsuPosition->TotalPsuQuantity ? ShmPsuPosition->PsuAddressInfo[address].GIndex : -1; } //================================= // Save data to share memory Function //================================= bool FindChargingInfoData(byte target, struct ChargingInfoData **chargingData) { if(GENERAL_GUN_QUANTITY > 0 && target < GENERAL_GUN_QUANTITY) { chargingData[target] = &ShmSysConfigAndInfo->SysInfo.ConnectorInfo[target].GeneralChargingData; return true; } return false; } //================================= // Alarm code mapping to share memory Function //================================= // 檢查 Byte 中某個 Bit 的值 // _byte : 欲改變的 byte // _bit : 該 byte 的第幾個 bit unsigned char mask_table[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; unsigned char DetectBitValue(unsigned char _byte, unsigned char _bit) { return ( _byte & mask_table[_bit] ) != 0x00; } void AbnormalStopAnalysis(byte gun_index, int errCode) { for (char i = 0; i < 4; i++) { unsigned char byteIndex = (errCode >> (8 * i)) & 0xff; for (char bitIndex = 0; bitIndex < 8; bitIndex++) { if(DetectBitValue(byteIndex , bitIndex) == 1) { switch(i) { case 0: { // err 1 if (bitIndex == 2) { // 012307 // fuse burn-out ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFuseBurnOut = YES; } else if (bitIndex == 3) { // 012308 // Communication fault between PFC and DCDC ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuPfcAndDcdcCommFault = YES; } else if (bitIndex == 6) { // 012309 // Unbalance positive and negative BUS voltage ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuBusVoltageUnbalance = YES; } else if (bitIndex == 7) { // 012310 // BUS over voltage ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuBusOverVoltage = YES; } } break; case 1: { // err 2 if (bitIndex == 0) { // 012311 // BUS voltage abnormal ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuBusVoltageAbnormal = YES; } else if (bitIndex == 1) { // 012270 // Over voltage of any phase ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuInputOVP = YES; } else if (bitIndex == 2) { // 012263 // ID number repetition ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDuplicateID = YES; } else if (bitIndex == 3) { // 012312 // BUS under voltage ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuBusUnderVoltage = YES; } else if (bitIndex == 4) { // 012313 // Phase loss ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuInputPhaseLoss = YES; } else if (bitIndex == 6) { // 012271 // Under voltage of any phase ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuInputUVP = NO; } } break; case 2: { // err3 if (bitIndex == 0) { // 012240 // CAN communication fault ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuCommunicationFail = YES; } else if (bitIndex == 1) { // 012275 // DCDC uneven current sharing ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuSevereUnevenCurrent = YES; } else if (bitIndex == 3) { // 012278 // PFC power off //ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFfcSideShutDown = YES; } else if (bitIndex == 5) { // 012314 // Full speed of fan ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFanFullSpeed = YES; } else if (bitIndex == 6) { // 012266 // DCDC power off //ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDcSideShutDown = YES; } else if (bitIndex == 7) { // 012273 // Module unders power limiting status ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuPowerLimitedState = YES; } } break; case 3: { // err 4 if (bitIndex == 0) { // 012315 // Temperature power limiting ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuTemperaturePowerLimit = YES; } else if (bitIndex == 1) { // 012316 // AC power limiting ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuAcPowerLimit = YES; } else if (bitIndex == 2) { // 012317 // DCDC eeprom faults ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDcdcEepromFault = YES; } else if (bitIndex == 3) { // 012269 // Fan faults ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFanFailureAlarm = YES; } else if (bitIndex == 4) { // 012264 // DCDC short circuit ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuOutputShortCircuit = YES; } else if (bitIndex == 5) { // 012318 // PFC eeprom faults ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuPfcEepromFault = YES; } else if (bitIndex == 6) { // 012226 // DCDC over temperature ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuCriticalPointOTP = YES; } else if (bitIndex == 7) { // 012319 // DCDC output over voltage ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDcdcOverVoltage = YES; } } break; } } // else // { // switch (byteIndex) { // case 0: { // if (bitIndex == 0) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuOutputShortCircuit = NO; // else if (bitIndex == 5) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDcSideShutDown = NO; // } // break; // case 1: { // if (bitIndex == 1) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFailureAlarm = NO; // else if (bitIndex == 2) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuProtectionAlarm = NO; // else if (bitIndex == 3) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFanFailureAlarm = NO; // else if (bitIndex == 4) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuCriticalPointOTP = NO; // else if (bitIndex == 5) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDcSideShutDown = NO; // } // break; // case 2: { // if (bitIndex == 1) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDuplicateID = NO; // if (bitIndex == 2) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuThreePhaseOnputImbalance = NO; // else if (bitIndex == 3) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuThreePhaseInputInadequate = NO; // else if (bitIndex == 4) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuThreePhaseInputInadequate = NO; // else if (bitIndex == 5) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuInputUVP = NO; // else if (bitIndex == 6) // ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuInputOVP = NO; // } // break; // } // } } } } //================================= // Callback Function //================================= // 0x04: PSU_RCmd_ModuleStatus void GetStatusCallback(byte group, byte SN, byte temp, int alarm) { if(ShmPsuData->Work_Step != Get_PSU_LOCATION) { return; } if(group >= _maxGroupCount || SN >= MAX_MODULE_PER_GROUP) { return; } ShmPsuPosition->PsuAddressInfo[SN].Address = SN; ShmPsuPosition->PsuAddressInfo[SN].GroupNo = group; if(!ShmPsuPosition->PsuAddressInfo[SN].CheckIn) { ShmPsuPosition->PsuAddressInfo[SN].CheckIn = true; ShmPsuPosition->TotalPsuQuantity++; //LOG_INFO("SN = %d At Group %02X", SN, group); } if(ShmPsuPosition->TotalPsuQuantity == ShmPsuData->SystemPresentPsuQuantity) { byte group = 0, quantity = 0; if(!ShmPsuPosition->PsuLocationInit) { memset(ShmPsuPosition->GroupLocationInfo, 0x00, sizeof(ShmPsuPosition->GroupLocationInfo)); for(int index = 0; index < MAX_PSU_MODULE_QUANTITY; index++) { if(ShmPsuPosition->PsuAddressInfo[index].CheckIn) { group = ShmPsuPosition->PsuAddressInfo[index].GroupNo; quantity = ShmPsuPosition->GroupLocationInfo[group].GroupPsuQuantity; ShmPsuPosition->PsuAddressInfo[index].GIndex = quantity; ShmPsuPosition->GroupLocationInfo[group].PsuSN[quantity] = ShmPsuPosition->PsuAddressInfo[index].Address; ShmPsuPosition->GroupLocationInfo[group].GroupPsuQuantity++; //LOG_INFO("Psu %d Assign To Group %02X At %d", index, group, ShmPsuPosition->PsuAddressInfo[index].GIndex); } } bool match = true; for(int index = 0; index < _maxGroupCount; index++) { if(ShmPsuPosition->GroupLocationInfo[index].GroupPsuQuantity != ShmPsuData->PsuGroup[index].GroupPresentPsuQuantity) { match = false; } } if(match) { ShmPsuPosition->PsuLocationInit = true; } else { // try to re-initial psu location ShmPsuPosition->ReInitPsuLocation = true; } } else { ShmPsuData->PsuGroup[group].PsuModule[ShmPsuPosition->PsuAddressInfo[SN].GIndex].CriticalTemp1 = temp; } } } // 0x02: PSU_RCmd_SysModuleCount void GetModuleCountCallback(byte group, byte count) { if(group == SYSTEM_CMD) { ShmPsuData->SystemPresentPsuQuantity = count; } else { if(group < _maxGroupCount) { ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity = count; ShmPsuGrouping->GroupCollection[group].GunPsuQuantity = count; } } } void UpdateGunAvailableCapability(unsigned char group) { int _availableCurrent = 0, _availablePower = 0, _realPower = 0; unsigned char master = 0; master = ShmChargerInfo->PsuGrouping.GroupCollection[group].TargetGroup; // calculate output group capability if(master == 0) { chargingInfo[group]->AvailableChargingCurrent = ShmPsuData->PsuGroup[group].GroupAvailableCurrent; chargingInfo[group]->AvailableChargingPower = ShmPsuData->PsuGroup[group].GroupAvailablePower; chargingInfo[group]->RealRatingPower = ShmPsuData->PsuGroup[group].GroupRealOutputPower * 10; } else { if(ShmPsuGrouping->GroupCollection[master - 1].Role == _GROLE_MASTER) { _availableCurrent = ShmPsuData->PsuGroup[master - 1].GroupAvailableCurrent; _availablePower = ShmPsuData->PsuGroup[master - 1].GroupAvailablePower; _realPower = ShmPsuData->PsuGroup[master - 1].GroupRealOutputPower * 10; for(byte i = 0; i < ShmPsuGrouping->GroupCollection[master - 1].Partner.Quantity; i++) { byte slave = ShmPsuGrouping->GroupCollection[master - 1].Partner.Member[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE || ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_PREPARE_SWITCH_OFF) { _availableCurrent += ShmPsuData->PsuGroup[slave].GroupAvailableCurrent; _availablePower += ShmPsuData->PsuGroup[slave].GroupAvailablePower; _realPower += ShmPsuData->PsuGroup[slave].GroupRealOutputPower * 10; } } if(ShmPsuGrouping->GroupCollection[master - 1].GroupCtrl.bits.DeratingConfirmed) { chargingInfo[master - 1]->AvailableChargingCurrent = ShmPsuGrouping->GroupCollection[master - 1].ReAssignAvailableCurrent; } else { chargingInfo[master - 1]->AvailableChargingCurrent = _availableCurrent; } chargingInfo[master - 1]->AvailableChargingPower = _availablePower; chargingInfo[master - 1]->RealRatingPower = _realPower; // if((master - 1) != group) // { // chargingInfo[group]->AvailableChargingCurrent = 0;; // chargingInfo[group]->AvailableChargingPower = 0; // chargingInfo[group]->RealRatingPower = 0; // } } } } void UpdateSystemAvailable(void) { int _sysCurrent = 0, _sysPower = 0; // summation of system current & power for(byte i = 0; i < _maxGroupCount; i++) { _sysCurrent += ShmPsuData->PsuGroup[i].GroupAvailableCurrent; _sysPower += ShmPsuData->PsuGroup[i].GroupAvailablePower; } ShmPsuData->SystemAvailableCurrent = _sysCurrent; ShmPsuData->SystemAvailablePower = _sysPower; } // maxVol, minVol unit: 0.1V // maxCur unit: 0.1A // totalPow unit: 0.01kW // 0x0A: PSU_RCmd_ModuleCapability void GetAvailableCapCallback(byte address, short maxVol, short minVol, short maxCur, short totalPow) { int _groupCurrent = 0, _groupPower = 0; if (ShmPsuData->Work_Step < GET_SYS_CAP || address >= ShmPsuData->SystemPresentPsuQuantity || !ShmPsuPosition->PsuLocationInit) { return; } byte master = 0; int group = FindTargetGroup(address); int gIndex = FindGroupIndex(address); if(group < 0 || gIndex < 0) { return; } // calculate psu module available power ShmPsuData->PsuGroup[group].PsuModule[gIndex].AvailablePower = totalPow; //if(ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage >= PSU_MIN_VOL) if(ShmPsuGrouping->GroupOutput[group].GTargetVoltage >= PSU_MIN_VOL) { ShmPsuData->PsuGroup[group].PsuModule[gIndex].AvailableCurrent = ((ShmPsuData->PsuGroup[group].PsuModule[gIndex].AvailablePower * 100) * 10) / (ShmPsuGrouping->GroupOutput[group].GTargetVoltage / 10); } else { ShmPsuData->PsuGroup[group].PsuModule[gIndex].AvailableCurrent = maxCur > PSU_MAX_CUR ? PSU_MAX_CUR : maxCur; } // summation of psu group current & power for(byte i = 0; i < ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity; i++) { _groupCurrent += ShmPsuData->PsuGroup[group].PsuModule[i].AvailableCurrent; _groupPower += ShmPsuData->PsuGroup[group].PsuModule[i].AvailablePower; } if(ShmPsuData->PsuGroup[group].StableIAvailableCurrent != 0) { ShmPsuData->PsuGroup[group].GroupAvailableCurrent = ShmPsuData->PsuGroup[group].StableIAvailableCurrent; } else { if(ShmPsuData->PsuGroup[group].GroupRealOutputPower != 0 && ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage >= PSU_MIN_VOL) { ShmPsuData->PsuGroup[group].GroupAvailableCurrent = ((ShmPsuData->PsuGroup[group].GroupRealOutputPower * 1000) * 10) / (ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage / 10); } else { ShmPsuData->PsuGroup[group].GroupAvailableCurrent = _groupCurrent; } } ShmPsuData->PsuGroup[group].GroupAvailablePower = _groupPower; // update MaximumChargingVoltage master = ShmChargerInfo->PsuGrouping.GroupCollection[group].TargetGroup; if(master == 0) { chargingInfo[group]->MaximumChargingVoltage = maxVol; } else { if(ShmPsuGrouping->GroupCollection[master - 1].Role == _GROLE_MASTER) { chargingInfo[master - 1]->MaximumChargingVoltage = maxVol; // if((master - 1) != group) // { // chargingInfo[group]->MaximumChargingVoltage = 0; // } } } UpdateGunAvailableCapability(group); UpdateSystemAvailable(); //LOG_INFO("address = %d, maxVol = %d, minVol = %d, maxCur = %d, totalPow = %d", address, maxVol, minVol, maxCur, totalPow); } // 0x07: PSU_RCmd_ModuleVersion void GetFwCallback(byte address, short dcSwVer, short pfcSwVer, short hwVer) { if (ShmPsuData->Work_Step != Get_PSU_VERSION || address >= ShmPsuData->SystemPresentPsuQuantity || !ShmPsuPosition->PsuLocationInit) { return; } int group = FindTargetGroup(address); int gIndex = FindGroupIndex(address); if(group < 0 || gIndex < 0) { return; } sprintf((char *)ShmPsuData->PsuVersion[address].FwPrimaryVersion, "DC %d.%02d", (dcSwVer & 0xFF00) >> 8, dcSwVer & 0xFF); sprintf((char *)ShmPsuData->PsuVersion[address].FwSecondVersion, "PFC %d.%02d", (pfcSwVer & 0xFF00) >> 8, pfcSwVer & 0xFF); sprintf((char *)ShmPsuData->PsuGroup[group].PsuModule[gIndex].FwVersion, "DC %d.%02d", (dcSwVer & 0xFF00) >> 8, dcSwVer & 0xFF); //LOG_INFO("Psu %d %s %s", address, ShmPsuData->PsuVersion[address].FwPrimaryVersion, ShmPsuData->PsuVersion[address].FwSecondVersion); } // no using -- GetInputVoltageCallback void GetInputVoltageCallback(byte address, unsigned short vol1, unsigned short vol2, unsigned short vol3) { } // no using -- GetInputVoltageCallback End // no using -- GetPresentOutputCallback void GetPresentOutputCallback(byte group, unsigned short outVol, unsigned short outCur) { } // no using -- GetPresentOutputCallback End // type 1: FAN_SPEED_CMD // type 2: TEMP_DC_CMD // type 3: TEMP_PFC_CMD // 0x0E: PSU_RCmd_ModuleMiscInfo void GetMisCallback(byte address, unsigned int value, byte type) { if (ShmPsuData->Work_Step < GET_SYS_CAP || address >= ShmPsuData->SystemPresentPsuQuantity || !ShmPsuPosition->PsuLocationInit) { return; } int group = FindTargetGroup(address); int gIndex = FindGroupIndex(address); if(group < 0 || gIndex < 0) { return; } if (type == 1) { ShmPsuData->PsuGroup[group].PsuModule[gIndex].FanSpeed_1 = value; ShmPsuData->PsuGroup[group].PsuModule[gIndex].FanSpeed_2 = value; ShmPsuData->PsuGroup[group].PsuModule[gIndex].FanSpeed_3 = value; ShmPsuData->PsuGroup[group].PsuModule[gIndex].FanSpeed_4 = value; } else if (type == 2) { //ShmPsuData->PsuGroup[group].PsuModule[gIndex].CriticalTemp1 = value; //ShmPsuData->PsuGroup[group].PsuModule[gIndex].CriticalTemp2 = value; //ShmPsuData->PsuGroup[group].PsuModule[gIndex].CriticalTemp3 = value; ShmPsuData->PsuGroup[group].PsuModule[gIndex].ExletTemp = value; } else if (type == 3) { //printf("PFC - group = %d, index = %d, value = %d \n", group, address, value); ShmPsuData->PsuGroup[group].PsuModule[gIndex].InletTemp = value; } } // Iavail unit: 0.1A // Vext unit: 0.1V // 0x0C: PSU_RCmd_ModuleIAvailable // 0x02CCF0XX void GetIavailableCallback(byte address, unsigned short Iavail, unsigned short Vext) { if (ShmPsuData->Work_Step < GET_SYS_CAP || address >= ShmPsuData->SystemPresentPsuQuantity || !ShmPsuPosition->PsuLocationInit) { return; } byte master = 0; unsigned short diffIAvailable = 0; int totalCurrent = 0; int group = FindTargetGroup(address); int gIndex = FindGroupIndex(address); if(group < 0 || gIndex < 0) { return; } ShmPsuData->PsuGroup[group].PsuModule[gIndex].IAvailableCurrent = Iavail; // summation of psu group i available current for (byte i = 0; i < ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity; i++) { totalCurrent += ShmPsuData->PsuGroup[group].PsuModule[i].IAvailableCurrent; } ShmPsuData->PsuGroup[group].TotalIAvailableCurrent = totalCurrent; diffIAvailable = totalCurrent >= ShmPsuData->PsuGroup[group].TempIAvailableCurrent ? totalCurrent - ShmPsuData->PsuGroup[group].TempIAvailableCurrent : ShmPsuData->PsuGroup[group].TempIAvailableCurrent - totalCurrent; if(diffIAvailable > STABLE_CURRENT_TOLERANCE) { ShmPsuData->PsuGroup[group].StableCurrentCounter = 0; ShmPsuData->PsuGroup[group].StableIAvailableCurrent = 0; } ShmPsuData->PsuGroup[group].TempIAvailableCurrent = ShmPsuData->PsuGroup[group].TotalIAvailableCurrent; ShmPsuData->PsuGroup[group].StableCurrentCounter++; if(ShmPsuData->PsuGroup[group].StableCurrentCounter >= MAX_STABLE_COUNT) { // get stable StableIAvailableCurrent ShmPsuData->PsuGroup[group].StableIAvailableCurrent = ShmPsuData->PsuGroup[group].TotalIAvailableCurrent; // get stable GroupRealOutputPower ShmPsuData->PsuGroup[group].GroupRealOutputPower = (ShmPsuData->PsuGroup[group].StableIAvailableCurrent * ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage) / 100 / 1000; ShmPsuData->PsuGroup[group].StableCurrentCounter = 0; } master = ShmChargerInfo->PsuGrouping.GroupCollection[group].TargetGroup; if(master == 0) { // unit: 0.1A chargingInfo[group]->DeratingChargingCurrent = ShmPsuData->PsuGroup[group].StableIAvailableCurrent; chargingInfo[group]->DeratingChargingPower = (chargingInfo[group]->DeratingChargingCurrent * chargingInfo[group]->PresentChargingVoltage) / 10 / 100; } else { // calculate DeratingChargingCurrent of master group totalCurrent = ShmPsuData->PsuGroup[master - 1].StableIAvailableCurrent; for(byte i = 0; i < ShmPsuGrouping->GroupCollection[master - 1].Partner.Quantity; i++) { byte slave = ShmPsuGrouping->GroupCollection[master - 1].Partner.Member[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE) { totalCurrent += ShmPsuData->PsuGroup[slave].StableIAvailableCurrent; } } chargingInfo[master - 1]->DeratingChargingCurrent = totalCurrent; chargingInfo[master - 1]->DeratingChargingPower = (chargingInfo[master - 1]->DeratingChargingCurrent * chargingInfo[master - 1]->PresentChargingVoltage) / 10 / 100; if((master - 1) != group) { chargingInfo[group]->DeratingChargingCurrent = 0; chargingInfo[group]->DeratingChargingPower = 0; } } } // outVol unit: 1V // outCur unit: 1A // 0x01: PSU_RCmd_SysOutputVolCur_F void GetPresentOutputFCallback(byte group, float outVol, float outCur) { if (ShmPsuData->Work_Step < GET_SYS_CAP || group >= _maxGroupCount || !ShmPsuPosition->PsuLocationInit) { return; } byte master = 0; unsigned short pVoltage = 0, pCurrent = 0; unsigned short gVoltage = 0, gCurrent = 0; pVoltage = isinf(outVol) == 0 ? (unsigned short)(outVol * 10) : 0; pCurrent = isinf(outCur) == 0 ? (unsigned short)(outCur * 10) : 0; ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage = pVoltage; ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent = pCurrent; ShmPsuData->PsuGroup[group].GroupPresentOutputPower = (pVoltage * pCurrent) / 100 / 100; master = ShmChargerInfo->PsuGrouping.GroupCollection[group].TargetGroup; if(master == 0) { // calculate PresentChargingVoltage and PresentChargingCurrent of self group chargingInfo[group]->PresentChargingVoltage = (float)ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage / 10; chargingInfo[group]->PresentChargingCurrent = (float)ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent / 10; } else { // calculate PresentChargingVoltage and PresentChargingCurrent of master group gVoltage = ShmPsuData->PsuGroup[master - 1].GroupPresentOutputVoltage; gCurrent = ShmPsuData->PsuGroup[master - 1].GroupPresentOutputCurrent; for(byte i = 0; i < ShmPsuGrouping->GroupCollection[master - 1].Partner.Quantity; i++) { byte slave = ShmPsuGrouping->GroupCollection[master - 1].Partner.Member[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE || ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_PREPARE_SWITCH_OFF || ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE_POWER_OFF) { if(ShmPsuData->PsuGroup[slave].GroupPresentOutputVoltage > gVoltage) { gVoltage = ShmPsuData->PsuGroup[slave].GroupPresentOutputVoltage; } gCurrent += ShmPsuData->PsuGroup[slave].GroupPresentOutputCurrent; } } chargingInfo[master - 1]->PresentChargingVoltage = (float)gVoltage / 10; chargingInfo[master - 1]->PresentChargingCurrent = (float)gCurrent / 10; if((master - 1) != group) { chargingInfo[group]->PresentChargingVoltage = 0; chargingInfo[group]->PresentChargingCurrent = 0; } } } //========================================== // 特規用指令 //========================================== // 0x1901: Nexton_PSU_DcOutputValue void GetOutputAndTempCallback(byte address, unsigned short outputVol_s, unsigned short outputCur_s, unsigned short outputPower, unsigned char Temperature) { if (ShmPsuData->Work_Step < GET_SYS_CAP || address >= ShmPsuData->SystemPresentPsuQuantity || !ShmPsuPosition->PsuLocationInit) { return; } int group = FindTargetGroup(address); int gIndex = FindGroupIndex(address); if(group < 0 || gIndex < 0) { return; } //ShmPsuData->PsuGroup[group].PsuModule[gIndex].CriticalTemp1 = Temperature; ShmPsuData->PsuGroup[group].PsuModule[gIndex].CriticalTemp2 = Temperature; ShmPsuData->PsuGroup[group].PsuModule[gIndex].CriticalTemp3 = Temperature; } // 0x1902: Nexton_PSU_StatusEvent void GetModuleStatusCallback(byte address, unsigned char isErr, unsigned char status, unsigned char err1, unsigned char err2, unsigned char err3, unsigned char err4) { if (ShmPsuData->Work_Step < GET_SYS_CAP || address >= ShmPsuData->SystemPresentPsuQuantity || !ShmPsuPosition->PsuLocationInit) { return; } int group = FindTargetGroup(address); int gIndex = FindGroupIndex(address); if(group < 0 || gIndex < 0) { return; } int alarm = (err4 << 24) | (err3 << 16) | (err2 << 8) | err1; ShmPsuData->PsuGroup[group].PsuModule[gIndex].AlarmCode = alarm; AbnormalStopAnalysis(group, alarm); if(isErr) { // 012267 //ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFailureAlarm = YES; ShmPsuData->PsuGroup[group].GroupErrorFlag.bits.PsuFailure = true; } } // 0x1903: Nexton_PSU_AcInputValue void GetModuleInputCallback(byte address, unsigned short inputR, unsigned short inputS, unsigned short inputT) { if (ShmPsuData->Work_Step < GET_SYS_CAP || address >= ShmPsuData->SystemPresentPsuQuantity || !ShmPsuPosition->PsuLocationInit) { return; } int group = FindTargetGroup(address); int gIndex = FindGroupIndex(address); if(group < 0 || gIndex < 0) { return; } ShmPsuData->PsuGroup[group].PsuModule[gIndex].InputVoltageL1 = inputR; ShmPsuData->PsuGroup[group].PsuModule[gIndex].InputVoltageL2 = inputS; ShmPsuData->PsuGroup[group].PsuModule[gIndex].InputVoltageL3 = inputT; } //========================================== // Init all share memory //========================================== int InitShareMemory() { int result = PASS; int MeterSMId; //creat ShmSysConfigAndInfo if ((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo), 0777)) < 0) { #ifdef SystemLogMessage LOG_ERROR("shmget ShmSysConfigAndInfo NG %d"); #endif result = FAIL; } else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1) { #ifdef SystemLogMessage LOG_ERROR("shmat ShmSysConfigAndInfo NG"); #endif result = FAIL; } else {} //creat ShmStatusCodeData if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData), 0777)) < 0) { #ifdef SystemLogMessage LOG_ERROR("shmget ShmStatusCodeData NG"); #endif result = FAIL; } else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) -1) { #ifdef SystemLogMessage LOG_ERROR("shmat ShmStatusCodeData NG"); #endif result = FAIL; } else {} //creat ShmPsuData 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 ((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(result == PASS) { ShmPsuPosition = &ShmChargerInfo->PsuPosition; ShmPsuGrouping = &ShmChargerInfo->PsuGrouping; } return result; } //================================================ // Main process //================================================ void InitialPsuData() { ShmPsuData->SystemPresentPsuQuantity = 0; ShmPsuData->SystemAvailablePower = 0; LOG_INFO("************ psu Group = %d", ShmPsuData->GroupCount); for (byte _groupCount = 0; _groupCount < ShmPsuData->GroupCount; _groupCount++) { ShmPsuData->PsuGroup[_groupCount].GroupPresentPsuQuantity = 0; ShmPsuData->PsuGroup[_groupCount].GroupAvailablePower = 0; ShmPsuData->PsuGroup[_groupCount].GroupAvailableCurrent = 0; ShmPsuData->PsuGroup[_groupCount].GroupErrorFlag.PsuGroupErrorValue = 0; } ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFuseBurnOut = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuPfcAndDcdcCommFault = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuBusVoltageUnbalance = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuBusOverVoltage = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuBusVoltageAbnormal = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuInputOVP = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDuplicateID = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuBusUnderVoltage = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuInputPhaseLoss = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuInputUVP = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuCommunicationFail = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuSevereUnevenCurrent = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFfcSideShutDown = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFanFullSpeed = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDcSideShutDown = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuPowerLimitedState = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuTemperaturePowerLimit = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuAcPowerLimit = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDcdcEepromFault = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuFanFailureAlarm = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuOutputShortCircuit = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuPfcEepromFault = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuCriticalPointOTP = NO; ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuDcdcOverVoltage = NO; } void Initialization() { bool isPass = false; while(!isPass) { isPass = true; for (byte _index = 0; _index < _maxGroupCount; _index++) { if (!FindChargingInfoData(_index, &chargingInfo[0])) { LOG_ERROR("Module_PsuComm : FindChargingInfoData false"); isPass = false; break; } } sleep(1); } ShmPsuData->GroupCount = _maxGroupCount; } void Await() { usleep(CMD_DELAY_TIME); } void PsuReceiveRecoveryCheck(void) { char *ptrSave, *ptrToken; int fd, psuTaskCount = 0; char pathBuffer[64], psuTaskPidString[64]; system("pidof Module_PsuComm > /tmp/Module_PsuTask"); snprintf(pathBuffer, sizeof(pathBuffer), "/tmp/Module_PsuTask"); fd = open(pathBuffer, O_RDWR); if(fd < 0) { return; } if(read(fd, psuTaskPidString, 64) < 0) { return; } ptrToken = strtok_r(psuTaskPidString, " ", &ptrSave); while(ptrToken != NULL) { int psuPid = atoi(ptrToken); if(psuPid > 0) { psuTaskCount++; } ptrToken = strtok_r(NULL, " ", &ptrSave); } close(fd); if(psuTaskCount == 1) { LOG_INFO("************ PSU Receive Task Need Recovery ************\n"); InitialCommunication(); psuReceiveRecovery = true; } } void ShowGroupMember(unsigned char group) { if(group < MAX_GROUP_QUANTITY) { if(ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0) { for(byte psu = 0; psu < ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity; psu++) { LOG_INFO("Group %d - Number = %d, SN = %02X", group + 1, psu, ShmPsuPosition->GroupLocationInfo[group].PsuSN[psu]); } } } } void ShowPsuVersion(unsigned char group) { if(group < MAX_GROUP_QUANTITY) { if(ShmPsuPosition->GroupLocationInfo[group].GroupPsuQuantity > 0) { LOG_INFO("Group %d PSU Version Info", group + 1); for(int i = 0; i < ShmPsuPosition->GroupLocationInfo[group].GroupPsuQuantity; i++) { int psuIndex = ShmPsuPosition->GroupLocationInfo[group].PsuSN[i]; LOG_INFO("PSU Primary: %s, Second: %s", ShmPsuData->PsuVersion[psuIndex].FwPrimaryVersion, ShmPsuData->PsuVersion[psuIndex].FwSecondVersion); } } } } void ShowGroupAvailableCurrentPower(unsigned char group) { if(group < MAX_GROUP_QUANTITY) { LOG_INFO("Group %d Available Current = %3d.%dA, Power = %3d.%dkW", group + 1, (ShmPsuData->PsuGroup[group].GroupAvailableCurrent / 10), (ShmPsuData->PsuGroup[group].GroupAvailableCurrent % 10), (ShmPsuData->PsuGroup[group].GroupAvailablePower / 10), (ShmPsuData->PsuGroup[group].GroupAvailablePower % 10)); } } unsigned char _TempType = 0; void PsuGroupRoutineQuery(void) { for(byte group = 0; group < GENERAL_GUN_QUANTITY; group++) { if(ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0) { if (psuCmdSeq == _PSU_CMD_CAP) { // 取系統總輸出能力 GetModuleCap(group); } else if (psuCmdSeq == _PSU_CMD_OUTPUT) { // 取各群輸出電壓電流 (float) GetModuleOutputF(group); } else if (psuCmdSeq == _PSU_CMD_IVAILIABLE) { // 取得模塊輸出額定電流能力 GetModuleIavailable(group); } else if (psuCmdSeq == _PSU_CMD_TEMP) { // 取得模塊溫度 if(_TempType == _PSU_TMP_DCDC) { GetDcTemperature(group); } else { GetPfcTemperature(group); } } } } if(psuCmdSeq == _PSU_CMD_CAP) { psuCmdSeq = _PSU_CMD_OUTPUT; } else if(psuCmdSeq == _PSU_CMD_OUTPUT) { psuCmdSeq = _PSU_CMD_IVAILIABLE; } else if(psuCmdSeq == _PSU_CMD_IVAILIABLE) { psuCmdSeq = _PSU_CMD_TEMP; _TempType = _TempType == _PSU_TMP_DCDC ? _PSU_TMP_PFC : _PSU_TMP_DCDC; } else { psuCmdSeq = _PSU_CMD_CAP; } Await(); } void SetPsuGroupRole(byte group, byte role) { if(group < GENERAL_GUN_QUANTITY) { ShmPsuGrouping->GroupCollection[group].Role = role; } } void SetPsuGroupToIdle(byte group) { if(group < GENERAL_GUN_QUANTITY) { SetPsuGroupRole(group, _GROLE_IDLE); memset(&ShmPsuGrouping->GroupCollection[group].Partner, 0x00, sizeof(PsuGroupPartner)); memset(&ShmPsuGrouping->GroupCollection[group].PossibleMember, 0x00, sizeof(PsuGroupPartner)); ShmPsuGrouping->GroupCollection[group].TargetGroup = 0; ShmPsuGrouping->GroupCollection[group].ReservedTarget = 0; //LOG_INFO("Reset Group[%02X] To Idle", group); } } // group: self group index void SetPsuGroupToMaster(byte group) { if(group < GENERAL_GUN_QUANTITY) { SetPsuGroupRole(group, _GROLE_MASTER); ShmPsuGrouping->GroupCollection[group].TargetGroup = group + 1; //LOG_INFO("Set Group[%02X] As Master To Gun %d", group, group + 1); } } // group: self group index // target: target group index + 1 void SetPsuGroupToSlave(byte group, byte target) { if(group < GENERAL_GUN_QUANTITY && target <= GENERAL_GUN_QUANTITY) { SetPsuGroupRole(group, _GROLE_SLAVE); ShmPsuGrouping->GroupCollection[group].TargetGroup = target; ShmPsuGrouping->GroupCollection[group].ReservedTarget = 0; memset(&ShmPsuGrouping->GroupCollection[group].PossibleMember, 0x00, sizeof(PsuGroupPartner)); ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.SlaveCtrlValue = 0; //LOG_INFO("Set Group[%02X] As Slave To Gun %d", group, target); } } // target: target group index + 1 // tPartner: set member to power off void PrepareToPowerOff(byte target, PsuGroupPartner *tPartner) { unsigned char master = 0, slave = 0; char strMember[64]; bool find = false; sprintf(strMember, "Set Member:"); for(int i = 0; i < tPartner->Quantity; i++) { slave = tPartner->Member[i]; master = ShmPsuGrouping->GroupCollection[slave].TargetGroup - 1; SetPsuGroupRole(slave, _GROLE_PREPARE_SWITCH_OFF); ShmPsuGrouping->GroupCollection[slave].ReservedTarget = target; ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedDerating = true; char strSlave[8]; sprintf(strSlave, " [%02X]", slave); strcat(strMember, strSlave); find = true; } if(find) { char strTemp[32]; if(target != 0) { sprintf(strTemp, " Prepare To Change To Gun %d", target); } else { sprintf(strTemp, " Prepare To Power Of"); } strcat(strMember, strTemp); LOG_INFO("%s", strMember); } } // target: target group index + 1 // tPartner: set member to prepare to attach on void PrepareToExtendCapability(byte target, PsuGroupPartner *tPartner) { char strMember[64]; sprintf(strMember, "Set Member:"); for(int i = 0; i < tPartner->Quantity; i++) { SetPsuGroupRole(tPartner->Member[i], _GROLE_PREPARE_ATTACH_ON); ShmPsuGrouping->GroupCollection[tPartner->Member[i]].ReservedTarget = target; char strSlave[8]; sprintf(strSlave, " [%02X]", tPartner->Member[i]); strcat(strMember, strSlave); } if(tPartner->Quantity > 0) { char strTemp[32]; sprintf(strTemp, " Prepare To Attach To Gun %d", target); strcat(strMember, strTemp); LOG_INFO("%s", strMember); } } void FindPsuGroupPartner(byte master, byte quantity, PsuGroupPartner *tPartner) { int slave = 0, location = 0; PsuGroupPartner partner; memset(&partner, 0x00, sizeof(PsuGroupPartner)); // search from left location = ShmPsuGrouping->GroupCollection[master].Location - 1; for(int i = location; i >= 0; i--) { if(partner.Quantity >= quantity) { break; } slave = ShmPsuGrouping->Layout[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_IDLE || (ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_WAIT_SLAVE && (ShmPsuGrouping->GroupCollection[slave].ReservedTarget - 1) == master)) { partner.Member[partner.Quantity++] = slave; } else { if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE && master == (ShmPsuGrouping->GroupCollection[slave].TargetGroup - 1)) { continue; } break; } } // search from right location = ShmPsuGrouping->GroupCollection[master].Location + 1; for(int i = location; i < ShmChargerInfo->Control.MaxConnector; i++) { if(partner.Quantity >= quantity) { break; } slave = ShmPsuGrouping->Layout[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_IDLE || (ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_WAIT_SLAVE && (ShmPsuGrouping->GroupCollection[slave].ReservedTarget - 1) == master)) { partner.Member[partner.Quantity++] = slave; } else { if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE && master == (ShmPsuGrouping->GroupCollection[slave].TargetGroup - 1)) { continue; } break; } } memcpy(tPartner, &partner, sizeof(PsuGroupPartner)); } int GetPsuGroupAvailable(byte group) { PsuGroupPartner partner; FindPsuGroupPartner(group, MAX_GROUP_QUANTITY, &partner); return partner.Quantity; } // return grab success or fail // return tPartner with possible member bool PsuGroupGrabCheck(byte group, PsuGroupPartner *tPartner) { int slave = 0, target = 0, location = 0, share = 0, average = 0, total = 0; if(ShmPsuGrouping->GroupCollection[group].Role != _GROLE_SLAVE && ShmPsuGrouping->GroupCollection[group].Role != _GROLE_REQUEST_TO_CHARGING) { return share > 0 ? true : false; } total = ShmPsuGrouping->GroupCollection[group].Role == _GROLE_REQUEST_TO_CHARGING ? GetPsuGroupAvailable(group) + 1 : 0; // search from left location = ShmPsuGrouping->GroupCollection[group].Location - 1; for(int i = location; i >= 0; i--) { slave = ShmPsuGrouping->Layout[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE && (ShmPsuGrouping->GroupCollection[group].Role == _GROLE_REQUEST_TO_CHARGING || ShmPsuGrouping->GroupCollection[slave].TargetGroup == ShmPsuGrouping->GroupCollection[group].TargetGroup)) { target = ShmPsuGrouping->GroupCollection[slave].TargetGroup - 1; if((ShmPsuGrouping->GroupCollection[target].Partner.Quantity + 1) > total) { average = (ShmPsuGrouping->GroupCollection[target].Partner.Quantity + 1 + total) / 2; share = average > 0 ? average - 1 : 0; } break; } else { break; } } if(share == 0) { // search from right location = ShmPsuGrouping->GroupCollection[group].Location + 1; for(int i = location; i < ShmChargerInfo->Control.MaxConnector; i++) { slave = ShmPsuGrouping->Layout[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE && (ShmPsuGrouping->GroupCollection[group].Role == _GROLE_REQUEST_TO_CHARGING || ShmPsuGrouping->GroupCollection[slave].TargetGroup == ShmPsuGrouping->GroupCollection[group].TargetGroup)) { target = ShmPsuGrouping->GroupCollection[slave].TargetGroup - 1; if((ShmPsuGrouping->GroupCollection[target].Partner.Quantity + 1) > total) { average = (ShmPsuGrouping->GroupCollection[target].Partner.Quantity + 1 + total) / 2; share = average > 0 ? average - 1 : 0; } break; } else { break; } } } tPartner->Quantity = share; tPartner->Member[0] = share > 0 ? slave : 0; return share > 0 ? true : false; } // return tPartner with possible member when group power off void PsuGroupPowerOffCheck(byte group, PsuGroupPartner *tPartner) { int master = 0, quantity = 0, location = 0; memset(tPartner, 0x00, sizeof(PsuGroupPartner)); if(ShmPsuGrouping->GroupCollection[group].Role != _GROLE_SLAVE) { return; } master = ShmPsuGrouping->GroupCollection[group].TargetGroup - 1; quantity = ShmPsuGrouping->GroupCollection[master].Partner.Quantity; for(int i = 0; i < quantity; i++) { if(tPartner->Quantity == 0) { if(group == ShmPsuGrouping->GroupCollection[master].Partner.Member[i]) { location = i; tPartner->Member[tPartner->Quantity] = group; tPartner->Quantity++; } } else { // find other group in the same direction if(ShmPsuGrouping->Location[ShmPsuGrouping->GroupCollection[master].Partner.Member[location]] < ShmPsuGrouping->Location[master]) { if(ShmPsuGrouping->Location[ShmPsuGrouping->GroupCollection[master].Partner.Member[i]] < ShmPsuGrouping->Location[master]) { tPartner->Member[tPartner->Quantity] = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; tPartner->Quantity++; //printf("\r\n Find Other Group %02X In The Same Direction", ShmPsuGrouping->GroupCollection[master].Partner.Member[i]); } } if(ShmPsuGrouping->Location[ShmPsuGrouping->GroupCollection[master].Partner.Member[location]] > ShmPsuGrouping->Location[master]) { if(ShmPsuGrouping->Location[ShmPsuGrouping->GroupCollection[master].Partner.Member[i]] > ShmPsuGrouping->Location[master]) { tPartner->Member[tPartner->Quantity] = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; tPartner->Quantity++; //printf("\r\n Find Other Group %02X In The Same Direction", ShmPsuGrouping->GroupCollection[master].Partner.Member[i]); } } } } } // group: group index, target: target index // master: master group index void AddMember(byte group, byte master) { if(group < GENERAL_GUN_QUANTITY && master < GENERAL_GUN_QUANTITY) { if(ShmPsuGrouping->GroupCollection[master].Role != _GROLE_MASTER && ShmPsuGrouping->GroupCollection[master].Role != _GROLE_REQUEST_TO_CHARGING) { return; } if(ShmPsuGrouping->GroupCollection[group].Role != _GROLE_IDLE && ShmPsuGrouping->GroupCollection[group].Role != _GROLE_WAIT_SLAVE && ShmPsuGrouping->GroupCollection[group].Role != _GROLE_PRECHARGE_READY) { return; } SetPsuGroupToSlave(group, master + 1); ShmPsuGrouping->GroupCollection[master].Partner.Member[ShmPsuGrouping->GroupCollection[master].Partner.Quantity++] = group; if(ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0) { ShmPsuGrouping->GroupCollection[master].Partner.RealQuantity++; ShmPsuGrouping->GroupCollection[master].GunPsuQuantity += ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity; } //printf("\r\n Add Group %02X To Gun %d (Quantity %d), Set Parallel Relay %d On", group, target + 1, ShmPsuGrouping->GroupCollection[target].Partner.Quantity, ParallelConfig); } } // master: master group index void AddAvailableMember(unsigned char master) { PsuGroupPartner partner; char strMember[64]; FindPsuGroupPartner(master, MAX_GROUP_QUANTITY, &partner); sprintf(strMember, "Gun %d Add Available Member:", master + 1); for(int i = 0; i < partner.Quantity; i++) { AddMember(partner.Member[i], master); char strSlave[8]; sprintf(strSlave, " [%02X]", partner.Member[i]); strcat(strMember, strSlave); } if(partner.Quantity > 0) { LOG_INFO("%s", strMember); } } // master: master group index // slave: slave group index void RemoveMember(unsigned char master, unsigned char slave) { bool find = false; int location = 0; unsigned char other = 0; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { if(slave == ShmPsuGrouping->GroupCollection[master].Partner.Member[i]) { ShmPsuGrouping->GroupCollection[master].Partner.Member[i] = 0; location = i; find = true; break; } } if(find) { for(int i = location + 1; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { other = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; ShmPsuGrouping->GroupCollection[master].Partner.Member[i] = 0; ShmPsuGrouping->GroupCollection[master].Partner.Member[i - 1] = other; } ShmPsuGrouping->GroupCollection[master].Partner.Quantity--; if(ShmPsuData->PsuGroup[slave].GroupPresentPsuQuantity > 0) { ShmPsuGrouping->GroupCollection[master].Partner.RealQuantity--; ShmPsuGrouping->GroupCollection[master].GunPsuQuantity -= ShmPsuData->PsuGroup[slave].GroupPresentPsuQuantity; } } } // master: master group index void RemoveNonGroupMember(unsigned char master) { unsigned char slave = 0; char strMember[64]; PsuGroupPartner partner; bool find = false; if(ShmPsuGrouping->GroupCollection[master].Role == _GROLE_MASTER) { sprintf(strMember, "Gun %d Remove Member:", master + 1); memcpy(&partner, &ShmPsuGrouping->GroupCollection[master].Partner, sizeof(PsuGroupPartner)); for(int i = 0; i < partner.Quantity; i++) { slave = partner.Member[i]; if(ShmPsuGrouping->GroupCollection[slave].Role != _GROLE_SLAVE) { RemoveMember(master, slave); if(ShmPsuGrouping->GroupCollection[slave].ReservedTarget == 0) { SetPsuGroupToIdle(slave); } else { SetPsuGroupRole(slave, _GROLE_WAIT_SLAVE); } char strSlave[8]; sprintf(strSlave, " [%02X]", slave); strcat(strMember, strSlave); find = true; } } if(find) { LOG_INFO("%s", strMember); } } } void SetPsuGroupPowerOnOff(unsigned char group, unsigned char power_on_off) { if(group < CONNECTOR_QUANTITY) { enum PSU_POWER_CMD power_cmd = power_on_off == PSU_POWER_ON ? PSU_POWER_ON : PSU_POWER_OFF; enum PSU_FLASH_CMD led_cmd = power_on_off == PSU_POWER_ON ? PSU_FLASH_ON : PSU_FLASH_NORMAL; if(ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0) { #if ONE_MODULE_OUTPUT // for safety test (inrush current) if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.InPrechargeMode) { SinglePsuPower(ShmPsuPosition->GroupLocationInfo[group].PsuSN[0], power_cmd); } else { SwitchPower(group, power_cmd); } #else SwitchPower(group, power_cmd); #endif Await(); FlashLed(group, led_cmd); Await(); } isStartOutputSwitch[group] = power_on_off == PSU_POWER_ON ? true : false; } } // master: master group index unsigned short GetPresentTargetCurrent(unsigned char master, unsigned char role_condition) { unsigned char slave = 0; unsigned short current = 0; current = ShmPsuGrouping->GroupOutput[master].GTargetCurrent; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; if(role_condition == _GROLE_MASTER || ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE) { current += ShmPsuGrouping->GroupOutput[slave].GTargetCurrent; } } return current; } // group: group index int GetPsuModuleQuantity(unsigned char group) { int quantity = 0; unsigned char slave = 0; quantity = ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity; for(int i = 0; i < ShmPsuGrouping->GroupCollection[group].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[group].Partner.Member[i]; quantity += ShmPsuData->PsuGroup[slave].GroupPresentPsuQuantity; } return quantity; } // group: group index bool IsAvailableGroup(unsigned char group) { return ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0 ? true : false; } // master: master group index int GetMasterAvailableGroup(unsigned char master) { int quantity = 0; unsigned char slave = 0; quantity = IsAvailableGroup(master) ? quantity + 1 : quantity; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; quantity = IsAvailableGroup(slave) ? quantity + 1 : quantity; } return quantity; } // group: group index void UpdateGunLoading(unsigned char group) { if(ShmChargerInfo->PsuGrouping.GroupCollection[group].TargetGroup != 0 && group == ShmChargerInfo->PsuGrouping.GroupCollection[group].TargetGroup - 1) { unsigned short TargetCurrent = (int)(chargingInfo[group]->EvBatterytargetCurrent * 10); ShmPsuGrouping->GroupCollection[group].GunLoading = chargingInfo[group]->AvailableChargingCurrent != 0 ? (TargetCurrent * 10000 / (int)chargingInfo[group]->AvailableChargingCurrent) : 0; } else { ShmPsuGrouping->GroupCollection[group].GunLoading = 0; } } // group: group index void UpdatePsuGroupLoading(unsigned char group) { ShmPsuGrouping->GroupOutput[group].OutputLoading = ShmPsuData->PsuGroup[group].GroupAvailableCurrent != 0 ? (ShmPsuGrouping->GroupOutput[group].GTargetCurrent * 10000 / ShmPsuData->PsuGroup[group].GroupAvailableCurrent) : 0; } // master: master group index void UpdateMasterPsuGroupLoading(unsigned char master) { unsigned char slave = 0; UpdatePsuGroupLoading(master); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; UpdatePsuGroupLoading(slave); } } void PsuGroupIncreaseCurrent(unsigned char group, unsigned short needIncrease, unsigned short *realIncreaseCurrent) { unsigned short increase = needIncrease; if((ShmPsuGrouping->GroupOutput[group].GTargetCurrent + increase) <= ShmPsuData->PsuGroup[group].GroupAvailableCurrent) { ShmPsuGrouping->GroupOutput[group].GTargetCurrent += increase; } else { increase = ShmPsuData->PsuGroup[group].GroupAvailableCurrent - ShmPsuGrouping->GroupOutput[group].GTargetCurrent; ShmPsuGrouping->GroupOutput[group].GTargetCurrent = ShmPsuData->PsuGroup[group].GroupAvailableCurrent; } *realIncreaseCurrent += increase; } void PsuGroupDecreaseCurrent(unsigned char group, unsigned short needDecrease, unsigned short *realDecreaseCurrent) { unsigned short decrease = needDecrease; if(ShmPsuGrouping->GroupOutput[group].GTargetCurrent >= (decrease + ZERO_CURRENT)) { ShmPsuGrouping->GroupOutput[group].GTargetCurrent -= decrease; } else { decrease = ShmPsuGrouping->GroupOutput[group].GTargetCurrent - ZERO_CURRENT; ShmPsuGrouping->GroupOutput[group].GTargetCurrent = ZERO_CURRENT; } *realDecreaseCurrent += decrease; } void IncreaseCurrentByGunLoading(unsigned char group, unsigned short loading, unsigned short *realIncrease, unsigned short MaxIncrease) { unsigned short target = 0, need = 0; if(*realIncrease >= MaxIncrease) { return; } target = (loading * ShmPsuData->PsuGroup[group].GroupAvailableCurrent) / 10000; need = target >= ShmPsuGrouping->GroupOutput[group].GTargetCurrent ? (target - ShmPsuGrouping->GroupOutput[group].GTargetCurrent) : 0; need = need > (MaxIncrease - *realIncrease) ? (MaxIncrease - *realIncrease) : need; //LOG_INFO("Group [%02X] Target Current %d.%d A, Increase Current: %d.%d A", // group, (target / 10), (target % 10), (need / 10), (need % 10)); if(need > 0) { PsuGroupIncreaseCurrent(group, need, realIncrease); } } void DecreaseCurrentByGunLoading(unsigned char group, unsigned short loading, unsigned short *realDecrease, unsigned short MaxDecrease) { unsigned short target = 0, need = 0; if(*realDecrease >= MaxDecrease) { return; } target = (loading * ShmPsuData->PsuGroup[group].GroupAvailableCurrent) / 10000; need = ShmPsuGrouping->GroupOutput[group].GTargetCurrent >= target ? (ShmPsuGrouping->GroupOutput[group].GTargetCurrent - target) : 0; need = need > (MaxDecrease - *realDecrease) ? (MaxDecrease - *realDecrease) : need; //LOG_INFO("Group [%02X] Target Current %d.%d A, Decrease Current: %d.%d A", // group, (target / 10), (target % 10), (need / 10), (need % 10)); if(need > 0) { PsuGroupDecreaseCurrent(group, need, realDecrease); } } // master: master group index void AverageIncreaseCurrent(unsigned char master, unsigned short NeedIncreaseCurrent) { unsigned char availableGroup = 0, slave = 0; unsigned short avgGroupCurrent = 0, excessCurrent = 0, realIncrease = 0; #if 0 PSU_LOG("Gun %d Increase By GunLoading %d.%02d Percent", master + 1, (ShmPsuGrouping->GroupCollection[master].GunLoading / 100), (ShmPsuGrouping->GroupCollection[master].GunLoading % 100)); #endif IncreaseCurrentByGunLoading(master, ShmPsuGrouping->GroupCollection[master].GunLoading, &realIncrease, NeedIncreaseCurrent); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; IncreaseCurrentByGunLoading(slave, ShmPsuGrouping->GroupCollection[master].GunLoading, &realIncrease, NeedIncreaseCurrent); } availableGroup = GetMasterAvailableGroup(master); if(NeedIncreaseCurrent > realIncrease && availableGroup > 0) { // increase current by group quantity avgGroupCurrent = (NeedIncreaseCurrent - realIncrease) / availableGroup; #if 0 PSU_LOG("Gun %d Increase By Group, AvgGroupCurrent %d.%d A", master + 1, (avgGroupCurrent / 10), (avgGroupCurrent % 10)); #endif PsuGroupIncreaseCurrent(master, avgGroupCurrent, &realIncrease); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; PsuGroupIncreaseCurrent(slave, avgGroupCurrent, &realIncrease); } } if(NeedIncreaseCurrent > realIncrease) { // increase excess current to master group excessCurrent = NeedIncreaseCurrent - realIncrease; #if 0 PSU_LOG("Gun %d Increase, ExcessCurrent %d.%d A", master + 1, (excessCurrent / 10), (excessCurrent % 10)); #endif PsuGroupIncreaseCurrent(master, excessCurrent, &realIncrease); } } // master: master group index void AverageDecreaseCurrent(unsigned char master, unsigned short NeedDecreaseCurrent) { unsigned char availableGroup = 0, slave = 0; unsigned short avgGroupCurrent = 0, excessCurrent = 0, realDecrease = 0; #if 0 PSU_LOG("Gun %d Decrease By GunLoading %d.%02d Percent", master + 1, (ShmPsuGrouping->GroupCollection[master].GunLoading / 100), (ShmPsuGrouping->GroupCollection[master].GunLoading % 100)); #endif DecreaseCurrentByGunLoading(master, ShmPsuGrouping->GroupCollection[master].GunLoading, &realDecrease, NeedDecreaseCurrent); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; DecreaseCurrentByGunLoading(slave, ShmPsuGrouping->GroupCollection[master].GunLoading, &realDecrease, NeedDecreaseCurrent); } availableGroup = GetMasterAvailableGroup(master); if(NeedDecreaseCurrent > realDecrease) { // decrease current by group quantity avgGroupCurrent = (NeedDecreaseCurrent - realDecrease) / availableGroup; #if 0 PSU_LOG("Gun %d Decrease By Group, AvgGroupCurrent %d.%d A", master + 1, (avgGroupCurrent / 10), (avgGroupCurrent % 10)); #endif PsuGroupDecreaseCurrent(master, avgGroupCurrent, &realDecrease); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; PsuGroupDecreaseCurrent(slave, avgGroupCurrent, &realDecrease); } } if(NeedDecreaseCurrent > realDecrease) { // decrease excess current excessCurrent = NeedDecreaseCurrent - realDecrease; #if 0 PSU_LOG("Gun %d Decrease, ExcessCurrent %d.%d A", master + 1, (excessCurrent / 10), (excessCurrent % 10)); #endif PsuGroupDecreaseCurrent(master, excessCurrent, &realDecrease); } } void MasterIncreaseCurrent(unsigned char master, unsigned short NeedIncreaseCurrent) { unsigned short excessCurrent = 0, realIncrease = 0; PsuGroupIncreaseCurrent(master, NeedIncreaseCurrent, &realIncrease); if(NeedIncreaseCurrent > realIncrease) { excessCurrent = NeedIncreaseCurrent - realIncrease; AverageIncreaseCurrent(master, excessCurrent); } } void MasterDecreaseCurrent(unsigned char master, unsigned short NeedDecreaseCurrent) { unsigned char slave = 0; unsigned short avgGroupCurrent = 0, excessCurrent = 0, realDecrease = 0; if(ShmPsuGrouping->GroupCollection[master].Partner.RealQuantity > 0) { // decrease current by group quantity avgGroupCurrent = NeedDecreaseCurrent / ShmPsuGrouping->GroupCollection[master].Partner.RealQuantity; #if 0 PSU_LOG("Gun %d Decrease By Group, AvgGroupCurrent %d.%d A", master + 1, (avgGroupCurrent / 10), (avgGroupCurrent % 10)); #endif for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; if(ShmPsuData->PsuGroup[slave].GroupPresentPsuQuantity > 0) { PsuGroupDecreaseCurrent(slave, avgGroupCurrent, &realDecrease); } } } if(NeedDecreaseCurrent > realDecrease) { excessCurrent = NeedDecreaseCurrent - realDecrease; PsuGroupDecreaseCurrent(master, excessCurrent, &realDecrease); } } void UpdateMaxCurrent(unsigned char master, unsigned short current) { int time = 0; // update max stage current if(current > StageMaxCurrent[master]) { StageMaxCurrent[master] = current; ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxStageCurrent = false; GetClockTime(&_StageCurrent_time[master]); } if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxStageCurrent) { time = GetTimeoutValue(_StageCurrent_time[master]) / uSEC_VAL; if(time >= EV_MAX_CURRENT_DELAY) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxStageCurrent = true; LOG_INFO("Gun %d Reach Max Stage Current", master + 1); } } // update max target current and reset max current time if(current > MaxCurrentDemand[master]) { if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxCurrentDemand) { LOG_INFO("Gun %d Max Current Demand Timer Reset", master + 1); } MaxCurrentDemand[master] = current; ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxCurrentDemand = false; GetClockTime(&_MaxCurrent_time[master]); } if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxCurrentDemand) { time = GetTimeoutValue(_MaxCurrent_time[master]) / uSEC_VAL; if(time >= EV_MAX_CURRENT_DELAY) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxCurrentDemand = true; LOG_INFO("Gun %d Reach Max Current Demand", master + 1); } } } // master: master group index bool IsMasterOutputCurrentStable(unsigned char master) { int time = 0; bool stable = false, reachTarget = false; unsigned short presentOutput = (int)chargingInfo[master]->PresentChargingCurrent * 10; unsigned short TargetCurrent = 0; TargetCurrent = (int)(chargingInfo[master]->EvBatterytargetCurrent * 10); reachTarget = presentOutput > (TargetCurrent - REACH_CURRENT_TOLERANCE) && presentOutput < (TargetCurrent + REACH_CURRENT_TOLERANCE); if((presentOutput >= StableOutputCurrent[master] && presentOutput - StableOutputCurrent[master] > REACH_CURRENT_TOLERANCE) || (presentOutput < StableOutputCurrent[master] && StableOutputCurrent[master] - presentOutput > REACH_CURRENT_TOLERANCE)) { GetClockTime(&_ReachCurrent_time[master]); StableOutputCurrent[master] = presentOutput; } time = GetTimeoutValue(_ReachCurrent_time[master]) / uSEC_VAL; if(reachTarget || time >= CURRENT_STABLE_TIME || (ShmChargerInfo->Control.FCharging[master].FCtrl.bits.EnableForceCharging && time >= CURRENT_REACH_TARGET_TIME)) { stable = true; } return stable; } // master: master group index void MasterBalanceCurrent(unsigned char master) { int time = 0; unsigned char slave = 0; unsigned short realBalance = 0, grabCurrent = 0; time = GetTimeoutValue(_BalanceCurrent_time[master]) / mSEC_VAL; if(time >= BALANCE_CURRENT_INTERVAL) { //LOG_INFO("Gun %d Start Balance Current", master + 1); DecreaseCurrentByGunLoading(master, ShmPsuGrouping->GroupCollection[master].GunLoading, &grabCurrent, MAX_ADJ_BALANCE_CURRENT); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; DecreaseCurrentByGunLoading(slave, ShmPsuGrouping->GroupCollection[master].GunLoading, &grabCurrent, MAX_ADJ_BALANCE_CURRENT); } //LOG_INFO("Gun %d Grab Balance Current %d.%d A", master + 1, (grabCurrent / 10), (grabCurrent % 10)); IncreaseCurrentByGunLoading(master, ShmPsuGrouping->GroupCollection[master].GunLoading, &realBalance, grabCurrent); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; IncreaseCurrentByGunLoading(slave, ShmPsuGrouping->GroupCollection[master].GunLoading, &realBalance, grabCurrent); } //LOG_INFO("Gun %d Increase Balance Current %d.%d A", master + 1, (realBalance / 10), (realBalance % 10)); UpdateMasterPsuGroupLoading(master); GetClockTime(&_BalanceCurrent_time[master]); } } unsigned short GetLoadingDiff(unsigned char master) { unsigned char slave = 0; unsigned short maxLoading = 0, minLoading = 0, diffLoading = 0; maxLoading = ShmPsuGrouping->GroupOutput[master].OutputLoading; minLoading = ShmPsuGrouping->GroupOutput[master].OutputLoading; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; if(ShmPsuData->PsuGroup[slave].GroupPresentPsuQuantity == 0 || ShmPsuData->PsuGroup[slave].GroupAvailableCurrent == 0) { continue; } maxLoading = ShmPsuGrouping->GroupOutput[slave].OutputLoading > maxLoading ? ShmPsuGrouping->GroupOutput[slave].OutputLoading : maxLoading; minLoading = ShmPsuGrouping->GroupOutput[slave].OutputLoading < minLoading ? ShmPsuGrouping->GroupOutput[slave].OutputLoading : minLoading; } diffLoading = maxLoading - minLoading; return diffLoading; } void CheckCurrentBalance(unsigned char master) { unsigned short diffLoading = 0; diffLoading = GetLoadingDiff(master); if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedCurrentBalance) { if(diffLoading >= START_BALANCE_CRITERIA) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedCurrentBalance = true; PSU_LOG("Gun %d Output Current Is Unbalance, diffLoading = %d.%02d", master + 1, (diffLoading / 100), (diffLoading % 100)); GetClockTime(&_BalanceCurrent_time[master]); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedCurrentBalance) { MasterBalanceCurrent(master); diffLoading = GetLoadingDiff(master); if(diffLoading <= STOP_BALANCE_CRITERIA) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedCurrentBalance = false; PSU_LOG("Gun %d Output Current Is Balance", master + 1); } } } bool IsOtherCharging(unsigned char group) { bool find = false; int other = 0, selfTarget = 0, location = 0; if(ShmPsuGrouping->GroupCollection[group].TargetGroup == 0) { return find; } selfTarget = ShmPsuGrouping->GroupCollection[group].TargetGroup - 1; // search from left location = ShmPsuGrouping->GroupCollection[group].Location - 1; for(int i = location; i >= 0; i--) { other = ShmPsuGrouping->Layout[i]; if(ShmPsuGrouping->GroupCollection[other].Role == _GROLE_MASTER) { find = other != selfTarget ? true : false; break; } } if(!find) { // search from right location = ShmPsuGrouping->GroupCollection[group].Location + 1; for(int i = location; i < ShmChargerInfo->Control.MaxConnector; i++) { other = ShmPsuGrouping->Layout[i]; if(ShmPsuGrouping->GroupCollection[other].Role == _GROLE_MASTER) { find = other != selfTarget ? true : false; break; } } } return find; } // master: master group index void MasterReleasePsuGroup(unsigned char master) { bool findOtherInCharging = false; unsigned char slave = 0, member = 0, releaseTarget = 0; unsigned char otherCharging[MAX_GROUP_QUANTITY] = {0}; unsigned char releaseList[MAX_GROUP_QUANTITY] = {0}; unsigned short releaseLoading[MAX_GROUP_QUANTITY] = {0}; PsuGroupPartner ReleaseMember; unsigned short target = 0, releaseAvailableCurrent = 0, originalAvailableCurrent = 0, originalLoading = 0, maxReleaseLoading = 0; if(ShmChargerInfo->PsuGrouping.GroupCollection[master].Role != _GROLE_MASTER || ShmPsuGrouping->GroupCollection[master].Partner.Quantity == 0) { return; } target = (int)(chargingInfo[master]->EvBatterytargetCurrent * 10); originalAvailableCurrent = (int)chargingInfo[master]->AvailableChargingCurrent; originalLoading = ShmPsuGrouping->GroupCollection[master].GunLoading; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; releaseAvailableCurrent = 0; PsuGroupPowerOffCheck(slave, &ReleaseMember); otherCharging[i] = IsOtherCharging(slave); findOtherInCharging = otherCharging[i] ? otherCharging[i] : findOtherInCharging; releaseList[i] = slave; for(int j = 0; j < ReleaseMember.Quantity; j++) { member = ReleaseMember.Member[j]; releaseAvailableCurrent += ShmPsuData->PsuGroup[member].GroupAvailableCurrent; } releaseLoading[i] = originalAvailableCurrent > releaseAvailableCurrent ? (target * 10000 / (originalAvailableCurrent - releaseAvailableCurrent)) : 0; //LOG_INFO("Gun %d Release [%02X], Total Release %d Group, Gun Loading %d.%02d >> %d.%02d", master + 1, slave, ReleaseMember.Quantity, // (originalLoading / 100), (originalLoading % 100), // (releaseLoading[i] / 100), (releaseLoading[i] % 100)); } for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { if((otherCharging[i] || !findOtherInCharging) && releaseLoading[i] > maxReleaseLoading && releaseLoading[i] < (EXTEND_LOADING - RELEASE_LOADING_OFFSET)) { maxReleaseLoading = releaseLoading[i]; releaseTarget = releaseList[i]; } } if(maxReleaseLoading > 0) { ShmPsuGrouping->GroupCollection[releaseTarget].GroupCtrl.bits.SlavePowerOffRequest = true; LOG_INFO("Gun %d Set [%02X] Release(%s), Gun Loading %d.%02d >> %d.%02d", master + 1, releaseTarget, findOtherInCharging ? "Other In Charging" : "Normal", (originalLoading / 100), (originalLoading % 100), (maxReleaseLoading / 100), (maxReleaseLoading % 100)); } } void CheckReleaseOrExtend(unsigned char master) { if((ShmChargerInfo->Control.TestCtrl.bits.ChargingSimulation || !ShmChargerInfo->Control.FCharging[master].FCtrl.bits.EnableForceCharging) && ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxStageCurrent && ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.DeratingCtrlValue == 0 && ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.ExtendCapabilityCtrlValue == 0) { if(ShmPsuGrouping->GroupCollection[master].Partner.Quantity > 0 && ShmPsuGrouping->GroupCollection[master].GunLoading < RELEASE_LOADING) { MasterReleasePsuGroup(master); } else if(ShmPsuGrouping->GroupCollection[master].GunLoading >= EXTEND_LOADING) { int available = GetPsuGroupAvailable(master); if(available > 0) { if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendAvailable) { LOG_INFO("Gun %d Extend Capability Available", master + 1); GetClockTime(&_ExtendCapability_time[master]); } ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendAvailable = true; } } } } bool IsPsuGroupOutputConfigDifferent(unsigned char group) { bool different = false; if(ShmPsuGrouping->GroupOutput[group].GTargetVoltage != PreGroupOutput[group].GTargetVoltage || ShmPsuGrouping->GroupOutput[group].GTargetCurrent != PreGroupOutput[group].GTargetCurrent) { different = true; } PreGroupOutput[group].GTargetVoltage = ShmPsuGrouping->GroupOutput[group].GTargetVoltage; PreGroupOutput[group].GTargetCurrent = ShmPsuGrouping->GroupOutput[group].GTargetCurrent; return different; } // Gun X Demand Change -> Voltage: XXXX.X V, Current: XXXX.X A, Available: XXXX.X A, Loading: XXX.XX // Master [XX] -> Voltage: XXXX.X V, Current: XXXX.X A, Available: XXXX.X A, Loading: XXX.XX // Member [XX] -> Voltage: XXXX.X V, Current: XXXX.X A, Available: XXXX.X A, Loading: XXX.XX void ShowPsuGroupOutputConfig(unsigned char master) { unsigned char slave = 0; PSU_LOG("Gun %d Demand Change -> Voltage: %4d V, Current: %4d.%d A, Available: %4d.%d A, Loading: %3d.%02d", master + 1, (ShmPsuGrouping->GroupOutput[master].GTargetVoltage / 10), ((int)chargingInfo[master]->EvBatterytargetCurrent), (((int)(chargingInfo[master]->EvBatterytargetCurrent * 10)) % 10), ((int)chargingInfo[master]->AvailableChargingCurrent / 10), ((int)chargingInfo[master]->AvailableChargingCurrent % 10), (ShmPsuGrouping->GroupCollection[master].GunLoading / 100), (ShmPsuGrouping->GroupCollection[master].GunLoading % 100)); PSU_LOG(" Master [%02X] -> Voltage: %4d V, Current: %4d.%d A, Available: %4d.%d A, Loading: %3d.%02d", master, (ShmPsuGrouping->GroupOutput[master].GTargetVoltage / 10), (ShmPsuGrouping->GroupOutput[master].GTargetCurrent / 10), (ShmPsuGrouping->GroupOutput[master].GTargetCurrent % 10), (ShmPsuData->PsuGroup[master].GroupAvailableCurrent / 10), (ShmPsuData->PsuGroup[master].GroupAvailableCurrent % 10), (ShmPsuGrouping->GroupOutput[master].OutputLoading / 100), (ShmPsuGrouping->GroupOutput[master].OutputLoading % 100)); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; PSU_LOG(" Member [%02X] -> Voltage: %4d V, Current: %4d.%d A, Available: %4d.%d A, Loading: %3d.%02d", slave, (ShmPsuGrouping->GroupOutput[slave].GTargetVoltage / 10), (ShmPsuGrouping->GroupOutput[slave].GTargetCurrent / 10), (ShmPsuGrouping->GroupOutput[slave].GTargetCurrent % 10), (ShmPsuData->PsuGroup[slave].GroupAvailableCurrent / 10), (ShmPsuData->PsuGroup[slave].GroupAvailableCurrent % 10), (ShmPsuGrouping->GroupOutput[slave].OutputLoading / 100), (ShmPsuGrouping->GroupOutput[slave].OutputLoading % 100)); } } void PsuGroupOutputConfigCheck(unsigned char master) { unsigned char slave = 0; bool show = false; show = IsPsuGroupOutputConfigDifferent(master); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; if(IsPsuGroupOutputConfigDifferent(slave)) { show = true; } } if(show) { ShowPsuGroupOutputConfig(master); } } void StepRecognition(unsigned char master, unsigned short voltage) { if(_GfdStep[master] == PREPARE_STEP_NONE && voltage > 0) { // gfd step _GfdStep[master] = PREPARE_STEP_CABLE_CHECK; LOG_INFO("Gun %d GFD Start Step", master + 1); } else if(_GfdStep[master] == PREPARE_STEP_CABLE_CHECK && voltage == 0) { // gfd done step _GfdStep[master] = PREPARE_STEP_GFD_DONE; LOG_INFO("Gun %d GFD Stop Step", master + 1); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.CableCheckDone = true; } else if(_GfdStep[master] == PREPARE_STEP_GFD_DONE && voltage >= SAFETY_PRECHARGE_OFFSET) { // precharge step _GfdStep[master] = PREPARE_STEP_PRECHARGE; LOG_INFO("Gun %d PreCharge Step", master + 1); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.InPrechargeMode = true; } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AlreadyInChargingMode) { // charging step if(_GfdStep[master] != PREPARE_STEP_CHARGING) { LOG_INFO("Gun %d Charging Step", master + 1); } _GfdStep[master] = PREPARE_STEP_CHARGING; if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.InPrechargeMode) { if(_VoltageResumeCnt[master] < MAX_CHARGING_DELAY_COUNT) { int time = GetTimeoutValue(_ChargingDelay_time[master]) / mSEC_VAL; if(time >= CHARGING_DELAY_INTERVAL) { GetClockTime(&_ChargingDelay_time[master]); _VoltageResumeCnt[master]++; } } else { ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.InPrechargeMode = false; } } } } // master: master group index // group: self group index void UpdatePsuGroupOutputConfig(unsigned char master) { unsigned char slave = 0; unsigned short TargetVoltage = 0, TargetCurrent = 0, PresentTargetCurrent = 0; unsigned short current = 0; TargetVoltage = (int)(chargingInfo[master]->EvBatterytargetVoltage * 10); TargetCurrent = (int)(chargingInfo[master]->EvBatterytargetCurrent * 10); if(chargingInfo[master]->RelayK1K2Status || ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.CableCheckDone) { // update target voltage if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingConfirmed) { StepRecognition(master, TargetVoltage); #if PRECHARGE_OFFSET // for safety test (inrush current) if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AlreadyInChargingMode) { GCTargetVoltage[master] = _GfdStep[master] == PREPARE_STEP_PRECHARGE ? TargetVoltage - SAFETY_PRECHARGE_OFFSET : TargetVoltage; } else { if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.InPrechargeMode) { GCTargetVoltage[master] = TargetVoltage > (GCTargetVoltage[master] + VOLTAGE_RESUME_STEP) ? (GCTargetVoltage[master] + VOLTAGE_RESUME_STEP) : TargetVoltage; } else { GCTargetVoltage[master] = TargetVoltage; } } #else GCTargetVoltage[master] = TargetVoltage; #endif ShmPsuGrouping->GroupOutput[master].GTargetVoltage = GCTargetVoltage[master]; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; #if ONE_MODULE_OUTPUT // for safety test (inrush current) if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.InPrechargeMode) { ShmPsuGrouping->GroupOutput[slave].GTargetVoltage = 0; } else { ShmPsuGrouping->GroupOutput[slave].GTargetVoltage = GCTargetVoltage[master]; } #else ShmPsuGrouping->GroupOutput[slave].GTargetVoltage = GCTargetVoltage[master]; #endif } } // update target current if(((ShmPsuGrouping->GroupCollection[master].Partner.RealQuantity + 1) * ZERO_CURRENT) > TargetCurrent || !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AlreadyInChargingMode) { current = ZERO_CURRENT; #if 0 if(ShmPsuGrouping->GroupOutput[master].GTargetCurrent != current) { LOG_INFO("Gun %d Need Minimum Target Current = %d", master + 1, current); } #endif ShmPsuGrouping->GroupOutput[master].GTargetCurrent = current; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE || ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_PREPARE_SWITCH_OFF) { #if ONE_MODULE_OUTPUT // for safety test (inrush current) ShmPsuGrouping->GroupOutput[slave].GTargetCurrent = 0; #else ShmPsuGrouping->GroupOutput[slave].GTargetCurrent = ShmPsuData->PsuGroup[slave].GroupPresentPsuQuantity > 0 ? ZERO_CURRENT : 0; #endif } } } else { if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingConfirmed) { // master in normal output mode PresentTargetCurrent = GetPresentTargetCurrent(master, _GROLE_MASTER); current = TargetCurrent <= (int)chargingInfo[master]->AvailableChargingCurrent ? TargetCurrent : (int)chargingInfo[master]->AvailableChargingCurrent; #if 0 if(GCTargetCurrent[master] != current) { LOG_INFO("Gun %d Need Normal Target Current = %d", master + 1, current); } #endif GCTargetCurrent[master] = current; UpdateMaxCurrent(master, GCTargetCurrent[master]); if(GCTargetCurrent[master] > PresentTargetCurrent) { // increase current OutputConfigStep[master] = _CURRENT_MODE_INCREASE; PSU_LOG("Gun %d Increase Current: %d.%d A, %d.%d A -> %d.%d A", master + 1, ((GCTargetCurrent[master] - PresentTargetCurrent) / 10), ((GCTargetCurrent[master] - PresentTargetCurrent) % 10), (PresentTargetCurrent / 10), (PresentTargetCurrent % 10), (GCTargetCurrent[master] / 10), (GCTargetCurrent[master] % 10)); #if MASTER_OUTPUT_FIRST // for safety test if(GCTargetCurrent[master] <= CURRENT_BALANCE_CRITERIA) { MasterIncreaseCurrent(master, GCTargetCurrent[master] - PresentTargetCurrent); } else { AverageIncreaseCurrent(master, GCTargetCurrent[master] - PresentTargetCurrent); } #else AverageIncreaseCurrent(master, GCTargetCurrent[master] - PresentTargetCurrent); #endif } else if(GCTargetCurrent[master] < PresentTargetCurrent) { // decrease current OutputConfigStep[master] = _CURRENT_MODE_DECREASE; PSU_LOG("Gun %d Decrease Current: %d.%d A, %d.%d A -> %d.%d A", master + 1, ((PresentTargetCurrent - GCTargetCurrent[master]) / 10), ((PresentTargetCurrent - GCTargetCurrent[master]) % 10), (PresentTargetCurrent / 10), (PresentTargetCurrent % 10), (GCTargetCurrent[master] / 10), (GCTargetCurrent[master] % 10)); #if MASTER_OUTPUT_FIRST // for safety test if(GCTargetCurrent[master] <= ShmPsuGrouping->GroupCollection[master].Partner.RealQuantity * CURRENT_BALANCE_CRITERIA) { MasterDecreaseCurrent(master, PresentTargetCurrent - GCTargetCurrent[master]); } else { AverageDecreaseCurrent(master, PresentTargetCurrent - GCTargetCurrent[master]); } #else AverageDecreaseCurrent(master, PresentTargetCurrent - GCTargetCurrent[master]); #endif } else { // balance current or do not change OutputConfigStep[master] = _CURRENT_MODE_BALANCE; if(OutputConfigStep[master] != _preOutputConfigStep[master]) { GetClockTime(&_ReachCurrent_time[master]); LOG_INFO("Gun %d In Balance Mode", master + 1); StableOutputCurrent[master] = (int)chargingInfo[master]->PresentChargingCurrent * 10; ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.OutputCurrentStable = false; ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedCurrentBalance = false; } if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.OutputCurrentStable) { if(IsMasterOutputCurrentStable(master)) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.OutputCurrentStable = true; LOG_INFO("Gun %d Output Current = %5.1f A Stable", master + 1, chargingInfo[master]->PresentChargingCurrent); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.OutputCurrentStable) { #if MASTER_OUTPUT_FIRST // for safety test if(GCTargetCurrent[master] > ShmPsuGrouping->GroupCollection[master].Partner.RealQuantity * CURRENT_BALANCE_CRITERIA) { CheckCurrentBalance(master); } else { ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedCurrentBalance = false; } #else CheckCurrentBalance(master); #endif if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedCurrentBalance) { CheckReleaseOrExtend(master); } } } } else { // master in derating mode OutputConfigStep[master] = _CURRENT_MODE_DERATING; if(OutputConfigStep[master] != _preOutputConfigStep[master]) { LOG_INFO("Gun %d In Derating Mode", master + 1); } } _preOutputConfigStep[master] = OutputConfigStep[master]; } } else { ShmPsuGrouping->GroupOutput[master].GTargetVoltage = 0; ShmPsuGrouping->GroupOutput[master].GTargetCurrent = 0; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; ShmPsuGrouping->GroupOutput[slave].GTargetVoltage = 0; ShmPsuGrouping->GroupOutput[slave].GTargetCurrent = 0; } } } // group: self group index void SetPsuGroupOutput(unsigned char group) { int time = 0; if(ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0) { #if ONE_MODULE_OUTPUT // for safety test (inrush current) if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.InPrechargeMode) { SingleOutputVol(ShmPsuPosition->GroupLocationInfo[group].PsuSN[0], ShmPsuGrouping->GroupOutput[group].GTargetVoltage, ShmPsuGrouping->GroupOutput[group].GTargetCurrent); } else { PresentOutputVol(group, ShmPsuGrouping->GroupOutput[group].GTargetVoltage, ShmPsuGrouping->GroupOutput[group].GTargetCurrent); } #else PresentOutputVol(group, ShmPsuGrouping->GroupOutput[group].GTargetVoltage, ShmPsuGrouping->GroupOutput[group].GTargetCurrent); #endif Await(); } //UpdatePsuGroupLoading(group); UpdateGunLoading(group); if(ShmPsuGrouping->GroupOutput[group].GTargetVoltage >= PSU_MIN_VOL) { if(!isStartOutputSwitch[group]) { SetPsuGroupPowerOnOff(group, PSU_POWER_ON); GetClockTime(&_PoweOnOff_time[group]); } time = GetTimeoutValue(_PoweOnOff_time[group]) / uSEC_VAL; if(time > POWER_ONOFF_RESEND_INTERVAL) { SetPsuGroupPowerOnOff(group, PSU_POWER_ON); GetClockTime(&_PoweOnOff_time[group]); } } else { if(isStartOutputSwitch[group]) { SetPsuGroupPowerOnOff(group, PSU_POWER_OFF); GetClockTime(&_PoweOnOff_time[group]); } time = GetTimeoutValue(_PoweOnOff_time[group]) / uSEC_VAL; if(time > POWER_ONOFF_RESEND_INTERVAL) { SetPsuGroupPowerOnOff(group, PSU_POWER_OFF); GetClockTime(&_PoweOnOff_time[group]); } } } // master: master group index bool CanMasterStartDerating(unsigned char master) { bool start = false; unsigned short TargetCurrent = 0; TargetCurrent = (int)(chargingInfo[master]->EvBatterytargetCurrent * 10); if(TargetCurrent <= ShmPsuGrouping->GroupCollection[master].ReAssignAvailableCurrent) { start = true; } return start; } // master: master group index // role_condition: when role_condition = _GROLE_MASTER, stop all member void SetMemberStartPowerOff(unsigned char master, unsigned char role_condition) { unsigned char slave = 0; char strMember[64]; bool find = false; if(ShmPsuGrouping->GroupCollection[master].Role == _GROLE_MASTER) { sprintf(strMember, "Gun %d Set Member:", master + 1); for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; if(role_condition == _GROLE_MASTER || ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_PREPARE_SWITCH_OFF) { SetPsuGroupRole(slave, _GROLE_SLAVE_POWER_OFF); char strSlave[8]; sprintf(strSlave, " [%02X]", slave); strcat(strMember, strSlave); find = true; } } if(find) { strcat(strMember, " Power Off"); LOG_INFO("%s", strMember); } } } // master: master group index bool IsMemberPowerOffOK(unsigned char master) { bool done = true; unsigned char slave = 0; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; if(ShmPsuGrouping->GroupCollection[slave].Role != _GROLE_SLAVE && ShmPsuGrouping->GroupCollection[slave].Role != _GROLE_SWITCH_OFF_OK) { done = false; } } return done; } // master: master group index void SetMemberPowerOffDone(unsigned char master) { unsigned char slave = 0; if(ShmPsuGrouping->GroupCollection[master].Role == _GROLE_MASTER) { for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].Partner.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].Partner.Member[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SWITCH_OFF_OK) { SetPsuGroupRole(slave, _GROLE_WAIT_IDLE); } else if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE_POWER_OFF) { // can not power off normally SetPsuGroupRole(slave, _GROLE_TERMINATE); } } } } unsigned char _ParallelRelay[MAX_GROUP_QUANTITY]; void SetPCabinetParallelRelay(unsigned short parallelConfig) { bool change = false; if(ShmChargerInfo->Control.CabinetRole == _CROLE_MASTER) { for(int i = 0; i < MAX_SLAVE_CABINET_QUANTITY; i++) { change = false; if(ShmChargerInfo->ParallelCabinet.PCabinet[i].LocalStatus == _DeviceStatus_Idle || ShmChargerInfo->ParallelCabinet.PCabinet[i].LocalStatus == _DeviceStatus_Alarm || ShmChargerInfo->ParallelCabinet.PCabinet[i].LocalStatus == _DeviceStatus_Charging) { for(int j = 0; j < GENERAL_GUN_QUANTITY; j++) { _ParallelRelay[j] = (parallelConfig & (1 << j)) > 0 ? YES : NO; if(_ParallelRelay[i] != ShmChargerInfo->ParallelCabinet.PCabinet[i].ParallelRelaySetting[i]) { change = true; } } if(change) { char strParallel[128]; sprintf(strParallel, "Set PCabinet %d Parallel Relay:", i + 1); for(int j = 0; j < GENERAL_GUN_QUANTITY; j++) { char strState[8]; sprintf(strState, " %3s", _ParallelRelay[j] ? "ON" : "OFF"); strcat(strParallel, strState); } LOG_INFO("%s", strParallel); } ShmChargerInfo->ParallelCabinet.PCabinet[i].ParallelRelaySetting[i] = _ParallelRelay[i]; } } } } // master: master group index // Yes_No: Yes: relay on, No: relay off // role_condition: when role_condition = _GROLE_MASTER, parallel relay of all member do the action(on or off) void SetParallelRelayOnOff(unsigned char master, unsigned char Yes_No, unsigned char role_condition) { int ParallelConfig = 0; unsigned char slave = 0; PsuGroupPartner *partner; if(ShmPsuGrouping->GroupCollection[master].Role == _GROLE_MASTER || (ShmPsuGrouping->GroupCollection[master].Role == _GROLE_REQUEST_TO_CHARGING && Yes_No)) { ShmPsuGrouping->GroupCollection[master].ParallelCheck = 0; partner = role_condition == _GROLE_PRECHARGE_READY ? &ShmPsuGrouping->GroupCollection[master].PossibleMember : &ShmPsuGrouping->GroupCollection[master].Partner; for(int i = 0; i < partner->Quantity; i++) { slave = partner->Member[i]; if(role_condition == _GROLE_MASTER || ShmPsuGrouping->GroupCollection[slave].Role != _GROLE_SLAVE) { ParallelConfig = ShmPsuGrouping->GroupCollection[master].ParallelConfig[slave]; if(ParallelConfig != 0) { ShmPsuGrouping->GroupCollection[master].ParallelCheck |= (1 << (ParallelConfig - 1)); } } } if(Yes_No) { ShmPsuGrouping->ParallelRelayConfig.CtrlValue |= ShmPsuGrouping->GroupCollection[master].ParallelCheck; } else { ShmPsuGrouping->ParallelRelayConfig.CtrlValue &= ~ShmPsuGrouping->GroupCollection[master].ParallelCheck; } SetPCabinetParallelRelay(ShmPsuGrouping->ParallelRelayConfig.CtrlValue); } } // master: master group index bool IsParallelRelayConfirmed(unsigned char master) { bool confirmed = true; for(int i = 0; i < CONNECTOR_QUANTITY; i++) { if((1 << i) & ShmPsuGrouping->GroupCollection[master].ParallelCheck) { if((ShmPsuGrouping->ParallelRelayConfig.CtrlValue & (1 << i)) != (ShmPsuGrouping->ParallelRelayConfirmed.CtrlValue & (1 << i))) { confirmed = false; break; } } } return confirmed; } // master: master group index bool IsPossibleMemberReady(unsigned char master) { bool ready = true; unsigned char slave = 0; if(ShmPsuGrouping->GroupCollection[master].PossibleMember.Quantity > 0) { for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].PossibleMember.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].PossibleMember.Member[i]; if(ShmPsuGrouping->GroupCollection[slave].Role != _GROLE_WAIT_SLAVE) { ready = false; } } } return ready; } // master: master group index bool IsExtendPrechargeReady(unsigned char master) { bool ready = true; unsigned char slave = 0; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].PossibleMember.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].PossibleMember.Member[i]; if(ShmPsuData->PsuGroup[slave].GroupPresentPsuQuantity == 0 || ShmPsuData->PsuGroup[slave].GroupAvailableCurrent == 0) { continue; } if(ShmPsuGrouping->GroupCollection[slave].Role != _GROLE_PREPARE_ATTACH_ON || ShmPsuData->PsuGroup[slave].GroupPresentOutputVoltage > (ShmPsuData->PsuGroup[master].GroupPresentOutputVoltage + PRECHARGE_RANGE_VOLTAGE) || ShmPsuData->PsuGroup[slave].GroupPresentOutputVoltage < (ShmPsuData->PsuGroup[master].GroupPresentOutputVoltage - PRECHARGE_OFFSET_VOLTAGE- PRECHARGE_RANGE_VOLTAGE)) { ready = false; } } return ready; } // master: master group index void SetExtendPrechargeCompleted(unsigned char master) { unsigned char slave = 0; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].PossibleMember.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].PossibleMember.Member[i]; SetPsuGroupRole(slave, _GROLE_PRECHARGE_READY); } } // master: master group index void SetExtendPrechargeStop(unsigned char master) { unsigned char slave = 0; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].PossibleMember.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].PossibleMember.Member[i]; SetPsuGroupRole(slave, _GROLE_EXTEND_STOP); } memset(&ShmPsuGrouping->GroupCollection[master].PossibleMember, 0x00, sizeof(PsuGroupPartner)); } // master: master group index void AddExtendMember(unsigned char master) { unsigned char slave = 0; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].PossibleMember.Quantity; i++) { slave = ShmPsuGrouping->GroupCollection[master].PossibleMember.Member[i]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_PRECHARGE_READY) { AddMember(slave, master); } } } // group: self group index void CheckChargingRequest(unsigned char group) { if(ShmPsuGrouping->GroupCollection[group].Role != _GROLE_IDLE) { return; } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ChargingRequest) { SetPsuGroupRole(group, _GROLE_REQUEST_TO_CHARGING); } } // group: self group index void ChargingRequestProcess(unsigned char group) { int time = 0; PsuGroupPartner partner; if(ShmPsuGrouping->GroupCollection[group].Role != _GROLE_REQUEST_TO_CHARGING) { return; } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ChargingRequest && !ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ChargingRequestConfirmed) { GetClockTime(&_ChargingRequest_time[group]); ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ChargingRequest = false; ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ChargingRequestConfirmed = true; ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.GroupShareCheck = true; } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.GroupShareCheck && !ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ShareConfirmed) { // check is there psu group to grab if(PsuGroupGrabCheck(group, &partner)) { GetClockTime(&_ChargingRequest_time[group]); ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.GrabGroupWait = true; PrepareToPowerOff(group + 1, &partner); memcpy(&ShmPsuGrouping->GroupCollection[group].PossibleMember, &partner, sizeof(PsuGroupPartner)); } else { ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ShareCheckDone = true; LOG_INFO("Gun %d Grab Nothing", group + 1); } ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ShareConfirmed = true; } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.GrabGroupWait && !ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ShareCheckDone) { // wait until grab psu ready or timeout bool ready = IsPossibleMemberReady(group); time = GetTimeoutValue(_ChargingRequest_time[group]) / uSEC_VAL; if(ready || time >= WAIT_GRAB_TIME) { ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ShareCheckDone = true; LOG_INFO("Gun %d Grab %s", group + 1, ready ? "OK" : "Timeout"); } } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ShareCheckDone && !ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.FindGroupPartner) { GetClockTime(&_ChargingRequest_time[group]); AddAvailableMember(group); ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.FindGroupPartner = true; } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.FindGroupPartner && !ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ParallelRelayOn) { // after a little delay, set parallel relay on time = GetTimeoutValue(_ChargingRequest_time[group]) / uSEC_VAL; if(time >= WAIT_PARALLEL_RELAY_DELAY) { unsigned short original = ShmPsuGrouping->ParallelRelayConfig.CtrlValue; GetClockTime(&_ChargingRequest_time[group]); SetParallelRelayOnOff(group, YES, _GROLE_MASTER); ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ParallelRelayOn = true; LOG_INFO("Gun %d Charging Request Set All Member Parallel Relay On %X -> %X", group + 1, original, ShmPsuGrouping->ParallelRelayConfig.CtrlValue); } } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ParallelRelayOn && !ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ParallelRelayConfirmed) { // wait until parallel relay on confirmed bool confirmed = IsParallelRelayConfirmed(group); time = GetTimeoutValue(_ChargingRequest_time[group]) / uSEC_VAL; //if(confirmed || time >= WAIT_RELAY_CONFIRMED_TIME) if(time >= WAIT_RELAY_CONFIRMED_TIME) { GetClockTime(&_ChargingRequest_time[group]); ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ParallelRelayConfirmed = true; LOG_INFO("Gun %d Charging Request Parallel Relay Confirmed %s", group + 1, confirmed ? "OK" : "Timeout"); } } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ParallelRelayConfirmed && !ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.GroupingDone) { ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.GroupingDone = true; } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.GroupingDone) { SetPsuGroupToMaster(group); ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.IdleCtrlValue = 0; LOG_INFO("Gun %d Grouping Completed", group + 1); } } // master: master group index void PsuGroupDeratingProcess(unsigned char master) { int time = 0; // slave group set NeedDerating flag to start re-assign if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedDerating) { // reduce output current capability start GetClockTime(&_PsuGroupDerating_time[master]); unsigned short ReAssignCurrent = GetPresentTargetCurrent(master, _GROLE_SLAVE); ShmPsuGrouping->GroupCollection[master].ReAssignAvailableCurrent = ReAssignCurrent; ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.NeedDerating = false; LOG_INFO("Gun %d DeratingConfirmed%s, ReAssignAvailableCurrent = %d.%d A", master + 1, ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingConfirmed ? " Again" : "", (ReAssignCurrent / 10), (ReAssignCurrent % 10)); if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingConfirmed) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.DeratingCtrlValue = 0; } ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingConfirmed = true; } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingConfirmed && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingStart) { // wait until derating from ev or timeout bool start = CanMasterStartDerating(master); bool bypass = ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AlreadyInChargingMode ? false : true; time = GetTimeoutValue(_PsuGroupDerating_time[master]) / uSEC_VAL; if(start || time >= WAIT_EV_DERATING_TIMEOUT || bypass) { GetClockTime(&_PsuGroupDerating_time[master]); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingStart = true; LOG_INFO("Gun %d %s Start Derating%s", master + 1, start ? "Normal" : "Force", bypass ? " (Bypass)" : ""); SetMemberStartPowerOff(master, _GROLE_PREPARE_SWITCH_OFF); if(!bypass) { // update stage max current and reset max current time StageMaxCurrent[master] = start ? (int)(chargingInfo[master]->EvBatterytargetCurrent * 10) : ShmPsuGrouping->GroupCollection[master].ReAssignAvailableCurrent; ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxStageCurrent = false; GetClockTime(&_StageCurrent_time[master]); } } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingStart && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingPowerOffDone) { // wait until psu member power off ok or timeout bool power_off_ok = IsMemberPowerOffOK(master); time = GetTimeoutValue(_PsuGroupDerating_time[master]) / uSEC_VAL; if(power_off_ok || time >= WAIT_SLAVE_POWER_OFF_TIMEOUT) { GetClockTime(&_PsuGroupDerating_time[master]); SetMemberPowerOffDone(master); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingPowerOffDone = true; LOG_INFO("Gun %d Set Derating Member Power Off %s", master + 1, power_off_ok ? "OK" : "Timeout"); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingPowerOffDone && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingRelayOff) { // after a little delay, set parallel relay off time = GetTimeoutValue(_PsuGroupDerating_time[master]) / uSEC_VAL; if(time >= WAIT_PARALLEL_RELAY_DELAY) { unsigned short original = ShmPsuGrouping->ParallelRelayConfig.CtrlValue; GetClockTime(&_PsuGroupDerating_time[master]); SetParallelRelayOnOff(master, NO, _GROLE_SWITCH_OFF_OK); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingRelayOff = true; LOG_INFO("Gun %d Set Parallel Relay Off %X -> %X", master + 1, original, ShmPsuGrouping->ParallelRelayConfig.CtrlValue); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingRelayOff && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingRelayConfirmed) { // wait until parallel relay off confirmed bool confirmed = IsParallelRelayConfirmed(master); time = GetTimeoutValue(_PsuGroupDerating_time[master]) / uSEC_VAL; //if(confirmed || time >= WAIT_RELAY_CONFIRMED_TIME) if(time >= WAIT_RELAY_CONFIRMED_TIME) { GetClockTime(&_PsuGroupDerating_time[master]); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingRelayConfirmed = true; LOG_INFO("Gun %d Parallel Relay Confirmed %s", master + 1, confirmed ? "OK" : "Timeout"); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingRelayConfirmed && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingCompleted) { // remove all non group member RemoveNonGroupMember(master); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingCompleted = true; } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingCompleted) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.DeratingCtrlValue = 0; UpdateGunAvailableCapability(master); LOG_INFO("Gun %d Derating Completed", master + 1); } } void MasterStopChargingProcess(unsigned char master) { int time = 0; if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.StopChargingRequest && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.StopChargingConfirmed) { // set all member to power off GetClockTime(&_StopCharging_time[master]); SetMemberStartPowerOff(master, _GROLE_MASTER); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.StopChargingConfirmed = true; } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.StopChargingConfirmed && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllPowerOffDone) { // wait until psu member power off ok or timeout bool power_off_ok = IsMemberPowerOffOK(master); time = GetTimeoutValue(_StopCharging_time[master]) / uSEC_VAL; if(power_off_ok || time >= WAIT_SLAVE_POWER_OFF_TIMEOUT) { GetClockTime(&_StopCharging_time[master]); SetMemberPowerOffDone(master); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllPowerOffDone = true; LOG_INFO("Gun %d Set All Member Power Off %s", master + 1, power_off_ok ? "OK" : "Timeout"); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllPowerOffDone && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllParallelRelayOff) { // after a little delay, set all member parallel relay off time = GetTimeoutValue(_StopCharging_time[master]) / uSEC_VAL; if(time >= WAIT_PARALLEL_RELAY_DELAY) { unsigned short original = ShmPsuGrouping->ParallelRelayConfig.CtrlValue; GetClockTime(&_StopCharging_time[master]); SetParallelRelayOnOff(master, NO, _GROLE_MASTER); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllParallelRelayOff = true; LOG_INFO("Gun %d Set All Member Parallel Relay Off %X -> %X", master + 1, original, ShmPsuGrouping->ParallelRelayConfig.CtrlValue); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllParallelRelayOff && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllParallelRelayConfirmed) { // wait until parallel relay off confirmed bool confirmed = IsParallelRelayConfirmed(master); time = GetTimeoutValue(_StopCharging_time[master]) / uSEC_VAL; //if(confirmed || time >= WAIT_RELAY_CONFIRMED_TIME) if(time >= WAIT_RELAY_CONFIRMED_TIME) { GetClockTime(&_StopCharging_time[master]); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllParallelRelayConfirmed = true; LOG_INFO("Gun %d All Member Parallel Relay Confirmed %s", master + 1, confirmed ? "OK" : "Timeout"); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllParallelRelayConfirmed && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllMemberStopCompleted) { // remove all non group member RemoveNonGroupMember(master); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllMemberStopCompleted = true; LOG_INFO("Gun %d All Member Stop Completed", master + 1); } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.AllMemberStopCompleted && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.StopChargingCompleted) { // check self group power down or not if(ShmPsuData->PsuGroup[master].GroupPresentOutputCurrent <= MAX_PSU_POWER_OFF_CURRENT) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.StopChargingCompleted = true; } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.StopChargingCompleted) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.MasterCtrlValue = 0; ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.StopChargingCtrlValue = 0; SetPsuGroupToIdle(master); LOG_INFO("Gun %d Stop Charging Completed", master + 1); } } void SlaveStopChargingProcess(unsigned char slave) { int time = 0; unsigned char master = 0; PsuGroupPartner PowerOffMember, GrabMember; if(ShmPsuGrouping->GroupCollection[slave].Role != _GROLE_SLAVE) { return; } master = ShmPsuGrouping->GroupCollection[slave].TargetGroup - 1; if(ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlaveChargingRequest && !ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.CheckSlaveReady) { ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.CheckSlaveReady = true; if(ShmPsuGrouping->GroupCollection[master].Role == _GROLE_REQUEST_TO_CHARGING) { LOG_INFO("Gun %d Need Wait Gun %d Grouping Completed", slave + 1, master + 1); GetClockTime(&_CheckSlaveReady_time[slave]); } else if(ShmPsuGrouping->GroupCollection[master].Role == _GROLE_MASTER && ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingConfirmed) { LOG_INFO("Gun %d Need Wait Gun %d Derating Completed", slave + 1, master + 1); GetClockTime(&_CheckSlaveReady_time[slave]); } else { ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlavePowerOffRequest = true; ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.WaitSlaveReady = true; LOG_INFO("Gun %d Need Power Off And Request To Charging", slave + 1); } } // wait until master derating completed if(ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.CheckSlaveReady && !ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.WaitSlaveReady) { time = GetTimeoutValue(_CheckSlaveReady_time[slave]) / uSEC_VAL; if(time >= WAIT_SLAVE_READY_TIME || (ShmPsuGrouping->GroupCollection[master].Role == _GROLE_MASTER && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingConfirmed)) { if(!(ShmPsuGrouping->GroupCollection[master].Role == _GROLE_MASTER && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.DeratingConfirmed)) { ShmPsuGrouping->GroupCollection[slave].GroupCtrl.RoleCtrl.SlaveCtrlValue = 0; LOG_INFO("Gun %d Wait Gun %d Timeout", slave + 1); } else { ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.WaitSlaveReady = true; LOG_INFO("Gun %d Is Ready", slave + 1); GetClockTime(&_CheckSlaveReady_time[slave]); } } } if(ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.WaitSlaveReady && !ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlavePowerOffRequest) { time = GetTimeoutValue(_CheckSlaveReady_time[slave]) / uSEC_VAL; if(time >= WAIT_SLAVE_DELAY) { ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlavePowerOffRequest = true; } } // self group, other slave or master set SlavePowerOffRequest flag to power off if(ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlavePowerOffRequest && !ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlavePowerOffConfirmed) { PsuGroupPowerOffCheck(slave, &PowerOffMember); if(PowerOffMember.Quantity == 1 && ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlaveChargingRequest) { // grab here if(PsuGroupGrabCheck(slave, &GrabMember)) { for(int i = 0; i < GrabMember.Quantity; i++) { PowerOffMember.Member[PowerOffMember.Quantity++] = GrabMember.Member[i]; } } } if(PowerOffMember.Quantity > 0) { unsigned char target = ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlaveChargingRequest ? (slave + 1) : 0; PrepareToPowerOff(target, &PowerOffMember); if(PowerOffMember.Quantity > 1 && ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlaveChargingRequest) { ShmPsuGrouping->GroupCollection[slave].PossibleMember.Quantity = PowerOffMember.Quantity - 1; memcpy(ShmPsuGrouping->GroupCollection[slave].PossibleMember.Member, &PowerOffMember.Member[1], PowerOffMember.Quantity - 1); } ShmPsuGrouping->GroupCollection[slave].GroupCtrl.bits.SlavePowerOffConfirmed = true; } else { ShmPsuGrouping->GroupCollection[slave].GroupCtrl.RoleCtrl.SlaveCtrlValue = 0; } } } void MasterExtendCapabilityProcess(unsigned char master) { int time = 0; if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.DeratingCtrlValue != 0 || ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.StopChargingRequest) { if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.MorePowerConfirmed) { SetExtendPrechargeStop(master); } ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.ExtendCapabilityCtrlValue = 0; return; } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.MorePowerRequest && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.MorePowerConfirmed) { GetClockTime(&_ExtendCapability_time[master]); memset(&ShmPsuGrouping->GroupCollection[master].PossibleMember, 0x00, sizeof(PsuGroupPartner)); FindPsuGroupPartner(master, MAX_GROUP_QUANTITY, &ShmPsuGrouping->GroupCollection[master].PossibleMember); if(ShmPsuGrouping->GroupCollection[master].PossibleMember.Quantity > 0) { PrepareToExtendCapability(master + 1, &ShmPsuGrouping->GroupCollection[master].PossibleMember); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.MorePowerConfirmed = true; } else { ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.ExtendCapabilityCtrlValue = 0; } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.MorePowerConfirmed && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendPrechargeDone) { unsigned short voltage = ShmPsuData->PsuGroup[master].GroupPresentOutputVoltage >= PRECHARGE_OFFSET_VOLTAGE ? ShmPsuData->PsuGroup[master].GroupPresentOutputVoltage - PRECHARGE_OFFSET_VOLTAGE : 0; for(int i = 0; i < ShmPsuGrouping->GroupCollection[master].PossibleMember.Quantity; i++) { unsigned char slave = ShmPsuGrouping->GroupCollection[master].PossibleMember.Member[i]; if(ShmPsuData->PsuGroup[slave].GroupPresentPsuQuantity > 0 && ShmPsuData->PsuGroup[slave].GroupAvailableCurrent > 0) { ShmPsuGrouping->GroupOutput[slave].GTargetVoltage = voltage; ShmPsuGrouping->GroupOutput[slave].GTargetCurrent = ZERO_CURRENT; } } if(!ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendPrecharge) { GetClockTime(&_ExtendCapability_time[master]); LOG_INFO("Gun %d Set ExtendPrecharge Voltage %d.%d V", master + 1, (voltage / 10), (voltage % 10)); } ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendPrecharge = true; } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendPrecharge && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendPrechargeDone) { bool ready = IsExtendPrechargeReady(master); time = GetTimeoutValue(_ExtendCapability_time[master]) / uSEC_VAL; if(ready || time >= WAIT_PRECHARGE_TIME) { LOG_INFO("Gun %d ExtendPrecharge %s", master + 1, ready ? "Ready" : "Timeout"); if(ready) { GetClockTime(&_ExtendCapability_time[master]); SetExtendPrechargeCompleted(master); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendPrechargeDone = true; } else { SetExtendPrechargeStop(master); ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.ExtendCapabilityCtrlValue = 0; } } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendPrechargeDone && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendRelayOn) { // after a little delay, set extend parallel relay on time = GetTimeoutValue(_ExtendCapability_time[master]) / uSEC_VAL; if(time >= WAIT_PARALLEL_RELAY_DELAY) { unsigned short original = ShmPsuGrouping->ParallelRelayConfig.CtrlValue; GetClockTime(&_ExtendCapability_time[master]); SetParallelRelayOnOff(master, YES, _GROLE_PRECHARGE_READY); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendRelayOn = true; LOG_INFO("Gun %d Set Extend Parallel Relay On %X -> %X", master + 1, original, ShmPsuGrouping->ParallelRelayConfig.CtrlValue); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendRelayOn && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendRelayConfirmed) { // wait until extend parallel relay on confirmed bool confirmed = IsParallelRelayConfirmed(master); time = GetTimeoutValue(_ExtendCapability_time[master]) / uSEC_VAL; //if(confirmed || time >= WAIT_RELAY_CONFIRMED_TIME) if(time >= WAIT_RELAY_CONFIRMED_TIME) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendRelayConfirmed = true; LOG_INFO("Gun %d Extend Parallel Relay Confirmed %s", master + 1, confirmed ? "OK" : "Timeout"); } } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendRelayConfirmed && !ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendCompleted) { AddExtendMember(master); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendCompleted = true; } if(ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ExtendCompleted) { ShmPsuGrouping->GroupCollection[master].GroupCtrl.RoleCtrl.ExtendCapabilityCtrlValue = 0; ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.OutputCurrentStable = false; GetClockTime(&_ReachCurrent_time[master]); UpdateGunAvailableCapability(master); ShmPsuGrouping->GroupCollection[master].GroupCtrl.bits.ReachMaxStageCurrent = false; GetClockTime(&_StageCurrent_time[master]); LOG_INFO("Gun %d Extend Capability Completed", master + 1); } } void ShowGunVoltageCurrent(unsigned char master) { unsigned short voltage = 0, current = 0, fireVoltage = 0; unsigned short diffVoltage = 0, diffCurrent = 0; if(ShmPsuGrouping->GroupCollection[master].Role == _GROLE_MASTER) { voltage = (int)(chargingInfo[master]->PresentChargingVoltage * 10); current = (int)(chargingInfo[master]->PresentChargingCurrent * 10); fireVoltage = chargingInfo[master]->FireChargingVoltage; diffVoltage = voltage >= evseOutVol[master] ? voltage - evseOutVol[master] : evseOutVol[master] - voltage; diffCurrent = current >= evseOutCur[master] ? current - evseOutCur[master] : evseOutCur[master] - current; if(diffVoltage >= 10 || diffCurrent >= 10) { evseOutputDelay[master] = SHOW_OUTPUT_DELAY; evseOutVol[master] = voltage; evseOutCur[master] = current; } if(evseOutputDelay[master] != 0) { LOG_INFO("Gun %d Need Voltage: %4d V, Current: %3d A, Output Voltage: %4d.%d V, Current: %3d.%d A, Fire Voltage: %4d V", master + 1, (int)chargingInfo[master]->EvBatterytargetVoltage, (int)chargingInfo[master]->EvBatterytargetCurrent, (voltage / 10), (voltage % 10), (current / 10), (current % 10), fireVoltage / 10); evseOutputDelay[master]--; } } } void PsuGroupControlProcess(void) { int time = 0; unsigned char role = 0; //unsigned short TargetVoltage = 0, TargetCurrent = 0; for(byte group = 0; group < GENERAL_GUN_QUANTITY; group++) { role = ShmPsuGrouping->GroupCollection[group].Role; switch(role) { case _GROLE_IDLE: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.IdleCtrlValue = 0; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.MasterCtrlValue = 0; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.StopChargingCtrlValue = 0; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.DeratingCtrlValue = 0; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.ExtendCapabilityCtrlValue = 0; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.SlaveCtrlValue = 0; PSU_LOG("===== PSU Group[%02X] ===== Idle", group); GetClockTime(&_PsuGroupRole_time[group]); ShmPsuGrouping->GroupOutput[group].GTargetVoltage = 0; ShmPsuGrouping->GroupOutput[group].GTargetCurrent = 0; PreGroupOutput[group].GTargetVoltage = 0; PreGroupOutput[group].GTargetCurrent = 0; SetPsuGroupOutput(group); } if(isStartOutputSwitch[group]) { SetPsuGroupPowerOnOff(group, PSU_POWER_OFF); } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.IdleCtrlValue != 0 && ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0) { CheckChargingRequest(group); } break; case _GROLE_MASTER: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.MasterCtrlValue = 0; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.StopChargingCtrlValue = 0; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.DeratingCtrlValue = 0; PSU_LOG("===== PSU Group[%02X] ===== Master", group); GetClockTime(&_PsuGroupRole_time[group]); OutputConfigStep[group] = _CURRENT_MODE_NONE; _preOutputConfigStep[group] = _CURRENT_MODE_NONE; MaxCurrentDemand[group] = 0; StageMaxCurrent[group] = 0; _GfdStep[group] = PREPARE_STEP_NONE; _VoltageResumeCnt[group] = 0; } if(!ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.AlreadyInChargingMode) { if(chargingInfo[group]->SystemStatus == S_CHARGING && chargingInfo[group]->RelayK1K2Status) { LOG_INFO("Gun %d Enter Charging Mode", group + 1); ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.AlreadyInChargingMode = true; ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ReachMaxCurrentDemand = false; ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ReachMaxStageCurrent = false; GetClockTime(&_MaxCurrent_time[group]); GetClockTime(&_StageCurrent_time[group]); GetClockTime(&_ChargingDelay_time[group]); } } if((chargingInfo[group]->SystemStatus <= S_IDLE) || (chargingInfo[group]->SystemStatus >= S_TERMINATING && chargingInfo[group]->SystemStatus <= S_FAULT)) { if(!ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.StopChargingRequest) { LOG_INFO("Gun %d SystemStatus(%d) Need Stop Charging", group + 1, chargingInfo[group]->SystemStatus); } ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.StopChargingRequest = true; } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.ExtendCapabilityCtrlValue == 0) { if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ExtendAvailable) { time = GetTimeoutValue(_ExtendCapability_time[group]) / uSEC_VAL; if(time >= EXTEND_CAPABILITY_DELAY) { int available = GetPsuGroupAvailable(group); if(available > 0) { ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.MorePowerRequest = true; LOG_INFO("Gun %d Start Extend Capability", group + 1); } ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ExtendAvailable = false; } } } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.ExtendCapabilityCtrlValue != 0) { MasterExtendCapabilityProcess(group); } // need derating if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.DeratingCtrlValue != 0 && !ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.StopChargingRequest) { PsuGroupDeratingProcess(group); } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.StopChargingRequest) { ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.DeratingCtrlValue = 0; ShmPsuGrouping->GroupOutput[group].GTargetVoltage = 0; ShmPsuGrouping->GroupOutput[group].GTargetCurrent = 0; MasterStopChargingProcess(group); } else { UpdateGunLoading(group); UpdatePsuGroupOutputConfig(group); UpdateMasterPsuGroupLoading(group); PsuGroupOutputConfigCheck(group); } ShowGunVoltageCurrent(group); SetPsuGroupOutput(group); break; case _GROLE_SLAVE: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Slave", group); GetClockTime(&_PsuGroupRole_time[group]); } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.SlaveCtrlValue != 0) { SlaveStopChargingProcess(group); } else { SetPsuGroupOutput(group); } break; case _GROLE_PREPARE_SWITCH_OFF: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Prepare Off", group); GetClockTime(&_PsuGroupRole_time[group]); } time = GetTimeoutValue(_PsuGroupRole_time[group]) / uSEC_VAL; if(time >= MAX_PREPARE_SWITCH_OFF_TIME) { // timeout shall not happen SetPsuGroupRole(group, _GROLE_SLAVE_POWER_OFF); } else { SetPsuGroupOutput(group); } break; case _GROLE_SLAVE_POWER_OFF: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Slave Power Off", group); GetClockTime(&_PsuGroupRole_time[group]); } time = GetTimeoutValue(_PsuGroupRole_time[group]) / uSEC_VAL; if(time >= MIN_POWER_OFF_TIME && ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent <= MAX_PSU_POWER_OFF_CURRENT) { SetPsuGroupRole(group, _GROLE_SWITCH_OFF_OK); } else { ShmPsuGrouping->GroupOutput[group].GTargetVoltage = 0; ShmPsuGrouping->GroupOutput[group].GTargetCurrent = 0; SetPsuGroupOutput(group); } break; case _GROLE_SWITCH_OFF_OK: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Switch Off OK, %d.%d V, %d.%d A", group, (ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage / 10), (ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage % 10), (ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent / 10), (ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent % 10)); GetClockTime(&_PsuGroupRole_time[group]); } break; case _GROLE_WAIT_IDLE: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Wait Idle, %d.%d V, %d.%d A", group, (ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage / 10), (ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage % 10), (ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent / 10), (ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent % 10)); GetClockTime(&_PsuGroupRole_time[group]); } break; case _GROLE_WAIT_SLAVE: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Wait Slave", group); GetClockTime(&_PsuGroupRole_time[group]); } time = GetTimeoutValue(_PsuGroupRole_time[group]) / uSEC_VAL; if(ShmPsuGrouping->GroupCollection[group].ReservedTarget != 0) { if(time >= WAIT_SLAVE_TO_CHARGING && (ShmPsuGrouping->GroupCollection[group].ReservedTarget - 1) == group) { SetPsuGroupRole(group, _GROLE_REQUEST_TO_CHARGING); ShmPsuGrouping->GroupCollection[group].GroupCtrl.bits.ChargingRequest = true; ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.SlaveCtrlValue = 0; break; } } if(time >= WAIT_SLAVE_TIMEOUT) { PSU_LOG("Group[%02X] Wait Slave Timeout", group); SetPsuGroupToIdle(group); } break; case _GROLE_PREPARE_ATTACH_ON: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Prepare Attach On", group); GetClockTime(&_PsuGroupRole_time[group]); } SetPsuGroupOutput(group); break; case _GROLE_PRECHARGE_READY: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Precharge %d.%d V Ready", group, (ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage / 10), (ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage % 10)); GetClockTime(&_PsuGroupRole_time[group]); } break; case _GROLE_EXTEND_STOP: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Extend Stop", group); GetClockTime(&_PsuGroupRole_time[group]); } time = GetTimeoutValue(_PsuGroupRole_time[group]) / uSEC_VAL; if(time >= MIN_POWER_OFF_TIME || (ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent <= MAX_PSU_POWER_OFF_CURRENT && ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage <= MAX_PSU_POWER_OFF_VOLTAGE)) { SetPsuGroupToIdle(group); } else { ShmPsuGrouping->GroupOutput[group].GTargetVoltage = 0; ShmPsuGrouping->GroupOutput[group].GTargetCurrent = 0; SetPsuGroupOutput(group); } break; case _GROLE_REQUEST_TO_CHARGING: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Request To Charging", group); GetClockTime(&_PsuGroupRole_time[group]); } if(ShmPsuGrouping->GroupCollection[group].GroupCtrl.RoleCtrl.IdleCtrlValue != 0) { ChargingRequestProcess(group); } break; case _GROLE_TERMINATE: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Terminate", group); GetClockTime(&_PsuGroupRole_time[group]); } break; case _GROLE_WAIT_TERMINATED: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Wait Terminated", group); GetClockTime(&_PsuGroupRole_time[group]); } break; case _GROLE_NONE: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== None", group); GetClockTime(&_PsuGroupRole_time[group]); } break; default: if(ShmPsuGrouping->GroupCollection[group].PreRole != role) { ShmPsuGrouping->GroupCollection[group].PreRole = role; PSU_LOG("===== PSU Group[%02X] ===== Unknown Role = %d", group, role); GetClockTime(&_PsuGroupRole_time[group]); } SetPsuGroupRole(group, _GROLE_NONE); break; } } } // only for test & debug purpose void PsuGroupSimulation(void) { unsigned char master = 0; int _groupCurrent = 0, _groupPower = 0; for(int i = 0; i < ShmPsuData->GroupCount; i++) { master = ShmChargerInfo->PsuGrouping.GroupCollection[i].TargetGroup; // calculate output group capability if(master == 0) { chargingInfo[i]->AvailableChargingCurrent = ShmPsuData->PsuGroup[i].GroupAvailableCurrent; chargingInfo[i]->AvailableChargingPower = ShmPsuData->PsuGroup[i].GroupAvailablePower; } else { _groupCurrent = ShmPsuData->PsuGroup[master - 1].GroupAvailableCurrent; _groupPower = ShmPsuData->PsuGroup[master - 1].GroupAvailablePower; for(byte j = 0; j < ShmPsuGrouping->GroupCollection[master - 1].Partner.Quantity; j++) { byte slave = ShmPsuGrouping->GroupCollection[master - 1].Partner.Member[j]; if(ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_SLAVE || ShmPsuGrouping->GroupCollection[slave].Role == _GROLE_PREPARE_SWITCH_OFF) { _groupCurrent += ShmPsuData->PsuGroup[slave].GroupAvailableCurrent; _groupPower += ShmPsuData->PsuGroup[slave].GroupAvailablePower; } } if(ShmPsuGrouping->GroupCollection[master - 1].GroupCtrl.bits.DeratingConfirmed) { chargingInfo[master - 1]->AvailableChargingCurrent = ShmPsuGrouping->GroupCollection[master - 1].ReAssignAvailableCurrent; } else { chargingInfo[master - 1]->AvailableChargingCurrent = _groupCurrent; } chargingInfo[master - 1]->AvailableChargingPower = _groupPower; if((master - 1) != i) { chargingInfo[i]->AvailableChargingCurrent = 0;; chargingInfo[i]->AvailableChargingPower = 0; chargingInfo[i]->RealRatingPower = 0; chargingInfo[i]->MaximumChargingVoltage = 0; } } } } void PsuGroupingInitial(void) { for(int i = 0; i < ShmPsuData->GroupCount; i++) { ShmPsuGrouping->GroupCollection[i].Role = 0; ShmPsuGrouping->GroupCollection[i].PreRole = 0xFF; memset(&ShmPsuGrouping->GroupCollection[i].Partner, 0x00, sizeof(PsuGroupPartner)); memset(&ShmPsuGrouping->GroupCollection[i].PossibleMember, 0x00, sizeof(PsuGroupPartner)); ShmPsuGrouping->GroupCollection[i].TargetGroup = 0; ShmPsuGrouping->GroupCollection[i].ReservedTarget = 0; memset(&ShmPsuGrouping->GroupCollection[i].GroupCtrl, 0x00, sizeof(PsuGroupControl)); ShmPsuGrouping->GroupCollection[i].ReAssignAvailableCurrent = 0; ShmPsuGrouping->GroupCollection[i].ParallelCheck = 0; ShmPsuGrouping->GroupCollection[i].GunLoading = 0; memset(&ShmPsuGrouping->GroupOutput[i], 0x00, sizeof(GroupOutputConfigInfo)); } } int main(void) { byte _TotalModuleCount = 0; byte _PrePsuWorkStep = 0; byte isInitialComp = NO; if(InitShareMemory() == FAIL) { #ifdef SystemLogMessage LOG_ERROR("InitShareMemory NG"); #endif if(ShmStatusCodeData != NULL) { ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory = 1; } sleep(5); return 0; } LOG_INFO("InitShareMemory OK"); signal(SIGCHLD, SIG_IGN); // register callback function RefreshStatus(&GetStatusCallback); RefreshModuleCount(&GetModuleCountCallback); RefreshAvailableCap(&GetAvailableCapCallback); RefreshFwVersion(&GetFwCallback); RefreshInputVol(&GetInputVoltageCallback); RefreshGetOutput(&GetPresentOutputCallback); RefreshMisInfo(&GetMisCallback); RefreshIavailable(&GetIavailableCallback); RefreshGetOutputF(&GetPresentOutputFCallback); // GetPresentOutputCallback & GetStatusCallback AutoMode_RefreshOutputAndTemp(&GetOutputAndTempCallback); // GetStatusCallback AutoMode_RefreshModuleStatus(&GetModuleStatusCallback); // GetInputVoltageCallback AutoMode_RefreshModuleInput(&GetModuleInputCallback); sleep(2); _gunCount = GENERAL_GUN_QUANTITY; _maxGroupCount = GENERAL_GUN_QUANTITY; _PrePsuWorkStep = _INIT_PSU_STATUS; ShmPsuData->Work_Step = INITIAL_START; isInitialComp = NO; LOG_INFO("PSU Work Status = %d", ShmPsuData->Work_Step); // initial object Initialization(); InitialPsuData(); libInitialize = InitialCommunication(); //main loop while (libInitialize) { // 斷電狀態 if (ShmChargerInfo->Control.RelayCtrl.bits.AcContactor == NO || ShmChargerInfo->Control.RelayCtrl.bits.AcContactorOffByPsu == YES || ShmChargerInfo->Control.RelayCtrl.bits.AcContactorOffByEmergency == YES || ShmChargerInfo->Control.PsuCtrl.bits.NeedSelfTest == YES || ShmChargerInfo->Control.PsuCtrl.bits.Paused == YES) { //一但 AC Off PSU 斷電全部的 PSU Group ID 會全部清 0 if (!isInitialComp) { _PrePsuWorkStep = _INIT_PSU_STATUS; ShmPsuData->Work_Step = INITIAL_START; psuCmdSeq = _PSU_CMD_STATUS; _TotalModuleCount = 0; sleep(2); InitialPsuData(); isInitialComp = YES; } if(ShmChargerInfo->Control.PsuCtrl.bits.NeedSelfTest) { ShmChargerInfo->Control.PsuCtrl.bits.NeedSelfTest = NO; ShmChargerInfo->Control.PsuCtrl.bits.SelfTestOK = NO; LOG_INFO("Psu Need Execute Self Test!"); } sleep(1); continue; } else { isInitialComp = NO; } // only for test & debug purpose if(ShmChargerInfo->Control.TestCtrl.bits.ChargingSimulation) { ShmPsuData->Work_Step = _WORK_CHARGING; ShmPsuData->PsuGroup[0].GroupPresentPsuQuantity = 3; ShmPsuData->PsuGroup[1].GroupPresentPsuQuantity = 3; ShmPsuData->PsuGroup[2].GroupPresentPsuQuantity = 3; ShmPsuData->PsuGroup[3].GroupPresentPsuQuantity = 3; ShmPsuData->PsuGroup[0].GroupAvailableCurrent = ShmPsuData->PsuGroup[0].GroupPresentPsuQuantity * 1000; ShmPsuData->PsuGroup[1].GroupAvailableCurrent = ShmPsuData->PsuGroup[1].GroupPresentPsuQuantity * 1000; ShmPsuData->PsuGroup[2].GroupAvailableCurrent = ShmPsuData->PsuGroup[2].GroupPresentPsuQuantity * 1000; ShmPsuData->PsuGroup[3].GroupAvailableCurrent = ShmPsuData->PsuGroup[3].GroupPresentPsuQuantity * 1000; ShmPsuData->PsuGroup[0].GroupAvailablePower = ShmPsuData->PsuGroup[0].GroupPresentPsuQuantity * 300; ShmPsuData->PsuGroup[1].GroupAvailablePower = ShmPsuData->PsuGroup[1].GroupPresentPsuQuantity * 300; ShmPsuData->PsuGroup[2].GroupAvailablePower = ShmPsuData->PsuGroup[2].GroupPresentPsuQuantity * 300; ShmPsuData->PsuGroup[3].GroupAvailablePower = ShmPsuData->PsuGroup[3].GroupPresentPsuQuantity * 300; PsuGroupSimulation(); } // 自檢失敗 if (ShmPsuData->Work_Step == _NO_WORKING) { LOG_INFO("== PSU == self test fail."); sleep(5); } if((GetTimeoutValue(_PsuReceiveRecoveryCheck_time) / uSEC_VAL) >= PSU_TASK_CHECK_TIME) { PsuReceiveRecoveryCheck(); GetClockTime(&_PsuReceiveRecoveryCheck_time); } switch(ShmPsuData->Work_Step) { case INITIAL_START: { if(_PrePsuWorkStep != ShmPsuData->Work_Step) { _PrePsuWorkStep = ShmPsuData->Work_Step; LOG_INFO("== PSU == INITIAL_START"); } sleep(5); SwitchPower(SYSTEM_CMD, PSU_POWER_OFF); SetWalkInConfig(SYSTEM_CMD, NO, 0); for (byte index = 0; index < ShmPsuData->GroupCount; index++) { isStartOutputSwitch[index] = false; } ShmPsuData->Work_Step = GET_PSU_COUNT; } break; case GET_PSU_COUNT: { if(_PrePsuWorkStep != ShmPsuData->Work_Step) { _PrePsuWorkStep = ShmPsuData->Work_Step; LOG_INFO("== PSU == GET_PSU_COUNT"); GetClockTime(&_PsuWorkStep_time); GetClockTime(&_cmdSubPriority_time); } int time = GetTimeoutValue(_PsuWorkStep_time) / uSEC_VAL; int interval = GetTimeoutValue(_cmdSubPriority_time) / uSEC_VAL; if(interval > GET_PSU_COUNT_INTERVAL) { _TotalModuleCount = 0; for (byte index = 0; index < ShmPsuData->GroupCount; index++) { // 總和各群模組數量 _TotalModuleCount += ShmPsuData->PsuGroup[index].GroupPresentPsuQuantity; } LOG_INFO("== PSU == indexCount = %d, moduleCount = %d, sysCount = %d", ShmPsuData->GroupCount, _TotalModuleCount, ShmPsuData->SystemPresentPsuQuantity); // 判斷系統數量與各群數量一致 if(_TotalModuleCount == ShmPsuData->SystemPresentPsuQuantity && _TotalModuleCount > 0 && time > GET_PSU_COUNT_TIME) { LOG_INFO("Psu Count = %d", _TotalModuleCount); ShmPsuData->Work_Step = Get_PSU_LOCATION; } else { // 發送取得目前全部模組數量 GetModuleCount(SYSTEM_CMD); for (byte index = 0; index < ShmPsuData->GroupCount; index++) { // 取各群模組數量 GetModuleCount(index); } } GetClockTime(&_cmdSubPriority_time); } } break; case Get_PSU_LOCATION: { if(_PrePsuWorkStep != ShmPsuData->Work_Step) { _PrePsuWorkStep = ShmPsuData->Work_Step; LOG_INFO("== PSU == Get_PSU_LOCATION"); // clean psu location info memset(ShmPsuPosition, 0x00, sizeof(PsuPositionInfoData)); GetClockTime(&_cmdSubPriority_time); } int interval = GetTimeoutValue(_cmdSubPriority_time) / mSEC_VAL; if(interval > GET_PSU_LOCATION_INTERVAL) { for(byte index = 0; index < ShmPsuData->GroupCount; index++) { if(ShmPsuData->PsuGroup[index].GroupPresentPsuQuantity > 0) { // 取得狀態 : 支援模塊不須按照順序功能 GetStatus(index); } } } if(ShmPsuPosition->PsuLocationInit) { #if 1 for(int i = 0; i < _maxGroupCount; i++) { if(ShmPsuPosition->GroupLocationInfo[i].GroupPsuQuantity > 0) { ShowGroupMember(i); } } #endif //ShmPsuData->Work_Step = Get_PSU_VERSION; ShmPsuData->Work_Step = PSU_COUNT_CONFIRM; } if(ShmPsuPosition->ReInitPsuLocation) { LOG_INFO("Retry Psu Location Initialization"); ShmPsuData->Work_Step = GET_PSU_COUNT; } } break; case PSU_COUNT_CONFIRM: { if(_PrePsuWorkStep != ShmPsuData->Work_Step) { _PrePsuWorkStep = ShmPsuData->Work_Step; LOG_INFO("== PSU == PSU_COUNT_CONFIRM"); GetClockTime(&_PsuWorkStep_time); GetClockTime(&_cmdSubPriority_time); } int time = GetTimeoutValue(_PsuWorkStep_time) / uSEC_VAL; int interval = GetTimeoutValue(_cmdSubPriority_time) / mSEC_VAL; if(interval > PSU_COUNT_CONFIRM_INTERVAL) { _TotalModuleCount = 0; for (byte index = 0; index < _maxGroupCount; index++) { // 總和各群模組數量 _TotalModuleCount += ShmPsuData->PsuGroup[index].GroupPresentPsuQuantity; } if(time > PSU_COUNT_CONFIRM_TIME) { if(_TotalModuleCount == ShmPsuData->SystemPresentPsuQuantity && _TotalModuleCount > 0) { ShmPsuData->Work_Step = Get_PSU_VERSION; } else { LOG_INFO("Total PSU = %d, System PSU Quantity = %d", _TotalModuleCount, ShmPsuData->SystemPresentPsuQuantity); LOG_INFO("PSU Quantity Confirm Fail"); ShmPsuData->Work_Step = GET_PSU_COUNT; } break; } // 發送取得目前全部模組數量 GetModuleCount(SYSTEM_CMD); for (byte index = 0; index < ShmPsuData->GroupCount; index++) { // 取各群模組數量 GetModuleCount(index); } GetClockTime(&_cmdSubPriority_time); } } break; case Get_PSU_VERSION: { if(_PrePsuWorkStep != ShmPsuData->Work_Step) { _PrePsuWorkStep = ShmPsuData->Work_Step; LOG_INFO("== PSU == Get_PSU_VERSION"); GetClockTime(&_cmdSubPriority_time); // clean version info memset(ShmPsuData->PsuVersion, 0x00, sizeof(ShmPsuData->PsuVersion)); } int interval = GetTimeoutValue(_cmdSubPriority_time) / mSEC_VAL; if (interval > GET_PSU_VERSION_INTERVAL) { bool isGetVersion = true; for(byte psu = 0; psu < ShmPsuData->SystemPresentPsuQuantity; psu++) { if (strcmp((char *)ShmPsuData->PsuVersion[psu].FwPrimaryVersion, "") == EQUAL) { isGetVersion = false; break; } } if(isGetVersion) { #if 1 for(int i = 0; i < _maxGroupCount; i++) { if(ShmPsuPosition->GroupLocationInfo[i].GroupPsuQuantity > 0) { ShowPsuVersion(i); } } #endif ShmPsuData->Work_Step = GET_SYS_CAP; } else { for(byte group = 0; group < _maxGroupCount; group++) { if(ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0) { // 取版號 GetModuleVer(group); } } } GetClockTime(&_cmdSubPriority_time); } } break; case GET_SYS_CAP: { if(_PrePsuWorkStep != ShmPsuData->Work_Step) { _PrePsuWorkStep = ShmPsuData->Work_Step; LOG_INFO("== PSU == GET_SYS_CAP"); GetClockTime(&_PsuWorkStep_time); GetClockTime(&_cmdSubPriority_time); } int time = GetTimeoutValue(_PsuWorkStep_time) / uSEC_VAL; int interval = GetTimeoutValue(_cmdSubPriority_time) / mSEC_VAL; if (interval > GET_PSU_CAP_INTERVAL) { if(time > GET_PSU_CAP_TIME && ShmPsuData->SystemAvailablePower > 0 && ShmPsuData->SystemAvailableCurrent > 0) { #if 1 for(int i = 0; i < _maxGroupCount; i++) { if(ShmPsuPosition->GroupLocationInfo[i].GroupPsuQuantity > 0) { ShowGroupAvailableCurrentPower(i); } } #endif // 判斷系統輸出額定功率與電流 LOG_INFO("SystemAvailableCurrent = %d, SystemAvailablePower = %d", ShmPsuData->SystemAvailableCurrent, ShmPsuData->SystemAvailablePower); ShmPsuData->Work_Step = BOOTING_COMPLETE; } else { for(byte index = 0; index < ShmPsuData->GroupCount; index++) { if(ShmPsuData->PsuGroup[index].GroupPresentPsuQuantity > 0) { // 取系統總輸出能力 GetModuleCap(index); } } } GetClockTime(&_cmdSubPriority_time); } } break; case BOOTING_COMPLETE: { if(_PrePsuWorkStep != ShmPsuData->Work_Step) { _PrePsuWorkStep = ShmPsuData->Work_Step; LOG_INFO("== PSU == BOOTING_COMPLETE"); } if(ShmChargerInfo->Control.PsuCtrl.bits.SelfTestOK) { ShmPsuData->Work_Step = _WORK_CHARGING; } else { LOG_INFO("== PSU == Self Test OK!"); } ShmChargerInfo->Control.PsuCtrl.bits.SelfTestOK = true; sleep(1); } break; case _WORK_CHARGING: { if(_PrePsuWorkStep != ShmPsuData->Work_Step) { _PrePsuWorkStep = ShmPsuData->Work_Step; LOG_INFO("== PSU == _WORK_CHARGING"); GetClockTime(&_PsuWorkStep_time); GetClockTime(&_cmdSubPriority_time); PsuGroupingInitial(); } int time = GetTimeoutValue(_cmdSubPriority_time) / 1000; // 低 Priority 的指令 if (time > 1000) { //PreCheckSmartChargingStep(); startModuleFlag = true; for(byte group = 0; group < GENERAL_GUN_QUANTITY; group++) { if(ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0) { GetStatus(group); } } for(byte group = 0; group < GENERAL_GUN_QUANTITY; group++) { if(ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity > 0) { GetModuleInput(group); } } GetClockTime(&_cmdSubPriority_time); } PsuGroupRoutineQuery(); PsuGroupControlProcess(); break; } case _TEST_MODE: { // 在測試模式中,保持與模塊的通訊 int time = GetTimeoutValue(_cmdSubPriority_time) / 1000; if (time > 1500) { for (byte index = 0; index < ShmPsuData->GroupCount; index++) { // 取系統總輸出能力 GetModuleCap(index); Await(); // 取各群輸出電壓電流 (float) GetModuleOutputF(index); Await(); } GetClockTime(&_cmdSubPriority_time); } byte _switch = 0x00; if ((chargingInfo[0]->EvBatterytargetVoltage * 10) > 0 && (chargingInfo[0]->EvBatterytargetCurrent * 10) > 0) _switch = 0x01; for (byte _groupCount_1 = 0; _groupCount_1 < conn_1_count; _groupCount_1++) { SetDirModulePresentOutput(connector_1[_groupCount_1], (chargingInfo[0]->EvBatterytargetVoltage * 10), (chargingInfo[0]->EvBatterytargetCurrent * 10), _switch, _switch); Await(); } for (byte _groupCount_2 = 0; _groupCount_2 < conn_2_count; _groupCount_2++) { SetDirModulePresentOutput(connector_2[_groupCount_2], (chargingInfo[0]->EvBatterytargetVoltage * 10), (chargingInfo[0]->EvBatterytargetCurrent * 10), _switch, _switch); Await(); } } break; case _INIT_PSU_STATUS: if(_PrePsuWorkStep != ShmPsuData->Work_Step) { _PrePsuWorkStep = ShmPsuData->Work_Step; LOG_INFO("== PSU == _INIT_PSU_STATUS"); GetClockTime(&_cmdSubPriority_time); } break; default: break; } usleep(20000); } return FAIL; }