/* * Module_SmartBox.c * * Created on: 2022年5月3日 * Author: 7564 */ #include "Module_SmartBox.h" struct SysConfigAndInfo *ShmSysConfigAndInfo; struct StatusCodeData *ShmStatusCodeData; struct PsuData *ShmPsuData; struct DcCommonInformation *ShmDcCommonData; struct SmartBoxData *ShmSmartBoxData; struct ChargingInfoData *chargingInfo[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY]; byte ConnectorUsingSeq[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY][4] = {{0, 2, 3, 1}, {1, 3, 2, 0}}; struct timespec _log_time; #define DEBUG_INFO(format, args...) StoreLogMsg("[%s:%d][%s][Info] "format, __FILE__, __LINE__, __FUNCTION__, ##args) #define DEBUG_WARN(format, args...) StoreLogMsg("[%s:%d][%s][Warn] "format, __FILE__, __LINE__, __FUNCTION__, ##args) #define DEBUG_ERROR(format, args...) StoreLogMsg("[%s:%d][%s][Error] "format, __FILE__, __LINE__, __FUNCTION__, ##args) int StoreLogMsg(const char *fmt, ...) { char Buf[4096+256]; char buffer[4096]; va_list args; struct timeb SeqEndTime; struct tm *tm; va_start(args, fmt); int rc = vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); memset(Buf,0,sizeof(Buf)); ftime(&SeqEndTime); SeqEndTime.time = time(NULL); tm=localtime(&SeqEndTime.time); if (ShmSysConfigAndInfo->SysConfig.SwitchDebugFlag == YES) { sprintf(Buf,"%02d:%02d:%02d:%03d - %s", tm->tm_hour,tm->tm_min,tm->tm_sec,SeqEndTime.millitm, buffer); printf("%s \n", Buf); } else { sprintf(Buf,"echo \"%04d-%02d-%02d %02d:%02d:%02d:%03d - %s\" >> /Storage/SystemLog/[%04d.%02d]SystemLog_%s_Log", tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,SeqEndTime.millitm, buffer, tm->tm_year+1900,tm->tm_mon+1, ShmSysConfigAndInfo->SysConfig.SerialNumber); system(Buf); } return rc; } void PRINTF_FUNC(char *string, ...) { va_list args; char buffer[4096]; va_start(args, string); vsnprintf(buffer, sizeof(buffer), string, args); va_end(args); DEBUG_INFO("%s ", buffer); } //========================================== // Init all share memory //========================================== int InitShareMemory() { int result = PASS; int MeterSMId; //creat ShmSysConfigAndInfo if ((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo), 0777)) < 0) { #ifdef SystemLogMessage DEBUG_ERROR("shmget ShmSysConfigAndInfoKey NG %d \n"); #endif result = FAIL; } else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1) { #ifdef SystemLogMessage DEBUG_ERROR("shmat ShmSysConfigAndInfo NG \n"); #endif result = FAIL; } //creat ShmStatusCodeData if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData), 0777)) < 0) { #ifdef SystemLogMessage DEBUG_ERROR("shmget ShmStatusCodeKey NG \n"); #endif result = FAIL; } else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) -1) { #ifdef SystemLogMessage DEBUG_ERROR("shmat ShmStatusCodeData NG \n"); #endif result = FAIL; } //creat ShmPsuData if ((MeterSMId = shmget(ShmPsuKey, sizeof(struct PsuData), 0777)) < 0) { #ifdef SystemLogMessage DEBUG_ERROR("shmget ShmPsuKey NG \n"); #endif result = FAIL; } else if ((ShmPsuData = shmat(MeterSMId, NULL, 0)) == (void *) -1) { #ifdef SystemLogMessage DEBUG_ERROR("shmat ShmPsuData NG \n"); #endif result = FAIL; } if ((MeterSMId = shmget(ShmCommonKey, sizeof(struct DcCommonInformation), IPC_CREAT | 0777)) < 0) { #ifdef SystemLogMessage DEBUG_ERROR("shmget ShmCommonKey NG \n"); #endif result = FAIL; } else if ((ShmDcCommonData = shmat(MeterSMId, NULL, 0)) == (void *) -1) { #ifdef SystemLogMessage DEBUG_ERROR("shmat ShmDcCommonData NG \n"); #endif result = FAIL; } if ((MeterSMId = shmget ( ShmSmartBoxKey, sizeof(struct SmartBoxData), IPC_CREAT | 0777 )) < 0) { #ifdef SystemLogMessage DEBUG_ERROR("shmat ShmSmartBoxKey NG \n"); #endif return FAIL; } else if ((ShmSmartBoxData = shmat ( MeterSMId, NULL, 0 )) == (void *) - 1) { #ifdef SystemLogMessage DEBUG_ERROR("shmat ShmSmartBoxData NG \n"); #endif return FAIL; } return result; } //========================================== // Public Function //========================================== bool FindChargingInfoData(byte target, struct ChargingInfoData **chargingData) { for (byte index = 0; index < CHAdeMO_QUANTITY; index++) { if (ShmSysConfigAndInfo->SysInfo.ChademoChargingData[index].Index == target) { chargingData[target] = &ShmSysConfigAndInfo->SysInfo.ChademoChargingData[index]; return true; } } for (byte index = 0; index < CCS_QUANTITY; index++) { if (ShmSysConfigAndInfo->SysInfo.CcsChargingData[index].Index == target) { chargingData[target] = &ShmSysConfigAndInfo->SysInfo.CcsChargingData[index]; return true; } } for (byte index = 0; index < GB_QUANTITY; index++) { if (ShmSysConfigAndInfo->SysInfo.GbChargingData[index].Index == target) { chargingData[target] = &ShmSysConfigAndInfo->SysInfo.GbChargingData[index]; return true; } } return false; } void Initialization() { bool isPass = false; while(!isPass) { isPass = true; for (byte _index = 0; _index < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; _index++) { if (!FindChargingInfoData(_index, &chargingInfo[0])) { DEBUG_ERROR("SmartBox (main) : FindChargingInfoData false \n"); isPass = false; break; } } sleep(1); } for (byte _index = 0; _index < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; _index++) { ShmSmartBoxData->Dynamic4Fetch[_index].ShareGroup = NONE_GROUP_CAN_SELECTED; ShmSmartBoxData->Dynamic4Fetch[_index].TargetRelay = NONE_RELAY_SELECTED; ShmSmartBoxData->Dynamic4Fetch[_index].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_NONE; ShmSmartBoxData->Dynamic4Release[_index].ReleaseGroup = NONE_GROUP_CAN_SELECTED; ShmSmartBoxData->Dynamic4Release[_index].TargetRelay = NONE_RELAY_SELECTED; ShmSmartBoxData->Dynamic4Release[_index].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_NONE; ShmSmartBoxData->ConnectorUsingGroupCount[_index] = 0; } } void PrintfLog() { if (ShmPsuData->Work_Step != _WORK_CHARGING) { PRINTF_FUNC ( "****************************************************************** \n" ); PRINTF_FUNC ( "SystemPresentPsuQuantity = %d \n", ShmPsuData->SystemPresentPsuQuantity); PRINTF_FUNC ( "SystemAvailableCurrent = %d (A) \n", ShmPsuData->SystemAvailableCurrent / 10); PRINTF_FUNC ( "SystemAvailablePower = %d (kw) \n", ShmPsuData->SystemAvailablePower / 10); PRINTF_FUNC ( "GroupCount = %d \n", ShmPsuData->GroupCount ); for (int _count = 0; _count < ARRAY_SIZE(ConnectorUsingSeq[0]); _count ++) { PRINTF_FUNC ( "----------------------------------------------------------------- \n" ); PRINTF_FUNC ( "Group Index = %d, UsingTarget for Gun-%d \n", _count, ShmPsuData->PsuGroup[_count].UsingTarget); PRINTF_FUNC ( "GroupPresentPsuQuantity = %d \n", ShmPsuData->PsuGroup[_count].GroupPresentPsuQuantity ); PRINTF_FUNC ( "GroupTargetOutputVoltage = %d (V) \n", ShmPsuData->PsuGroup[_count].GroupTargetOutputVoltage / 10); PRINTF_FUNC ( "GroupTargetOutputCurrent = %d (A) \n", ShmPsuData->PsuGroup[_count].GroupTargetOutputCurrent / 10); PRINTF_FUNC ( "GroupAvailableCurrent = %d (A) \n", ShmPsuData->PsuGroup[_count].GroupAvailableCurrent / 10); PRINTF_FUNC ( "GroupAvailablePower = %d (kw) \n", ShmPsuData->PsuGroup[_count].GroupAvailablePower / 10); PRINTF_FUNC ( "GroupMaxVoltage = %d (V) \n", ShmPsuData->PsuGroup[_count].GroupMaxVoltage / 10); PRINTF_FUNC ( "GroupPresentOutputVoltage = %.1f (V) \n", (double)ShmPsuData->PsuGroup[_count].GroupPresentOutputVoltage / 10); PRINTF_FUNC ( "GroupPresentOutputCurrent = %.1f (A) \n", (double)ShmPsuData->PsuGroup[_count].GroupPresentOutputCurrent / 10); PRINTF_FUNC ( "GroupPresentOutputPower = %d \n", ShmPsuData->PsuGroup[_count].GroupPresentOutputPower ); PRINTF_FUNC ( "TotalRatingPower = %f \n", (double)ShmPsuData->PsuGroup[_count].TotalRatingPower); PRINTF_FUNC ( "TotalIAvailableCurrent = %f \n", (double)ShmPsuData->PsuGroup[_count].TotalIAvailableCurrent / 10); } } else { for (byte conn = 0; conn < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; conn++) { byte totalUsingGpCount = 0; byte totalQuantity = 0; unsigned short _targetVol = 0 , _targetCur = 0; unsigned short _avaCur = 0 , _avaPow = 0; unsigned short _outputVol = 0 , _outputCur = 0; unsigned short _ratingPow = 0; for (byte gp = 0; gp < ShmSmartBoxData->ConnectorUsingGroupCount[conn]; gp++) { if (ShmPsuData->PsuGroup[ConnectorUsingSeq[conn][gp]].UsingTarget == GUN_LEFT) { totalUsingGpCount++; byte tarGp = ConnectorUsingSeq[conn][gp]; totalQuantity += ShmPsuData->PsuGroup[tarGp].GroupPresentPsuQuantity; _targetVol = ShmPsuData->PsuGroup[tarGp].GroupTargetOutputVoltage / 10; _targetCur += ShmPsuData->PsuGroup[tarGp].GroupTargetOutputCurrent / 10; _avaCur += ShmPsuData->PsuGroup[tarGp].GroupAvailableCurrent / 10; _avaPow += ShmPsuData->PsuGroup[tarGp].GroupAvailablePower / 10; _outputVol = (double)ShmPsuData->PsuGroup[tarGp].GroupPresentOutputVoltage / 10; _outputCur = (double)ShmPsuData->PsuGroup[tarGp].GroupPresentOutputCurrent / 10; _ratingPow += ShmPsuData->PsuGroup[tarGp].TotalRatingPower; } } if (totalUsingGpCount > 0) { PRINTF_FUNC ( "----------------------------------------------------------------- \n" ); PRINTF_FUNC ( "Group Index = %d, totalUsingGpCount = %d \n", conn, totalUsingGpCount); PRINTF_FUNC ( "totalQuantity = %d \n", totalQuantity); PRINTF_FUNC ( "_targetVol = %d (V), _targetCur = %d (A) \n", _targetVol, _targetCur); PRINTF_FUNC ( "_avaCur = %d (A), _avaPow = %d (kw) \n", _avaCur, _avaPow); PRINTF_FUNC ( "_outputVol = %d (V), _outputCur = %d (A) \n", _outputVol, _outputCur); PRINTF_FUNC ( "_ratingPow = %d \n", _ratingPow); } for (byte gp = 0; gp < ShmSmartBoxData->ConnectorUsingGroupCount[conn]; gp++) { if (ShmPsuData->PsuGroup[ConnectorUsingSeq[conn][gp]].UsingTarget == GUN_RIGHT) { totalUsingGpCount++; byte tarGp = ConnectorUsingSeq[conn][gp]; totalQuantity += ShmPsuData->PsuGroup [tarGp].GroupPresentPsuQuantity; _targetVol = ShmPsuData->PsuGroup [tarGp].GroupTargetOutputVoltage / 10; _targetCur += ShmPsuData->PsuGroup [tarGp].GroupTargetOutputCurrent / 10; _avaCur += ShmPsuData->PsuGroup [tarGp].GroupAvailableCurrent / 10; _avaPow += ShmPsuData->PsuGroup [tarGp].GroupAvailablePower / 10; _outputVol = (double) ShmPsuData->PsuGroup [tarGp].GroupPresentOutputVoltage / 10; _outputCur = (double) ShmPsuData->PsuGroup [tarGp].GroupPresentOutputCurrent / 10; _ratingPow += ShmPsuData->PsuGroup [tarGp].TotalRatingPower; } } if (totalUsingGpCount > 0) { PRINTF_FUNC ( "----------------------------------------------------------------- \n" ); PRINTF_FUNC ( "Group Index = %d, totalUsingGpCount = %d \n", conn, totalUsingGpCount); PRINTF_FUNC ( "totalQuantity = %d \n", totalQuantity); PRINTF_FUNC ( "_targetVol = %d (V), _targetCur = %d (A) \n", _targetVol, _targetCur); PRINTF_FUNC ( "_avaCur = %d (A), _avaPow = %d (kw) \n", _avaCur, _avaPow); PRINTF_FUNC ( "_outputVol = %d (V), _outputCur = %d (A) \n", _outputVol, _outputCur); PRINTF_FUNC ( "_ratingPow = %d \n", _ratingPow); } } } } void GetSystemMaxVoltage() { if (ShmSysConfigAndInfo->SysInfo.BootingStatus == BOOTTING) { int _sysVol = 0; for (byte group = 0; group < ShmPsuData->GroupCount; group++) { if (ShmPsuData->PsuGroup[group].GroupMaxVoltage > _sysVol) _sysVol = ShmPsuData->PsuGroup[group].GroupMaxVoltage; } for (byte gun_index = 0; gun_index < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; gun_index++) { if (chargingInfo[gun_index]->MaximumChargingVoltage != _sysVol) { chargingInfo[gun_index]->MaximumChargingVoltage = _sysVol; } } } } void GetTimespecFunc(struct timespec *time) { clock_gettime(CLOCK_MONOTONIC, time); } long int GetTimeoutValue(struct timespec *startTime) { struct timespec endTime; clock_gettime(CLOCK_MONOTONIC, &endTime); return 1000 * (endTime.tv_sec - startTime->tv_sec) + (endTime.tv_nsec - startTime->tv_nsec) / 1000000; } //========================================== // Relay Processing //========================================== void SmartRelayCheck() { /* --------------------------------------- G_0 -----------(R1)----------------> Gun - 0 | -> R3 G_2 --------| | -> R4 G_3 --------| | -> R5 G_1 -----------(R2)----------------> Gun - 1 --------------------------------------- R3 ON 時機 : G_2 屬於 Gun-0 輸出、G_0屬於 Gun - 1 輸出 R4 ON 時機 : G_3 屬於 Gun-0 輸出、G_2屬於 Gun - 1 輸出 R5 ON 時機 : G_3 屬於 Gun-1 輸出、G_1屬於 Gun - 0 輸出 ------------------------- 掃描PSU四個群及兩輸出的ShareGroup內的值各屬於哪個輸出 */ byte _buff[3] = {0}; for (byte group = 0; group < ShmPsuData->GroupCount; group++) { switch(group) { case _PSU_GROUP_INDEX_0: { if (ShmPsuData->PsuGroup[group].UsingTarget == GUN_RIGHT) { // 如果是標準品 if(ShmDcCommonData->systemType == _SYSTEM_TYPE_STANDARD) { _buff[_RELAY_SWITCH_NAME_R4] = 0x01; _buff[_RELAY_SWITCH_NAME_R5] = 0x01; } else { // R3 ON _buff[_RELAY_SWITCH_NAME_R3] = 0x01; } } } break; case _PSU_GROUP_INDEX_2: { if (ShmPsuData->PsuGroup[group].UsingTarget == GUN_LEFT) { // R3 ON _buff[_RELAY_SWITCH_NAME_R3] = 0x01; } if (ShmPsuData->PsuGroup[group].UsingTarget == GUN_RIGHT) { // R4 ON _buff[_RELAY_SWITCH_NAME_R4] = 0x01; } } break; case _PSU_GROUP_INDEX_3: { if (ShmPsuData->PsuGroup[group].UsingTarget == GUN_LEFT) { // R4 ON _buff[_RELAY_SWITCH_NAME_R4] = 0x01; } if (ShmPsuData->PsuGroup[group].UsingTarget == GUN_RIGHT) { // R5 ON _buff[_RELAY_SWITCH_NAME_R5] = 0x01; } } break; case _PSU_GROUP_INDEX_1: { if (ShmPsuData->PsuGroup[group].UsingTarget == GUN_LEFT) { // 如果是標準品 if(ShmDcCommonData->systemType == _SYSTEM_TYPE_STANDARD) { _buff[_RELAY_SWITCH_NAME_R4] = 0x01; _buff[_RELAY_SWITCH_NAME_R5] = 0x01; } else { // R5 ON _buff[_RELAY_SWITCH_NAME_R5] = 0x01; } } } break; } } for (byte _Conn = 0; _Conn < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; _Conn++) { if (ShmSmartBoxData->Dynamic4Fetch[_Conn].FetchLoopStep >= _PSU_DYNAMIC_FETCH_STEP_RELAY) { byte targetGroup = ShmSmartBoxData->Dynamic4Fetch[_Conn].ShareGroup; switch(targetGroup) { case _PSU_GROUP_INDEX_0: { if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_RIGHT) { // 如果是標準品 if(ShmDcCommonData->systemType == _SYSTEM_TYPE_STANDARD) { _buff[_RELAY_SWITCH_NAME_R4] = 0x01; _buff[_RELAY_SWITCH_NAME_R5] = 0x01; ShmSmartBoxData->Dynamic4Fetch[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R4; } else { // R3 ON -- P _buff[_RELAY_SWITCH_NAME_R3] = 0x01; ShmSmartBoxData->Dynamic4Fetch[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R3; } } } break; case _PSU_GROUP_INDEX_2: { if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_LEFT) { // R3 ON -- P _buff[_RELAY_SWITCH_NAME_R3] = 0x01; ShmSmartBoxData->Dynamic4Fetch[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R3; } if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_RIGHT) { // R4 ON -- P _buff[_RELAY_SWITCH_NAME_R4] = 0x01; ShmSmartBoxData->Dynamic4Fetch[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R4; } } break; case _PSU_GROUP_INDEX_3: { if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_LEFT) { // R4 ON -- P _buff[_RELAY_SWITCH_NAME_R4] = 0x01; ShmSmartBoxData->Dynamic4Fetch[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R4; } if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_RIGHT) { // R5 ON -- P _buff[_RELAY_SWITCH_NAME_R5] = 0x01; ShmSmartBoxData->Dynamic4Fetch[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R5; } } break; case _PSU_GROUP_INDEX_1: { if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_LEFT) { // 如果是標準品 if(ShmDcCommonData->systemType == _SYSTEM_TYPE_STANDARD) { _buff[_RELAY_SWITCH_NAME_R4] = 0x01; _buff[_RELAY_SWITCH_NAME_R5] = 0x01; ShmSmartBoxData->Dynamic4Fetch[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R4; } else { // R5 ON -- P _buff[_RELAY_SWITCH_NAME_R5] = 0x01; ShmSmartBoxData->Dynamic4Fetch[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R5; } } } break; } } if (ShmSmartBoxData->Dynamic4Release[_Conn].ReleaseLoopStep >= _PSU_DYNAMIC_RELEASE_STEP_RELAYOFF) { byte targetGroup = ShmSmartBoxData->Dynamic4Release[_Conn].ReleaseGroup; switch(targetGroup) { case _PSU_GROUP_INDEX_0: { if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_RIGHT) { // R3 OFF _buff[_RELAY_SWITCH_NAME_R3] = 0x00; ShmSmartBoxData->Dynamic4Release[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R3; // 如果是標準品 if(ShmDcCommonData->systemType == _SYSTEM_TYPE_STANDARD) { _buff[_RELAY_SWITCH_NAME_R4] = 0x00; _buff[_RELAY_SWITCH_NAME_R5] = 0x00; ShmSmartBoxData->Dynamic4Release[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R4; } } } break; case _PSU_GROUP_INDEX_2: { if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_LEFT) { // R3 OFF _buff[_RELAY_SWITCH_NAME_R3] = 0x00; ShmSmartBoxData->Dynamic4Release[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R3; } if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_RIGHT) { // R5 OFF _buff[_RELAY_SWITCH_NAME_R4] = 0x00; ShmSmartBoxData->Dynamic4Release[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R4; } } break; case _PSU_GROUP_INDEX_3: { if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_LEFT) { // R4 OFF _buff[_RELAY_SWITCH_NAME_R4] = 0x00; ShmSmartBoxData->Dynamic4Release[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R4; } if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_RIGHT) { // R5 OFF _buff[_RELAY_SWITCH_NAME_R5] = 0x00; ShmSmartBoxData->Dynamic4Release[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R5; } } break; case _PSU_GROUP_INDEX_1: { if (ShmPsuData->PsuGroup[targetGroup].UsingTarget == GUN_LEFT) { // 如果是標準品 if(ShmDcCommonData->systemType == _SYSTEM_TYPE_STANDARD) { _buff[_RELAY_SWITCH_NAME_R4] = 0x00; _buff[_RELAY_SWITCH_NAME_R5] = 0x00; ShmSmartBoxData->Dynamic4Release[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R4; } else { // R4 OFF _buff[_RELAY_SWITCH_NAME_R5] = 0x00; ShmSmartBoxData->Dynamic4Release[_Conn].TargetRelay = _RELAY_SWITCH_NAME_R5; } } } break; } } } memcpy(ShmSmartBoxData->ParallelRelayStatus, _buff, sizeof(_buff)); } //========================================== // Sub Processing //========================================== void Assign2ConnectorProcessing(byte _targetConn) { // 根據現在該槍拿到的群數量, 依順去設定群的使用目標為左 /右槍 // 一旦設定該模塊目標為左 /右槍後,AssignedPwr2Connector function 會將該模塊的資訊給對應的左/右槍 for (byte aGp = 0; aGp < ShmSmartBoxData->ConnectorUsingGroupCount[_targetConn]; aGp++) { if (ShmPsuData->PsuGroup[ConnectorUsingSeq[_targetConn][aGp]].IsUsing == YES) { ShmPsuData->PsuGroup[ConnectorUsingSeq[_targetConn][aGp]].UsingTarget = _targetConn; } } } // 將_group該群的模塊配給_targetConn槍號 void AddGroup2Connector(byte _targetConn, byte _group) { ShmPsuData->PsuGroup[_group].IsUsing = YES; if ((ShmSysConfigAndInfo->SysInfo.IsAlternatvieConf == YES && _targetConn == GUN_RIGHT) || (ShmDcCommonData->systemType == _SYSTEM_TYPE_STANDARD && ShmSmartBoxData->ConnectorUsingGroupCount[_targetConn] > 0)) ShmSmartBoxData->ConnectorUsingGroupCount[_targetConn] = 4; else ShmSmartBoxData->ConnectorUsingGroupCount[_targetConn]++; // 對應到相對群 Assign2ConnectorProcessing(_targetConn); } // 將targetGp該群的模塊從_targetConn槍號移除 bool ReleaseConnectorProcessing(byte _targetConn, byte targetGp) { bool result = false; if (ShmPsuData->PsuGroup[targetGp].PwSwitchStatus == _PSU_POWER_STATUS_OFF) { result = true; ShmPsuData->PsuGroup[targetGp].UsingTarget = GUN_CHECK; ShmPsuData->PsuGroup[targetGp].IsUsing = NO; ShmSmartBoxData->ConnectorUsingGroupCount[_targetConn]--; } return result; } //========================================== // Fetch fork //========================================== void InitializeDynamicFetch(byte _targetConn) { ShmSmartBoxData->Dynamic4Fetch[_targetConn].ShareGroup = NONE_GROUP_CAN_SELECTED; ShmSmartBoxData->Dynamic4Fetch[_targetConn].TargetRelay = NONE_RELAY_SELECTED; ShmSmartBoxData->Dynamic4Fetch[_targetConn].ShareTargetCurrent = 0; ShmSmartBoxData->SmartChk[_targetConn].IsFetchStart = NO; } void FetchLoopProcessing(byte _targetConn) { byte targetGroup = ShmSmartBoxData->Dynamic4Fetch[_targetConn].ShareGroup; if (targetGroup == NONE_GROUP_CAN_SELECTED && ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep >= _PSU_DYNAMIC_FETCH_STEP_TG_VOL) { ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_FINISH; } // 用 target vol 比較好還是用當前火線電壓 ? 可以試試看~ float EvVoltage = chargingInfo[_targetConn]->EvBatterytargetVoltage * 10; switch(ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep) { case _PSU_DYNAMIC_FETCH_STEP_NONE: case _PSU_DYNAMIC_FETCH_STEP_WAIT:{ } break; case _PSU_DYNAMIC_FETCH_STEP_TG_VOL: { // 該群升壓 if (ShmPsuData->PsuGroup[targetGroup].GroupPresentOutputVoltage != 0) { PRINTF_FUNC("EV_ReqVoltage = %f, targetGroup = %d, Group_CurVoltage = %d \n", EvVoltage, targetGroup, ShmPsuData->PsuGroup[targetGroup].GroupPresentOutputVoltage); } if (EvVoltage <= PSU_MIN_VOL) { PRINTF_FUNC("***** FETCH_STEP_ABORT ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_ABORT; } else if (ShmPsuData->PsuGroup[targetGroup].GroupPresentOutputVoltage >= EvVoltage - PSU_TG_VOL_GAP) { PRINTF_FUNC("***** FETCH_STEP_RELAY ***** (Gun - %d) \n", _targetConn); ShmPsuData->PsuGroup[targetGroup].UsingTarget = _targetConn; ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_RELAY; } } break; case _PSU_DYNAMIC_FETCH_STEP_RELAY: { // 搭上對應的 Relay if (ShmSmartBoxData->Dynamic4Fetch[_targetConn].TargetRelay < ARRAY_SIZE(ShmSmartBoxData->ParallelRelayStatus)) { if (ShmSmartBoxData->ParallelRelayStatus[ShmSmartBoxData->Dynamic4Fetch[_targetConn].TargetRelay] == YES) { if (ShmSmartBoxData->RcbParallelStatus[ShmSmartBoxData->Dynamic4Fetch[_targetConn].TargetRelay] == YES) { PRINTF_FUNC("***** FETCH_STEP_CUR_SHARE ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_CUR_SHARE; } } } } break; case _PSU_DYNAMIC_FETCH_STEP_CUR_SHARE: { // 均流 : 目標電流 = 當前電流 / (原本輸出群個數 + share group 個數) PRINTF_FUNC("GroupPresent = %d (0.1A), ShareTarget = %d (0.1A), CurrentPresent = %.1f \n", ShmPsuData->PsuGroup[targetGroup].GroupPresentOutputCurrent, ShmSmartBoxData->Dynamic4Fetch[_targetConn].ShareTargetCurrent, chargingInfo[_targetConn]->PresentChargingCurrent); if ((ShmPsuData->PsuGroup[targetGroup].GroupPresentOutputCurrent >= ShmSmartBoxData->Dynamic4Fetch[_targetConn].ShareTargetCurrent - PSU_TG_CUR_GAP && ShmPsuData->PsuGroup[targetGroup].GroupPresentOutputCurrent <= ShmSmartBoxData->Dynamic4Fetch[_targetConn].ShareTargetCurrent + PSU_TG_CUR_GAP) || chargingInfo[_targetConn]->PresentChargingCurrent <= PSU_MIN_CUR) { PRINTF_FUNC("***** FETCH_STEP_WATI_FINISH ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_WATI_FINISH; } } break; case _PSU_DYNAMIC_FETCH_STEP_WATI_FINISH: { AddGroup2Connector(_targetConn, targetGroup); GetTimespecFunc(&ShmSmartBoxData->SmartChk[_targetConn].FetchLoopTime); PRINTF_FUNC("***** FETCH_STEP_FINISH ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->Dynamic4Fetch[_targetConn].ShareGroup = NONE_GROUP_CAN_SELECTED; ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_FINISH; } break; case _PSU_DYNAMIC_FETCH_STEP_FINISH: { // 完成 int _t = GetTimeoutValue(&ShmSmartBoxData->SmartChk[_targetConn].FetchLoopTime); if (_t >= FETCH_FINISH_WAIT_TIME) { InitializeDynamicFetch(_targetConn); PRINTF_FUNC("***** FETCH_STEP_NONE ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_NONE; } } break; case _PSU_DYNAMIC_FETCH_STEP_ABORT: { // 中斷 if (ShmPsuData->PsuGroup[targetGroup].IsUsing) { ShmPsuData->PsuGroup[targetGroup].IsUsing = NO; ShmPsuData->PsuGroup[targetGroup].UsingTarget = GUN_CHECK; } GetTimespecFunc(&ShmSmartBoxData->SmartChk[_targetConn].FetchLoopTime); ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_FINISH; } break; } } byte CheckRemainPwrByConIndex(byte _targetConn) { byte result = NONE_GROUP_CAN_SELECTED; // 該槍已經使用的群數量 byte usingCount = ShmSmartBoxData->ConnectorUsingGroupCount[_targetConn]; for (byte Gp = usingCount; Gp < ARRAY_SIZE(ConnectorUsingSeq[_targetConn]); Gp++) { // 判斷是否有該槍下一個可用群的狀態 if (ShmPsuData->PsuGroup[ConnectorUsingSeq[_targetConn][Gp]].IsUsing == NO && ShmPsuData->PsuGroup[ConnectorUsingSeq[_targetConn][Gp]].GroupPresentPsuQuantity > 0) { result = ConnectorUsingSeq[_targetConn][Gp]; break; } } return result; } void Chk2StopFetchStep(byte _targetConn) { // 開始流程後,就讓流程走完吧~ if (ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep > _PSU_DYNAMIC_FETCH_STEP_NONE && ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep < _PSU_DYNAMIC_FETCH_STEP_TG_VOL) { PRINTF_FUNC("***** FETCH_STEP_ABORT ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->Dynamic4Fetch[_targetConn].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_ABORT; } } bool GetCanFetchResult(byte gun_index) { // 如果當前輸出接近實際可提供的能量 (距離 <= CAP_GAP_FETCH <3KW>) // 不取 EvBatterytargetVoltage 原因是小板會將該值做不同的處理方式 float needPower = chargingInfo[gun_index]->PresentChargingVoltage * chargingInfo[gun_index]->EvBatterytargetCurrent / 100; ShmSmartBoxData->ConnectorStatus[gun_index].NeedToFetch = (needPower > 0 && needPower >= chargingInfo[gun_index]->RealRatingPower - CAP_GAP_FETCH) ? true : false; if (!ShmSmartBoxData->ConnectorStatus[gun_index].NeedToFetch) { // 考慮每顆模塊最大輸出電流為 100A ShmSmartBoxData->ConnectorStatus[gun_index].NeedToFetch = (chargingInfo[gun_index]->EvBatterytargetCurrent >= chargingInfo[gun_index]->AvailableChargingCurrent - LIMIT_PWR_MODULE_GAP) ? true : false; } return ShmSmartBoxData->ConnectorStatus[gun_index].NeedToFetch; } void FetchFork() { pid_t fetchPid; fetchPid = fork(); if(fetchPid > 0) { bool isPass = false; struct ChargingInfoData *_chargingData[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY]; while(!isPass) { isPass = true; for (byte _index = 0; _index < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; _index++) { if (!FindChargingInfoData(_index, &_chargingData[0])) { DEBUG_ERROR("Smartbox (FetchFork) : FindChargingInfoData false \n"); isPass = false; break; } } sleep(1); } while(1) { for (byte gun_index = 0; gun_index < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; gun_index++) { if (chargingInfo[gun_index]->SystemStatus == SYS_MODE_IDLE || chargingInfo[gun_index]->SystemStatus == SYS_MODE_RESERVATION || chargingInfo[gun_index]->SystemStatus == SYS_MODE_MAINTAIN || chargingInfo[gun_index]->SystemStatus == SYS_MODE_FAULT) { ShmSmartBoxData->ConnectorStatus[gun_index].NeedToFetch = NO; if (ShmSmartBoxData->ConnectorStatus[gun_index].ConnectorStaus != _CONNECTOR_STATUS_NONE) ShmSmartBoxData->ConnectorStatus[gun_index].ConnectorStaus = _CONNECTOR_STATUS_NONE; } // 對充電邏輯來說,充電槍只提需求 else if (chargingInfo[gun_index]->SystemStatus >= SYS_MODE_MODE_REASSIGN_CHECK && chargingInfo[gun_index]->SystemStatus <= SYS_MODE_REASSIGN) { // 判斷該群是否被使用中 if (ShmPsuData->PsuGroup[gun_index].IsUsing) { // 如果使用中 : 等待把這顆模塊切出 ShmSmartBoxData->ConnectorStatus[gun_index].ConnectorStaus = _CONNECTOR_STATUS_WAIT; } else { // 直接同意該群後輸出 InitializeDynamicFetch(gun_index); ShmSmartBoxData->ConnectorStatus[gun_index].ConnectorStaus = _CONNECTOR_STATUS_USING; } } else if (chargingInfo[gun_index]->SystemStatus == SYS_MODE_PREPARING) { // 這個階段只會拿到屬於該槍的模塊群 if (ShmDcCommonData->systemType == _SYSTEM_TYPE_SIMPLE) { // 壁掛 : 強制使用第零群 AddGroup2Connector(gun_index, _PSU_GROUP_INDEX_0); } else { if (!ShmPsuData->PsuGroup[gun_index].IsUsing) { // 第一個 : Gun index // 第二個 : Psu Group index AddGroup2Connector(gun_index, gun_index); } } } else if (chargingInfo[gun_index]->SystemStatus == SYS_MODE_CHARGING) { // 判斷是否可以及需要提供多餘群輸出 byte chkRemainGp = NONE_GROUP_CAN_SELECTED; if (ShmSmartBoxData->Dynamic4Fetch[gun_index].ShareGroup == NONE_GROUP_CAN_SELECTED) chkRemainGp = CheckRemainPwrByConIndex(gun_index); else chkRemainGp = ShmSmartBoxData->Dynamic4Fetch[gun_index].ShareGroup; ShmSmartBoxData->ConnectorStatus[gun_index].NeedToFetch = GetCanFetchResult(gun_index); if (ShmDcCommonData->systemType != _SYSTEM_TYPE_SIMPLE && ShmSmartBoxData->Dynamic4Release[gun_index].ReleaseLoopStep == _PSU_DYNAMIC_RELEASE_STEP_NONE && ((ShmSmartBoxData->ConnectorStatus[gun_index].NeedToFetch && chkRemainGp != NONE_GROUP_CAN_SELECTED && ShmSmartBoxData->Dynamic4Fetch[gun_index].FetchLoopStep <= _PSU_DYNAMIC_FETCH_STEP_WAIT) || ShmDcCommonData->smartFetchRun[gun_index]) ) { if (!ShmSmartBoxData->SmartChk[gun_index].IsFetchStart) { // 判斷是否可以及需要提供多餘群輸出 PRINTF_FUNC("Fetch Target = %d, Target Group = %d \n", gun_index, chkRemainGp); // 鎖定目標群,動態分配開始 // 該群模塊即被鎖定, 預防另外一群也進入相同邏輯,預防搶同一個群的情形 ShmPsuData->PsuGroup[chkRemainGp].IsUsing = YES; ShmSmartBoxData->Dynamic4Fetch[gun_index].ShareGroup = chkRemainGp; PRINTF_FUNC("***** FETCH_STEP_WAIT ***** (Gun - %d) \n", gun_index); ShmSmartBoxData->Dynamic4Fetch[gun_index].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_WAIT; // 倒數進入動態分配邏輯 - Fetch loop ShmSmartBoxData->SmartChk [gun_index].IsFetchStart = YES; // 條件成立, 倒數 FETCH_SMART_CHK_TIME <5s> GetTimespecFunc(&ShmSmartBoxData->SmartChk[gun_index].FetchLoopTime); } else { if (ShmSmartBoxData->Dynamic4Fetch[gun_index].FetchLoopStep == _PSU_DYNAMIC_FETCH_STEP_WAIT) { int _t = GetTimeoutValue(&ShmSmartBoxData->SmartChk[gun_index].FetchLoopTime); if (_t >= FETCH_SMART_CHK_TIME) { PRINTF_FUNC("***** FETCH_STEP_TG_VOL ***** (Gun - %d) \n", gun_index); ShmSmartBoxData->Dynamic4Fetch[gun_index].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_TG_VOL; ShmDcCommonData->smartFetchRun[gun_index] = NO; } } } } else { // 動態分配條件中斷 Chk2StopFetchStep(gun_index); } } FetchLoopProcessing(gun_index); } usleep(50000); } } } //========================================== // Release fork //========================================== void InitializeDynamicRelease(byte _targetConn) { ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseGroup = NONE_GROUP_CAN_SELECTED; ShmSmartBoxData->Dynamic4Release[_targetConn].TargetRelay = NONE_RELAY_SELECTED; ShmSmartBoxData->Dynamic4Release[_targetConn].LimitCurCap = 0; ShmSmartBoxData->Dynamic4Release[_targetConn].LimitPwrCap = 0; ShmSmartBoxData->Dynamic4Release[_targetConn].LimitCur = 0; ShmSmartBoxData->Dynamic4Release[_targetConn].LimitPwr = 0; ShmSmartBoxData->Dynamic4Release[_targetConn].AutoRelease = NO; ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseLoopStep = _PSU_DYNAMIC_FETCH_STEP_FINISH; } byte CheckReleasePwrByConIndex(byte _targetConn) { byte result = NONE_GROUP_CAN_SELECTED; // 該槍已經使用的群數量 byte usingCount = ShmSmartBoxData->ConnectorUsingGroupCount[_targetConn]; // 不可把最後一個也拿來釋放 if (usingCount - 1 > 0) result = ConnectorUsingSeq[_targetConn][usingCount - 1]; return result; } void ReleaseLoopProcessing(byte _targetConn) { byte targetGroup = ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseGroup; switch(ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseLoopStep) { case _PSU_DYNAMIC_RELEASE_STEP_NONE: case _PSU_DYNAMIC_RELEASE_STEP_WAIT: {} break; case _PSU_DYNAMIC_RELEASE_STEP_LIMIT: { // 應該要通知可輸出的能量與電流 // if the current and power limits are below their capacity // if (ShmSmartBoxData->Dynamic4Release[_targetConn].LimitPwr <= ShmSmartBoxData->Dynamic4Release[_targetConn].LimitPwrCap || // chargingInfo[_targetConn]->PresentChargingCurrent * 10 <= PSU_LIMIT_CUR) if (ShmSmartBoxData->Dynamic4Release[_targetConn].LimitPwr <= ShmSmartBoxData->Dynamic4Release[_targetConn].LimitPwrCap) { PRINTF_FUNC("***** RELEASE_STEP_CUR_SHARE ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_CUR_SHARE; } } break; case _PSU_DYNAMIC_RELEASE_STEP_CUR_SHARE: { PRINTF_FUNC("targetGroup = %d, GroupPresentOutputCurrent = %.1f \n", targetGroup, ShmPsuData->PsuGroup[targetGroup].GroupPresentOutputCurrent); // if the output current is below their capacity for 5s int _t = GetTimeoutValue(&ShmSmartBoxData->SmartChk[_targetConn].ReleaseLoopTime); if (ShmPsuData->PsuGroup[targetGroup].GroupPresentOutputCurrent <= RELEASE_STEP_CUR_SHARE && _t >= WAIT_FOR_LIMIT_STABLE) { PRINTF_FUNC("***** RELEASE_STEP_RELAYOFF ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_RELAYOFF; } } break; case _PSU_DYNAMIC_RELEASE_STEP_RELAYOFF: { // 釋放對應的 Relay PRINTF_FUNC("TargetRelay (%d), targetGroup = %d, ParallelRelayStatus = (%d), RcbParallelStatus = (%d) \n", ShmSmartBoxData->Dynamic4Release[_targetConn].TargetRelay, targetGroup, ShmSmartBoxData->ParallelRelayStatus[ShmSmartBoxData->Dynamic4Release[_targetConn].TargetRelay], ShmSmartBoxData->RcbParallelStatus[ShmSmartBoxData->Dynamic4Release[_targetConn].TargetRelay]); if (ShmSmartBoxData->Dynamic4Release[_targetConn].TargetRelay < ARRAY_SIZE(ShmSmartBoxData->ParallelRelayStatus)) { if (ShmSmartBoxData->ParallelRelayStatus[ShmSmartBoxData->Dynamic4Release[_targetConn].TargetRelay] == NO) { if (ShmSmartBoxData->RcbParallelStatus[ShmSmartBoxData->Dynamic4Release[_targetConn].TargetRelay] == NO) { PRINTF_FUNC("***** RELEASE_STEP_WATI_FINISH ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_WAIT_FINISH; } } } } break; case _PSU_DYNAMIC_RELEASE_STEP_WAIT_FINISH: { if(ReleaseConnectorProcessing(_targetConn, targetGroup)) { GetTimespecFunc ( & ShmSmartBoxData->SmartChk [_targetConn].ReleaseLoopTime ); PRINTF_FUNC("***** RELEASE_STEP_FINISH ***** (Gun - %d) \n", _targetConn); InitializeDynamicRelease(_targetConn); ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_FINISH; } } break; case _PSU_DYNAMIC_RELEASE_STEP_FINISH: { // 完成 int _t = GetTimeoutValue(&ShmSmartBoxData->SmartChk[_targetConn].ReleaseLoopTime); if (_t >= RELEASE_FINISH_WAIT_TIME) { PRINTF_FUNC("***** RELEASE_STEP_NONE ***** (Gun - %d) \n", _targetConn); ShmSmartBoxData->SmartChk[_targetConn].IsReleaseStart = NO; ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_NONE; } } break; case _PSU_DYNAMIC_RELEASE_STEP_ABORT: { GetTimespecFunc(& ShmSmartBoxData->SmartChk [_targetConn].ReleaseLoopTime); PRINTF_FUNC("***** RELEASE_STEP_FINISH ***** (Gun - %d) \n", _targetConn); InitializeDynamicRelease(_targetConn); ShmSmartBoxData->Dynamic4Release[_targetConn].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_FINISH; } break; } } void Chk2StopReleaseStep(byte _targetConn) { // 開始流程後,就讓流程走完吧~ if (ShmSmartBoxData->Dynamic4Release [_targetConn].ReleaseGroup > _PSU_DYNAMIC_RELEASE_STEP_NONE && ShmSmartBoxData->Dynamic4Release [_targetConn].ReleaseGroup < _PSU_DYNAMIC_RELEASE_STEP_LIMIT) { PRINTF_FUNC ( "***** RELEASE_STEP_ABORT ***** (Gun - %d) \n", _targetConn ); //ShmSmartBoxData->Dynamic4Release [_targetConn].ReleaseGroup = _PSU_DYNAMIC_RELEASE_STEP_ABORT; } else ShmSmartBoxData->SmartChk[_targetConn].IsReleaseStart = NO; } bool GetCanReleaseResult(byte gun_index, byte releaseGp) { bool result = false; unsigned short releasePwr = ShmPsuData->PsuGroup[releaseGp].TotalRatingPower * 10; float needPower = chargingInfo[gun_index]->PresentChargingVoltage * chargingInfo[gun_index]->EvBatterytargetCurrent / 100; result = (chargingInfo[gun_index]->RealRatingPower > 0 && releasePwr > 0 && needPower < chargingInfo[gun_index]->RealRatingPower - releasePwr - CAP_GAP_RELEASE) ? true : false; if (result) { // afterReleaseMdCur 每個模塊最大 100A : 1A float afterReleaseMdCur = chargingInfo[gun_index]->AvailableChargingCurrent - ShmPsuData->PsuGroup[releaseGp].GroupAvailableCurrent; result = (chargingInfo[gun_index]->EvBatterytargetCurrent >= afterReleaseMdCur - LIMIT_PWR_MODULE_GAP) ? false : true; } return result; } void ReleaseFork() { pid_t releasePid; releasePid = fork(); if(releasePid > 0) { bool isPass = false; struct ChargingInfoData *_chargingData[CHAdeMO_QUANTITY + CCS_QUANTITY + GB_QUANTITY]; while(!isPass) { isPass = true; for (byte _index = 0; _index < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; _index++) { if (!FindChargingInfoData(_index, &_chargingData[0])) { DEBUG_ERROR("Smartbox (ReleaseFork) : FindChargingInfoData false \n"); isPass = false; break; } } sleep(1); } while(1) { for (byte gun_index = 0; gun_index < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; gun_index++) { if (chargingInfo[gun_index]->SystemStatus == SYS_MODE_CHARGING) { // 釋出多餘群 : 要判斷所要釋出群所能提供的最大能量 RealRatingPower // 先找到是否有下一個要釋出的群 byte releaseGp = CheckReleasePwrByConIndex(gun_index); if (releaseGp != NONE_GROUP_CAN_SELECTED) { bool canRelease = GetCanReleaseResult(gun_index, releaseGp); if (chargingInfo[gun_index]->Type != _Type_Test && ShmSmartBoxData->Dynamic4Fetch[gun_index].FetchLoopStep == _PSU_DYNAMIC_FETCH_STEP_NONE && (ShmSmartBoxData->AnotherConnectorStatus[gun_index].ConnectorStaus == _CONNECTOR_STATUS_WAIT || (ShmSmartBoxData->AnotherConnectorStatus[gun_index].NeedToFetch && SMART_MODE && chargingInfo[gun_index]->_TakePsuGpCount > ShmDcCommonData->halfGroupCount) || canRelease || ShmDcCommonData->smartReleaseRun[gun_index]) ) { //PRINTF_FUNC("RealRatingPower = %d \n", chargingInfo[gun_index]->RealRatingPower); //PRINTF_FUNC("releasePwr = %d, needPower = %f \n", releasePwr, needPower); if (ShmSmartBoxData->Dynamic4Release[gun_index].ReleaseLoopStep == _PSU_DYNAMIC_RELEASE_STEP_NONE) { if (!ShmSmartBoxData->SmartChk[gun_index].IsReleaseStart) { PRINTF_FUNC("***** Entry release chk (Gun_%d).. ***** \n", gun_index); ShmSmartBoxData->SmartChk[gun_index].IsReleaseStart = YES; ShmSmartBoxData->Dynamic4Release[gun_index].CheckOutPwrIsStable = chargingInfo[gun_index]->PresentChargingPower; GetTimespecFunc(&ShmSmartBoxData->SmartChk[gun_index].ReleaseLoopTime); } else { int _delayt = GetTimeoutValue(&ShmSmartBoxData->SmartChk[gun_index].ReleaseLoopTime); // 每五秒看一次輸出能量是否穩定, 穩定則進入分配 if (_delayt >= RELEASE_STABLE_CHK_TIME) { if (chargingInfo[gun_index]->PresentChargingPower > ShmSmartBoxData->Dynamic4Release[gun_index].CheckOutPwrIsStable + STABLE_CAP_GAP) { ShmSmartBoxData->Dynamic4Release[gun_index].CheckOutPwrIsStable = chargingInfo[gun_index]->PresentChargingPower; GetTimespecFunc(&ShmSmartBoxData->SmartChk[gun_index].ReleaseLoopTime); } else { // 進入穩定 PRINTF_FUNC("***** RELEASE_STEP_WAIT ***** \n"); ShmSmartBoxData->Dynamic4Release[gun_index].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_WAIT; GetTimespecFunc(&ShmSmartBoxData->SmartChk[gun_index].ReleaseLoopTime); } } } } else if (ShmSmartBoxData->Dynamic4Release[gun_index].ReleaseLoopStep == _PSU_DYNAMIC_RELEASE_STEP_WAIT) { int _delayt = GetTimeoutValue(&ShmSmartBoxData->SmartChk[gun_index].ReleaseLoopTime); // 每五秒看一次輸出能量是否穩定, 穩定則進入分配 if (_delayt >= RELEASE_SMART_CHK_TIME) { PRINTF_FUNC("***** RELEASE_STEP_LIMIT ***** \n"); ShmSmartBoxData->Dynamic4Release[gun_index].ReleaseGroup = releaseGp; ShmSmartBoxData->Dynamic4Release[gun_index].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_LIMIT; ShmDcCommonData->smartReleaseRun[gun_index] = NO; } } } else { // 動態分配條件中斷 Chk2StopReleaseStep(gun_index); } } } else if (chargingInfo[gun_index]->SystemStatus == SYS_MODE_IDLE || chargingInfo[gun_index]->SystemStatus == SYS_MODE_FAULT || chargingInfo[gun_index]->SystemStatus == SYS_MODE_RESERVATION || (chargingInfo[gun_index]->SystemStatus >= SYS_MODE_TERMINATING && chargingInfo[gun_index]->SystemStatus <= SYS_MODE_ALARM)) { if (ShmSmartBoxData->ConnectorStatus[gun_index].ConnectorStaus == _CONNECTOR_STATUS_NONE) { byte totoalUsingGpCount = ShmSmartBoxData->ConnectorUsingGroupCount[gun_index]; for (byte _gp = 0; _gp < totoalUsingGpCount; _gp++) { PRINTF_FUNC("ConnectorUsingSeq[%d][%d] = %d \n", gun_index, _gp, ConnectorUsingSeq[gun_index][_gp]); ReleaseConnectorProcessing(gun_index, ConnectorUsingSeq[gun_index][_gp]); } if (ShmSmartBoxData->Dynamic4Fetch[gun_index].ShareGroup != NONE_GROUP_CAN_SELECTED && ShmSmartBoxData->Dynamic4Fetch[gun_index].FetchLoopStep < _PSU_DYNAMIC_FETCH_STEP_FINISH) ShmSmartBoxData->Dynamic4Fetch[gun_index].FetchLoopStep = _PSU_DYNAMIC_FETCH_STEP_ABORT; ShmSmartBoxData->Dynamic4Release[gun_index].ReleaseLoopStep = _PSU_DYNAMIC_RELEASE_STEP_NONE; } if (ShmSmartBoxData->ConnectorStatus[gun_index].NeedToFetch) ShmSmartBoxData->ConnectorStatus[gun_index].NeedToFetch = NO; } ReleaseLoopProcessing(gun_index); } usleep(50000); } } } //========================================== // Main Function //========================================== void TakeAnotherGunStatus(byte target) { byte another = 0; if (target == 0) another = 1; ShmSmartBoxData->AnotherConnectorStatus[another].ConnectorStaus = ShmSmartBoxData->ConnectorStatus[target].ConnectorStaus; ShmSmartBoxData->AnotherConnectorStatus[another].NeedToFetch = ShmSmartBoxData->ConnectorStatus[target].NeedToFetch; } void AssignedPwr2Connector() { for(byte _gun = 0; _gun < ShmSysConfigAndInfo->SysConfig.TotalConnectorCount; _gun++) { byte usingGroupCount = 0; float availablePwr = 0, availableCur = 0, iAvailableCur = 0, totalPsuCount = 0, kwAvailablePwr = 0; float outputVol = 0, outputCur = 0; float limitCur = 0, limitPwr = 0, remainCur = 0; for (byte group = 0; group < ShmPsuData->GroupCount; group++) { if (ShmPsuData->PsuGroup[group].UsingTarget == _gun) { usingGroupCount++; if (group != ShmSmartBoxData->Dynamic4Fetch[_gun].ShareGroup && (group != ShmSmartBoxData->Dynamic4Release[_gun].ReleaseGroup || ShmSmartBoxData->Dynamic4Release[_gun].ReleaseLoopStep <= _PSU_DYNAMIC_RELEASE_STEP_WAIT_FINISH)) { availablePwr += ShmPsuData->PsuGroup[group].GroupAvailablePower; availableCur += ShmPsuData->PsuGroup[group].GroupAvailableCurrent; iAvailableCur += ShmPsuData->PsuGroup[group].TotalIAvailableCurrent; totalPsuCount += ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity; if (ShmPsuData->PsuGroup[group].TotalRatingPower <= 15 || ShmPsuData->PsuGroup[group].TotalRatingPower > 30) kwAvailablePwr += (ShmPsuData->PsuGroup[group].GroupAvailablePower) / 10; else kwAvailablePwr += ShmPsuData->PsuGroup[group].TotalRatingPower; } // Release Loop 用 : 實際上~ 電流會飄 .... // 這段, 只取還要留住的模塊輸出電流與能量就好 if (group != ShmSmartBoxData->Dynamic4Release[_gun].ReleaseGroup && ShmSmartBoxData->Dynamic4Release[_gun].ReleaseLoopStep == _PSU_DYNAMIC_RELEASE_STEP_LIMIT) { limitCur += ShmPsuData->PsuGroup[group].GroupTargetOutputCurrent; remainCur += ShmPsuData->PsuGroup[group].TotalIAvailableCurrent; limitPwr += ShmPsuData->PsuGroup[group].TotalRatingPower; } outputCur += ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent; if (outputVol < ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage || outputVol == 0) outputVol = ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage; } } chargingInfo[_gun]->AvailableChargingPower = availablePwr; chargingInfo[_gun]->AvailableChargingCurrent = availableCur; chargingInfo[_gun]->DeratingChargingCurrent = iAvailableCur; chargingInfo[_gun]->_TotalPsuCount = totalPsuCount; chargingInfo[_gun]->PresentChargingVoltage = outputVol / 10; // 在低於最大 1KW 輸出時~ 讓我們偷雞一下~ if (chargingInfo[_gun]->SystemStatus == SYS_MODE_CHARGING) { if (chargingInfo[_gun]->RealMaxPower <= 10 && outputCur < (chargingInfo[_gun]->RealMaxCurrent * 0.9)) outputCur = (chargingInfo[_gun]->RealMaxCurrent * 0.9); } chargingInfo[_gun]->PresentChargingCurrent = outputCur / 10; chargingInfo[_gun]->RealRatingPower = kwAvailablePwr * 10; chargingInfo[_gun]->_TakePsuGpCount = usingGroupCount; // 雙槍需要記得另外一把槍的狀態 if (ShmSysConfigAndInfo->SysConfig.TotalConnectorCount > 1) TakeAnotherGunStatus(_gun); // 取 Release group 的最大電流 if (ShmSmartBoxData->Dynamic4Release[_gun].ReleaseLoopStep == _PSU_DYNAMIC_RELEASE_STEP_LIMIT && ShmSmartBoxData->Dynamic4Release[_gun].LimitPwrCap == 0) { // limitCur : 下給剩餘群的需求電流 //ShmSmartBoxData->Dynamic4Release[_gun].LimitCurCap = limitCur; // Release 均流想法(總結) : 如果是抽載不足的自然釋放~ 應該都會跑均流方式 // 狀況一 : 群1 : 30A,群2 : 30A 共 60A 輸出,且實際上群1 可提供的電流為 75A (400V 輸出) // 狀況二 : 群1 : 75A,群2 : 75A 共 150A 輸出,且實際上群1 可提供的電流為 75A (400V 輸出) // 狀況三 : 群1 : 70A,群2 : 70A 共 140A 輸出,且實際上群1 可提供的電流為 75A (400V 輸出) // --------------------------------------------- // 狀況一 : 60A <= 群1可提供的 75A : 均流 // 狀況一 : 150A > 群1可提供的 75A : 直接切斷 // 狀況一 : 140A > 群1可提供的 75A : 直接切斷 if (chargingInfo[_gun]->PresentChargingCurrent <= remainCur) { PRINTF_FUNC("PresentChargingCurrent = %f, remainCur = %f \n", chargingInfo[_gun]->PresentChargingCurrent, remainCur / 10); limitCur = chargingInfo[_gun]->PresentChargingCurrent * 10; ShmSmartBoxData->Dynamic4Release[_gun].AutoRelease = YES; } else PRINTF_FUNC("limitCur = %f \n", limitCur); ShmSmartBoxData->Dynamic4Release[_gun].LimitCurCap = limitCur; ShmSmartBoxData->Dynamic4Release[_gun].LimitPwrCap = limitPwr * 10; } } } void CollectGroupInformation(byte group) { int _groupPower = 0 , _groupCurrent = 0 , _groupMaxVoltage = 0; int Iavail = 0, Pavail = 0; unsigned short _outputVolBuf = 0, _outputCurBuf = 0; for (byte index = 0; index < ShmPsuData->PsuGroup[group].GroupPresentPsuQuantity; index ++) { // Cap _groupCurrent += ShmPsuData->PsuGroup[group].PsuModule[index].AvailableCurrent; _groupPower += ShmPsuData->PsuGroup[group].PsuModule[index].AvailablePower; if (ShmPsuData->PsuGroup[group].PsuModule[index].PresentMaxOutputVoltage > _groupMaxVoltage) _groupMaxVoltage = ShmPsuData->PsuGroup[group].PsuModule[index].PresentMaxOutputVoltage; // Iavailable Iavail += ShmPsuData->PsuGroup[group].PsuModule[index].IAvailableCurrent; Pavail += ShmPsuData->PsuGroup[group].PsuModule[index].KwAvailablePower; // output _outputCurBuf += ShmPsuData->PsuGroup[group].PsuModule[index].PresentOutputCurrent; if (_outputVolBuf == 0 || (ShmPsuData->PsuGroup[group].PsuModule[index].PresentOutputVoltage > PSU_MIN_VOL && _outputVolBuf < ShmPsuData->PsuGroup[group].PsuModule[index].PresentOutputVoltage)) { _outputVolBuf = ShmPsuData->PsuGroup[group].PsuModule[index].PresentOutputVoltage; } } ShmPsuData->PsuGroup[group].GroupMaxVoltage = _groupMaxVoltage; ShmPsuData->PsuGroup[group].GroupAvailableCurrent = _groupCurrent; ShmPsuData->PsuGroup[group].GroupAvailablePower = _groupPower; ShmPsuData->PsuGroup[group].TotalIAvailableCurrent = Iavail; ShmPsuData->PsuGroup[group].TotalRatingPower = Pavail; ShmPsuData->PsuGroup[group].GroupPresentOutputVoltage = _outputVolBuf; ShmPsuData->PsuGroup[group].GroupPresentOutputCurrent = _outputCurBuf; } void CollectPsuInformation() { int _sysPwr = 0, _sysCur = 0, _sysVol = 0; for (byte group = 0; group < ShmPsuData->GroupCount; group++) { // 取群資訊 CollectGroupInformation(group); // 整理系統資訊 _sysPwr += ShmPsuData->PsuGroup[group].GroupAvailablePower; _sysCur += ShmPsuData->PsuGroup[group].GroupAvailableCurrent; if (ShmPsuData->PsuGroup[group].GroupMaxVoltage > _sysVol) _sysVol = ShmPsuData->PsuGroup[group].GroupMaxVoltage; } // 系統,電壓,電流,能量 ShmPsuData->SystemAvailableCurrent = _sysCur; ShmPsuData->SystemAvailablePower = _sysPwr; GetSystemMaxVoltage(); } //========================================== // Main Loop //========================================== int main(void) { if(InitShareMemory() == FAIL) { #ifdef SystemLogMessage DEBUG_ERROR("InitShareMemory NG\n"); #endif if(ShmStatusCodeData != NULL) { ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory = 1; } sleep(5); return 0; } Initialization(); // 對充電槍操作 : 將模塊切出來與補上剩餘的模塊功率 FetchFork(); ReleaseFork(); GetTimespecFunc(&_log_time); while(1) { if (ShmPsuData->Work_Step >= GET_SYS_CAP) { // 收集來自 PSU 的資訊 CollectPsuInformation(); // 將群資訊分配到對應的使用中充電槍 AssignedPwr2Connector(); int time = GetTimeoutValue(&_log_time); if (time < 0) GetTimespecFunc(&_log_time); // 低 Priority 的指令 if (time > 5000) { //PrintfLog(); GetTimespecFunc(&_log_time); } } if (ShmDcCommonData->systemType != _SYSTEM_TYPE_SIMPLE && ShmPsuData->Work_Step == _WORK_CHARGING) { // 橋接控制 SmartRelayCheck(); } usleep(50000); } return -1; }