FolusWen 4 жил өмнө
parent
commit
ea85979376

+ 1 - 0
EVSE/Modularization/.gitignore

@@ -5,4 +5,5 @@ Module_4g
 WebService
 logPackTools
 OcppBackend
+OcppBackend20
 Module_ProduceUtils

+ 156 - 3
EVSE/Projects/DO360/Apps/Config.h

@@ -7,7 +7,9 @@
 #ifndef CONFIG_H_
 #define CONFIG_H_
 
-typedef unsigned char			byte;
+#include <stdbool.h>
+
+typedef unsigned char			    byte;
 
 #define TOTAL_QUANTITY_GUN			4				//Max Count
 
@@ -45,8 +47,19 @@ typedef unsigned char			byte;
 #define BOOTTING			0
 #define BOOT_COMPLETE		1
 
+#define uSEC_VAL                    1000000
+#define mSEC_VAL                    1000
+#define MAX_GROUP_QUANTITY          4
+#define MAX_PSU_MODULE_QUANTITY     62
+#define MAX_MODULE_PER_GROUP        12
 #define SM_ChargerInfoKey           3000
 
+// **********  Audi ********** //
+// Model Name: DOYC182000D2AD
+
+// ********** e4you ********** //
+// Model Name: DKYE182000D1E4
+
 enum _SYSTEM_STATUS
 {
 	S_BOOTING = 						0,
@@ -239,15 +252,74 @@ enum _CONN_STATUS
     _Connnection_Disconnected = 2,
 };
 
+enum _LED_INDICATION_STATUS
+{
+    _LED_INDICATION_OFF             = 0,            // all led off
+    _LED_INDICATION_RUN             = 1,            // green led on, red led off
+    _LED_INDICATION_FAULT           = 2,            // green led off, red led on
+    _LED_INDICATION_ON              = 3,            // all led on
+    _LED_INDICATION_GREEN_TOGGLE    = 4,            // green led toggle
+    _LED_INDICATION_RED_TOGGLE      = 5,            // red led toggle
+};
+
+enum _STANDARD_INDICATION_STATUS
+{
+    _STANDARD_LED_None          = 0,
+    _STANDARD_LED_Disconnection = 1,
+    _STANDARD_LED_Running       = 2,
+    _STANDARD_LED_Alarm         = 3,
+};
+
+enum _E4YOU_INDICATION_STATUS
+{
+    _E4YOU_LED_None             = 0,
+    _E4YOU_LED_Disconnection    = 1,
+    _E4YOU_LED_Idle             = 2,
+    _E4YOU_LED_Charging         = 3,
+    _E4YOU_LED_Alarm            = 4,
+};
+
 typedef union
 {
     unsigned int CtrlValue;
     struct
     {
+        unsigned int SelfTestOK:1;                  // 0: self test not completed,  1: self test ok
+        unsigned int PrimaryDisable:1;              // 0: primary enable,           1: primary disable
+        unsigned int RelayBoardDisable:1;           // 0: relay board enable,       1: relay board disable
+        unsigned int FanBoardDisable:1;             // 0: fan board enable,         1: fan board disable
+        unsigned int LedBoardDisable:1;             // 0: led board enable,         1: led board disable
+        unsigned int SecondRelayBoardEnable:1;      // 0: second relay disable,     1: second relay enable
+        unsigned int StandardLedIndication:1;       // 0: no led indication,        1: enable standard led indication
+        unsigned int E4YOULedIndication:1;          // 0: no led indication,        1: enable e4you led indication
+        unsigned int res:24;
+    }bits;
+}SystemControl;
+
+typedef union
+{
+    unsigned int CtrlValue;
+    struct
+    {
+        unsigned int SelfTestOK:1;                  // 0: self test not completed,  1: self test ok
+        unsigned int DisableDoorSensor:1;           // 0: door sensor enable,       1: door sensor disable
+        unsigned int DisableEmergencyButton:1;      // 0: emergency button enable,  1: emergency button disable
+        unsigned int DoorSensorReverse:1;           // 0: the same as ds's,         1: status is different from ds's
+        unsigned int AcContactorReverse:1;          // 0: the same as ds's,         1: status is different from ds's
+        unsigned int res:27;
+    }bits;
+}PrimaryControl;
+
+typedef union
+{
+    unsigned int CtrlValue;
+    struct
+    {
+        unsigned int SelfTestOK:1;                  // 0: self test not completed,  1: self test ok
         unsigned int AcContactor:1;                 // 0: ac contactor off,         1: ac contactor on
         unsigned int AcContactorForceOff:1;         // 0: no effect,                1: ac contactor off
         unsigned int StandbyCountdown:1;            // 0: charger is using,         1: start countdown
-        unsigned int res:29;
+        unsigned int res:28;
     }bits;
 }RelayControl;
 
@@ -256,20 +328,101 @@ typedef union
     unsigned int CtrlValue;
     struct
     {
-        unsigned int FailureResume:1;               // 0: no error,                 1: psu failure, need resume
+        unsigned int SelfTestOK:1;                  // 0: self test not completed,  1: self test ok
+        unsigned int res:31;
+    }bits;
+}FanControl;
+
+typedef union
+{
+    unsigned int CtrlValue;
+    struct
+    {
+        unsigned int SelfTestOK:1;                  // 0: self test not completed,  1: self test ok
         unsigned int res:31;
     }bits;
+}LedFanControl;
+
+typedef union
+{
+    unsigned int CtrlValue;
+    struct
+    {
+        unsigned int SelfTestOK:1;                  // 0: self test not completed,  1: self test ok
+        unsigned int FailureResume:1;               // 0: no error,                 1: psu failure, need resume
+        unsigned int res:30;
+    }bits;
 }PsuControl;
 
 typedef struct
 {
+    SystemControl   SysCtrl;
+    PrimaryControl  PrimaryCtrl;
     RelayControl    RelayCtrl;
+    FanControl      FanCtrl;
+    LedFanControl   LedCtrl;
     PsuControl      PsuCtrl;
 }SysControl;
+// ************************************************************************************************* //
+typedef struct
+{
+    bool          CheckIn;
+    unsigned char Address;
+    unsigned char GroupNo;
+    unsigned char Index;
+}PsuAddressInfoData;
+
+typedef struct
+{
+    unsigned char GroupPsuQuantity;
+    unsigned char PsuSN[MAX_MODULE_PER_GROUP];
+}GroupInfoData;
+
+typedef struct
+{
+    bool                PsuLocationInit;
+    bool                ReInitPsuLocation;
+    unsigned char       TotalPsuQuantity;
+    GroupInfoData       GroupLocationInfo[MAX_GROUP_QUANTITY];
+    PsuAddressInfoData  PsuAddressInfo[MAX_PSU_MODULE_QUANTITY];
+}PsuPositionInfoData;
+// ************************************************************************************************* //
+typedef enum
+{
+    _GROLE_IDLE             = 0,
+    _GROLE_MASTER           = 1,
+    _GROLE_SLAVE            = 2,
+    _GROLE_SWITCH_TO_IDLE   = 10,
+    _GROLE_SWITCH_IDLE_OK   = 11,
+    _GROLE_SWITCH_TO_MASTER = 20,
+    _GROLE_SWITCH_MASTER_OK = 21,
+    _GROLE_SWITCH_TO_SLAVE  = 30,
+    _GROLE_SWITCH_SLAVE_OK  = 31,
+}_GROUP_ROLE;
+
+typedef struct
+{
+    unsigned char           Index;
+    unsigned char           Role;
+    unsigned char           Location;
+    unsigned char           GroupMemberQuantity;
+    unsigned char           GroupMember[MAX_GROUP_QUANTITY];
+    unsigned char           TargetGroup;                        // group index + 1
+}PsuGroupCollectionData;
+
+typedef struct
+{
+    byte                    Location[MAX_GROUP_QUANTITY];
+    byte                    Layout[MAX_GROUP_QUANTITY];
+    PsuGroupCollectionData  GroupCollection[MAX_GROUP_QUANTITY];
+}PsuGroupingInfoData;
+// ************************************************************************************************* //
 
 typedef struct
 {
     SysControl Control;
+    PsuPositionInfoData PsuPosition;
+    PsuGroupingInfoData PsuGrouping;
 }ChargerInfoData;
 
 #endif /* CONFIG_H_ */

+ 103 - 5
EVSE/Projects/DO360/Apps/Module_EvComm.c

@@ -2357,6 +2357,7 @@ void DispenserSocketProcess(int socketFd, struct sockaddr_in clientInfo, unsigne
 	                                ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectionInfo[index].Status = _CNS_DispenserMatched;
 	                                PRINTF_FUNC("********** Dispenser id %d check in, model name: %s **********\n", dispenserID, modelName);
 	                                memcpy(ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenserIndex].ModelName, modelName, 64);
+	                                ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenserIndex].ConnectionChannel = index;
 	                                ackResult = _R_OK;
 	                            }
 	                            else
@@ -2575,10 +2576,17 @@ void DispenserSocketProcess(int socketFd, struct sockaddr_in clientInfo, unsigne
 			usleep((SOCKET_RECEIVE_INTERVAL * 1000));
 		}
 
-		if(timeout >= DISPENSER_SOCKET_TIMEOUT)
+		if(timeout >= DISPENSER_SOCKET_TIMEOUT || ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenserIndex].Setting.bits.DuplicateIp)
 		{
-            //timeout
-            PRINTF_FUNC("IP: %s, Socket %d connection timeout", (inet_ntoa(clientInfo.sin_addr)), index);
+		    if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenserIndex].Setting.bits.DuplicateIp)
+		    {
+		        PRINTF_FUNC("Dispenser %d has been kick", dispenserIndex + 1);
+		    }
+		    else
+		    {
+                //timeout
+                PRINTF_FUNC("IP: %s, Socket %d connection timeout", (inet_ntoa(clientInfo.sin_addr)), index);
+		    }
 
 			if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectionInfo[index].Status == _CNS_DispenserMatched)
 			{
@@ -2644,6 +2652,8 @@ BOOL IsConflictIp(uint32_t ipAddress)
 	{
 		if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectionInfo[i].IpAddress == ipAddress)
 		{
+		    int dispenser = ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectionInfo[i].DispenserIndex;
+		    ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenser].Setting.bits.DuplicateIp = true;
 			return true;
 		}
 	}
@@ -2654,23 +2664,104 @@ void InitDispenserInfo(void)
 {
 	for(int i = 0; i < GENERAL_GUN_QUANTITY; i++)
 	{
+	    bool disconnect = false;
+
 		//memset(&ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i], 0x00, sizeof(struct ConnectorInfoData));
 	    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].RemoteStatus = 0;
 	    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].Enable = 0;
 	    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].ReadyToCharge = 0;
 	    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].ParentDispensetIndex = 0;
+	    disconnect = ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].Parameter.bits.Disconnection;
 	    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].Parameter.Value = 0;
+	    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].Parameter.bits.Disconnection = disconnect;
 	    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].AuthorizingType = 0;
 	    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].AuthorizingResult = 0;
 	    memset(&ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].WarningInfo, 0x00, sizeof(struct WARNING_CODE_INFO));
 
 	    //memset(&LastWarningInfo[i], 0x00, sizeof(struct WARNING_CODE_INFO));
 	}
-	memset(&ShmSysConfigAndInfo->SysInfo.DispenserInfo, 0x00, sizeof(struct DispenserInfoData));
+
+	//memset(&ShmSysConfigAndInfo->SysInfo.DispenserInfo, 0x00, sizeof(struct DispenserInfoData));
+	ShmSysConfigAndInfo->SysInfo.DispenserInfo.DispenserQuantity = 0;
+	ShmSysConfigAndInfo->SysInfo.DispenserInfo.TotalConnectorQuantity = 0;
+	for(int i = 0; i < GENERAL_GUN_QUANTITY; i++)
+	{
+	    unsigned char localStatus = 0;
+	    localStatus = ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].LocalStatus;
+        memset(&ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i], 0x00, sizeof(struct DispenserModule));
+        ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].LocalStatus = localStatus;
+	}
+	//ShmSysConfigAndInfo->SysInfo.DispenserInfo.CheckInLog.Status = 0;
+	//memset(ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectorLog, 0x00, sizeof(ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectorLog));
+	memset(ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectionInfo, 0x00, sizeof(ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectionInfo));
+	ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectorTimeout = 0;
+	ShmSysConfigAndInfo->SysInfo.DispenserInfo.DefaultPrice = 0;
 
 	ShmSysConfigAndInfo->SysInfo.DispenserInfo.Currency = ShmSysConfigAndInfo->SysConfig.BillingData.Currency;
 }
 
+struct timeval _IpConflicted_time[CONNECTOR_QUANTITY];
+
+void DispenserIpConflictedCheck(void)
+{
+    while(1)
+    {
+        for(int i = 0; i < CONNECTOR_QUANTITY; i++)
+        {
+            if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].Setting.bits.DuplicateIp)
+            {
+                if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].Setting.bits.DuplicateIpConfirm == NO)
+                {
+                    gettimeofday(&_IpConflicted_time[i], NULL);
+                    ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].Setting.bits.DuplicateIpConfirm = true;
+                    PRINTF_FUNC("Dispenser %d IP Conflicted Confirm", i + 1);
+                }
+                else
+                {
+                    if((GetTimeoutValue(_IpConflicted_time[i]) / mSEC_VAL) >= IP_CONFLICTED_TIME)
+                    {
+                        if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].LocalStatus != _DS_None &&
+                            ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].LocalStatus != _DS_Timeout)
+                        {
+                            unsigned char channel = ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].ConnectionChannel;
+
+                            if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectionInfo[channel].Status != _CNS_FREE)
+                            {
+                                PRINTF_FUNC("Dispenser %d IP Conflicted Stop", i + 1);
+                                DisableConnector(i);
+                                memset(&ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i], 0x00, sizeof(struct DispenserModule));
+                                ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].LocalStatus = _DS_Timeout;
+                                DispenserCheckInInfoUpdate();
+                                memset(&ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectionInfo[channel], 0x00, sizeof(struct ConnectionInfoData));
+                            }
+                        }
+                        ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].Setting.bits.DuplicateIp = false;
+                        ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[i].Setting.bits.DuplicateIpConfirm = false;
+                        PRINTF_FUNC("Dispenser %d IP Conflicted Clean", i + 1);
+                    }
+                }
+            }
+        }
+        usleep(500000);
+    }
+}
+
+void ForkIpConflictedCheck(void)
+{
+    pid_t forkId;
+
+    forkId = fork();
+    if(forkId == 0)
+    {
+        DispenserIpConflictedCheck();
+        return;
+    }
+    else if(forkId == -1)
+    {
+        PRINTF_FUNC("fork fail");
+    }
+}
+
 void tcpSocketServerStart(void)
 {
 	int sockFd = 0;
@@ -2702,6 +2793,9 @@ void tcpSocketServerStart(void)
 	PRINTF_FUNC("TCP Server Start");
 
 	signal(SIGCHLD, SIG_IGN);
+
+	ForkIpConflictedCheck();
+
 	// Main loop
 	while(1)
 	{
@@ -2756,6 +2850,7 @@ void tcpSocketServerStart(void)
 				// conflict ip address
 				PRINTF_FUNC("Conflict IP address, close socket");
 				close(clientSockFd);
+				memset(&ShmSysConfigAndInfo->SysInfo.DispenserInfo.ConnectionInfo[connectIndex], 0x00, sizeof(struct ConnectionInfoData));
 			}
 		}
 		else
@@ -2783,7 +2878,10 @@ int main(void)
 	}
 
 	// wait for self test completed
-	while(ShmSysConfigAndInfo->SysInfo.BootingStatus == BOOTTING);
+	while(ShmSysConfigAndInfo->SysInfo.BootingStatus == BOOTTING)
+	{
+	    sleep(1);
+	}
 
 	tcpSocketServerStart();
 

+ 1 - 0
EVSE/Projects/DO360/Apps/Module_EvComm.h

@@ -21,6 +21,7 @@
 #define DISPENSER_CONNECTOR_RESEND		3000		// 3s
 #define CABINET_STATUS_REQUEST_RESEND	1000		// 1s
 #define CHARGING_CAPABILITY_RESEND		1000		// 1s
+#define IP_CONFLICTED_TIME              1000        // 1s
 
 // socket setting
 #define	CONNECTION_LIMIT				5

+ 442 - 324
EVSE/Projects/DO360/Apps/Module_InternalComm.c

@@ -141,6 +141,7 @@ struct timeb 	_ac_endChargingTime;
 unsigned short _setFanSpeed = 0;
 float _beforeChargingTotalEnergy = 0.0;
 byte _checkLedChanged = 3;
+byte _RelaySelfTestOK;
 
 Ver ver;
 PresentInputVoltage inputVoltage;
@@ -280,14 +281,14 @@ void GetFwAndHwVersion_Fan()
 		strcpy((char *) ShmFanModuleData->version, ver.Version_FW);
 		// SystemInfo
 		strcpy((char *) ShmSysConfigAndInfo->SysInfo.FanModuleFwRev, ver.Version_FW);
-		PRINTF_FUNC("GetFwAndHwVersion_Fan s1 = %s \n", ver.Version_FW);
+		PRINTF_FUNC("GetFwAndHwVersion_Fan s1 = %s", ver.Version_FW);
 	}
 
 	if (Query_HW_Ver(Uart5Fd, Addr.Fan, &ver) == PASS)
 	{
 		// SystemInfo
 		strcpy((char *) ShmSysConfigAndInfo->SysInfo.FanModuleHwRev, ver.Version_FW);
-		PRINTF_FUNC("GetFwAndHwVersion_Fan s2 = %s \n", ver.Version_HW);
+		PRINTF_FUNC("GetFwAndHwVersion_Fan s2 = %s", ver.Version_HW);
 	}
 }
 
@@ -299,14 +300,14 @@ void GetFwAndHwVersion_Relay()
 		strcpy((char *) ShmRelayModuleData[0]->version, ver.Version_FW);
 		// SystemInfo
 		strcpy((char *) ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev, ver.Version_FW);
-		PRINTF_FUNC("GetFwAndHwVersion_RC1 s1 = %s \n", ver.Version_FW);
+		PRINTF_FUNC("GetFwAndHwVersion_RC1 s1 = %s", ver.Version_FW);
 	}
 
 	if (Query_HW_Ver(Uart5Fd, Addr.DO360_RC1, &ver) == PASS)
 	{
 		// SystemInfo
 		strcpy((char *) ShmSysConfigAndInfo->SysInfo.RelayModuleHwRev, ver.Version_FW);
-		PRINTF_FUNC("GetFwAndHwVersion_RC1 s2 = %s \n", ver.Version_HW);
+		PRINTF_FUNC("GetFwAndHwVersion_RC1 s2 = %s", ver.Version_HW);
 	}
 }
 
@@ -319,14 +320,14 @@ void GetFwAndHwVersion_Relay2()
 		strcpy((char *) ShmRelayModuleData[1]->version, ver.Version_FW);
 		// SystemInfo
 		strcpy((char *) ShmSysConfigAndInfo->SysInfo.Relay2ModuleFwRev, ver.Version_FW);
-		PRINTF_FUNC("GetFwAndHwVersion_RC2 s1 = %s \n", ver.Version_FW);
+		PRINTF_FUNC("GetFwAndHwVersion_RC2 s1 = %s", ver.Version_FW);
 	}
 
 	if (Query_HW_Ver(Uart5Fd, Addr.DO360_RC2, &ver) == PASS)
 	{
 		// SystemInfo
 		strcpy((char *) ShmSysConfigAndInfo->SysInfo.Relay2ModuleHwRev, ver.Version_FW);
-		PRINTF_FUNC("GetFwAndHwVersion_RC2 s2 = %s \n", ver.Version_HW);
+		PRINTF_FUNC("GetFwAndHwVersion_RC2 s2 = %s", ver.Version_HW);
 	}
 }
 
@@ -460,7 +461,7 @@ void SetModelName_Fan()
 {
 	if (Config_Model_Name(Uart5Fd, Addr.Fan, ShmSysConfigAndInfo->SysConfig.ModelName) == PASS)
 	{
-		PRINTF_FUNC("Set Model name PASS = %s \n", ShmSysConfigAndInfo->SysConfig.ModelName);
+		PRINTF_FUNC("Set Model name PASS = %s", ShmSysConfigAndInfo->SysConfig.ModelName);
 	}
 }
 
@@ -732,80 +733,48 @@ void GetPresentInputVol()
 // 左右槍的 Relay 前後的輸出電壓
 void GetPersentOutputVol()
 {
-	if (Query_Present_OutputVoltage(Uart5Fd, Addr.DO360_RC1, &outputVoltage) == PASS)
-	{
-//		PRINTF_FUNC("Conn1 fuse 1 = %f \n", outputVoltage.behindFuse_Voltage_C1);
-//		PRINTF_FUNC("Conn1 relay 1 = %f \n", outputVoltage.behindRelay_Voltage_C1);
-//		PRINTF_FUNC("Conn2 fuse 2 = %f \n", outputVoltage.behindFuse_Voltage_C2);
-//		PRINTF_FUNC("Conn2 relay 2 = %f \n", outputVoltage.behindRelay_Voltage_C2);
-
-		//PRINTF_FUNC("outputVoltage.behindFuse_Voltage_C1 = %f \n", outputVoltage.behindFuse_Voltage_C1);
-		//PRINTF_FUNC("outputVoltage.behindFuse_Voltage_C2 = %f \n", outputVoltage.behindFuse_Voltage_C2);
-
-		ShmRelayModuleData[0]->Gun1FuseOutputVolt = outputVoltage.behindFuse_Voltage_C1;
-		ShmRelayModuleData[0]->Gun1RelayOutputVolt = outputVoltage.behindRelay_Voltage_C1;
-		ShmRelayModuleData[0]->Gun2FuseOutputVolt = outputVoltage.behindFuse_Voltage_C2;
-		ShmRelayModuleData[0]->Gun2RelayOutputVolt = outputVoltage.behindRelay_Voltage_C2;
-
-		_chargingData[0]->FuseChargingVoltage = ShmRelayModuleData[0]->Gun1FuseOutputVolt;
-		_chargingData[0]->FireChargingVoltage = ShmRelayModuleData[0]->Gun1RelayOutputVolt;
-	}
-
-	// DO360 RC2
-	if (Query_Present_OutputVoltage(Uart5Fd, Addr.DO360_RC2, &outputVoltage) == PASS)
-	{
-//		PRINTF_FUNC("Conn1 fuse 1 = %f \n", outputVoltage.behindFuse_Voltage_C1);
-//		PRINTF_FUNC("Conn1 relay 1 = %f \n", outputVoltage.behindRelay_Voltage_C1);
-//		PRINTF_FUNC("Conn2 fuse 2 = %f \n", outputVoltage.behindFuse_Voltage_C2);
-//		PRINTF_FUNC("Conn2 relay 2 = %f \n", outputVoltage.behindRelay_Voltage_C2);
-
-		//PRINTF_FUNC("outputVoltage.behindFuse_Voltage_C1 = %f \n", outputVoltage.behindFuse_Voltage_C1);
-		//PRINTF_FUNC("outputVoltage.behindFuse_Voltage_C2 = %f \n", outputVoltage.behindFuse_Voltage_C2);
-
-		ShmRelayModuleData[1]->Gun1FuseOutputVolt = outputVoltage.behindFuse_Voltage_C1;
-		ShmRelayModuleData[1]->Gun1RelayOutputVolt = outputVoltage.behindRelay_Voltage_C1;
-		ShmRelayModuleData[1]->Gun2FuseOutputVolt = outputVoltage.behindFuse_Voltage_C2;
-		ShmRelayModuleData[1]->Gun2RelayOutputVolt = outputVoltage.behindRelay_Voltage_C2;
-
-		_chargingData[1]->FuseChargingVoltage = ShmRelayModuleData[1]->Gun2FuseOutputVolt;
-		_chargingData[1]->FireChargingVoltage = ShmRelayModuleData[1]->Gun2RelayOutputVolt;
-	}
-
-//		for (int index = 0; index < gunCount; index++)
-//		{
-//			if (index == 0)
-//			{
-//				if (_chargingData[index]->Evboard_id == 0x01)
-//				{
-//					_chargingData[index]->FuseChargingVoltage = ShmRelayModuleData->Gun1FuseOutputVolt;
-//					_chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun1RelayOutputVolt;
-//				}
-//				else if (_chargingData[index]->Evboard_id == 0x02)
-//				{
-//					_chargingData[index]->FuseChargingVoltage = ShmRelayModuleData->Gun2FuseOutputVolt;
-//					_chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun2RelayOutputVolt;
-//				}
-//			}
-//			else if (index == 1)
-//			{
-//				_chargingData[index]->FuseChargingVoltage = ShmRelayModuleData->Gun2FuseOutputVolt;
-//				_chargingData[index]->FireChargingVoltage = ShmRelayModuleData->Gun2RelayOutputVolt;
-//			}
-
-			//unsigned short Ovp = 0;
-			//unsigned short Ocp = 0;
-			//Ovp = MIN [VOUT_MAX_VOLTAGE, EV_BATTERY_VOLTAGE] 	// 最大輸出電壓與電池電壓最大值
-			//Ocp = MIN [IOUT_MAX_CURRENT, EV_CURRENT_REQ]		// 最大輸出電流與需求電流最小值
-//			if (_chargingData[index]->Type == _Type_Chademo)
-//			{
-				//Ovp = MaxValue(_chargingData[index]->MaximumChargingVoltage, _chargingData[index]->EvBatteryMaxVoltage);
-				//Ocp = MaxValue(_chargingData[index]->PresentChargingCurrent, ShmCHAdeMOData->ev[_chargingData[index]->type_index].ChargingCurrentRequest);
-//			}
-//			else if (_chargingData[index]->Type == _Type_CCS_2)
-//			{
-
-//			}
-//		}
+    if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+    {
+        // two relay board
+        if (Query_Present_OutputVoltage(Uart5Fd, Addr.DO360_RC1, &outputVoltage) == PASS)
+        {
+            ShmRelayModuleData[0]->Gun1FuseOutputVolt = outputVoltage.behindFuse_Voltage_C1;
+            ShmRelayModuleData[0]->Gun1RelayOutputVolt = outputVoltage.behindRelay_Voltage_C1;
+            ShmRelayModuleData[0]->Gun2FuseOutputVolt = outputVoltage.behindFuse_Voltage_C2;
+            ShmRelayModuleData[0]->Gun2RelayOutputVolt = outputVoltage.behindRelay_Voltage_C2;
+
+            _chargingData[0]->FuseChargingVoltage = ShmRelayModuleData[0]->Gun1FuseOutputVolt;
+            _chargingData[0]->FireChargingVoltage = ShmRelayModuleData[0]->Gun1RelayOutputVolt;
+        }
+
+        // DO360 RC2
+        if (Query_Present_OutputVoltage(Uart5Fd, Addr.DO360_RC2, &outputVoltage) == PASS)
+        {
+            ShmRelayModuleData[1]->Gun1FuseOutputVolt = outputVoltage.behindFuse_Voltage_C1;
+            ShmRelayModuleData[1]->Gun1RelayOutputVolt = outputVoltage.behindRelay_Voltage_C1;
+            ShmRelayModuleData[1]->Gun2FuseOutputVolt = outputVoltage.behindFuse_Voltage_C2;
+            ShmRelayModuleData[1]->Gun2RelayOutputVolt = outputVoltage.behindRelay_Voltage_C2;
+
+            _chargingData[1]->FuseChargingVoltage = ShmRelayModuleData[1]->Gun2FuseOutputVolt;
+            _chargingData[1]->FireChargingVoltage = ShmRelayModuleData[1]->Gun2RelayOutputVolt;
+        }
+    }
+    else
+    {
+        // only one relay board
+        if (Query_Present_OutputVoltage(Uart5Fd, Addr.DO360_RC1, &outputVoltage) == PASS)
+        {
+            ShmRelayModuleData[0]->Gun1FuseOutputVolt = outputVoltage.behindFuse_Voltage_C1;
+            ShmRelayModuleData[0]->Gun1RelayOutputVolt = outputVoltage.behindRelay_Voltage_C1;
+            ShmRelayModuleData[0]->Gun2FuseOutputVolt = outputVoltage.behindFuse_Voltage_C2;
+            ShmRelayModuleData[0]->Gun2RelayOutputVolt = outputVoltage.behindRelay_Voltage_C2;
+
+            _chargingData[0]->FuseChargingVoltage = ShmRelayModuleData[0]->Gun1FuseOutputVolt;
+            _chargingData[0]->FireChargingVoltage = ShmRelayModuleData[0]->Gun1RelayOutputVolt;
+            _chargingData[1]->FuseChargingVoltage = ShmRelayModuleData[0]->Gun2FuseOutputVolt;
+            _chargingData[1]->FireChargingVoltage = ShmRelayModuleData[0]->Gun2RelayOutputVolt;
+        }
+    }
 }
 
 // 風扇速度
@@ -839,28 +808,54 @@ void GetRelayOutputStatus()
 // 確認 K1 K2 relay 的狀態
 void CheckK1K2RelayOutput(byte index)
 {
-	if (index == 0)
-	{
-		if (regRelay[0].relay_event.bits.Gun1_N == YES && regRelay[0].relay_event.bits.Gun1_P == YES)
-			_chargingData[index]->RelayK1K2Status = YES;
-		else
-			_chargingData[index]->RelayK1K2Status = NO;
-	}
-	else if (index == 1)
-	{
-		if (regRelay[1].relay_event.bits.Gun2_N == YES && regRelay[1].relay_event.bits.Gun2_P == YES)
-			_chargingData[index]->RelayK1K2Status = YES;
-		else
-			_chargingData[index]->RelayK1K2Status = NO;
-	}
-
-	if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES && regRelay[0].relay_event.bits.Gun1_Parallel_P == YES &&
-		regRelay[0].relay_event.bits.Gun2_Parallel_N == YES && regRelay[0].relay_event.bits.Gun2_Parallel_P == YES &&
-		regRelay[1].relay_event.bits.Gun1_Parallel_N == YES && regRelay[1].relay_event.bits.Gun1_Parallel_P == YES)
-		ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus = YES;
-	else
-		ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus = NO;
-
+    if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+    {
+        // two relay board
+        if (index == 0)
+        {
+            if (regRelay[0].relay_event.bits.Gun1_N == YES && regRelay[0].relay_event.bits.Gun1_P == YES)
+                _chargingData[index]->RelayK1K2Status = YES;
+            else
+                _chargingData[index]->RelayK1K2Status = NO;
+        }
+        else if (index == 1)
+        {
+            if (regRelay[1].relay_event.bits.Gun2_N == YES && regRelay[1].relay_event.bits.Gun2_P == YES)
+                _chargingData[index]->RelayK1K2Status = YES;
+            else
+                _chargingData[index]->RelayK1K2Status = NO;
+        }
+
+        if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES && regRelay[0].relay_event.bits.Gun1_Parallel_P == YES &&
+            regRelay[0].relay_event.bits.Gun2_Parallel_N == YES && regRelay[0].relay_event.bits.Gun2_Parallel_P == YES &&
+            regRelay[1].relay_event.bits.Gun1_Parallel_N == YES && regRelay[1].relay_event.bits.Gun1_Parallel_P == YES)
+            ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus = YES;
+        else
+            ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus = NO;
+    }
+    else
+    {
+        // only one relay board
+        if (index == 0)
+        {
+            if (regRelay[0].relay_event.bits.Gun1_N == YES && regRelay[0].relay_event.bits.Gun1_P == YES)
+                _chargingData[index]->RelayK1K2Status = YES;
+            else
+                _chargingData[index]->RelayK1K2Status = NO;
+        }
+        else if (index == 1)
+        {
+            if (regRelay[0].relay_event.bits.Gun2_N == YES && regRelay[0].relay_event.bits.Gun2_P == YES)
+                _chargingData[index]->RelayK1K2Status = YES;
+            else
+                _chargingData[index]->RelayK1K2Status = NO;
+        }
+
+        if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES && regRelay[0].relay_event.bits.Gun1_Parallel_P == YES)
+            ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus = YES;
+        else
+            ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus = NO;
+    }
 //	PRINTF_FUNC("Check Relay Output. index = %d, RelayKPK2Status = %d, BridgeRelayStatus = %d \n",
 //			index, _chargingData[index]->RelayKPK2Status, ShmSysConfigAndInfo->SysInfo.BridgeRelayStatus);
 }
@@ -1051,51 +1046,86 @@ void SetFanModuleSpeed()
 //==========================================
 void SetK1K2RelayStatus(byte index)
 {
-	if (ShmPsuData->Work_Step >= _TEST_MODE && ShmPsuData->Work_Step <= _TEST_MODE)
-	{
-		if(regRelay[0].relay_event.bits.Gun1_N == NO)
-			outputRelay[0].relay_event.bits.Gun1_N = YES;
-		if (regRelay[0].relay_event.bits.Gun1_P == NO)
-			outputRelay[0].relay_event.bits.Gun1_P = YES;
-		return;
-	}
-
 	if (_chargingData[index]->SystemStatus < S_PREPARING_FOR_EVSE)
 	{
-		if(index == 0)
-		{
-			if(regRelay[0].relay_event.bits.Gun1_P == YES)
-				outputRelay[0].relay_event.bits.Gun1_P = NO;
-			if (regRelay[0].relay_event.bits.Gun1_N == YES)
-				outputRelay[0].relay_event.bits.Gun1_N = NO;
-		}
-		if(index == 1)
-		{
-			if(regRelay[1].relay_event.bits.Gun2_P == YES)
-				outputRelay[1].relay_event.bits.Gun2_P = NO;
-			if (regRelay[1].relay_event.bits.Gun2_N == YES)
-				outputRelay[1].relay_event.bits.Gun2_N = NO;
-		}
+	    if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+	    {
+	        // two relay board
+            if(index == 0)
+            {
+                if(regRelay[0].relay_event.bits.Gun1_P == YES)
+                    outputRelay[0].relay_event.bits.Gun1_P = NO;
+                if (regRelay[0].relay_event.bits.Gun1_N == YES)
+                    outputRelay[0].relay_event.bits.Gun1_N = NO;
+            }
+            if(index == 1)
+            {
+                if(regRelay[1].relay_event.bits.Gun2_P == YES)
+                    outputRelay[1].relay_event.bits.Gun2_P = NO;
+                if (regRelay[1].relay_event.bits.Gun2_N == YES)
+                    outputRelay[1].relay_event.bits.Gun2_N = NO;
+            }
+	    }
+	    else
+	    {
+	        // only one relay board
+            if(index == 0)
+            {
+                if(regRelay[0].relay_event.bits.Gun1_P == YES)
+                    outputRelay[0].relay_event.bits.Gun1_P = NO;
+                if (regRelay[0].relay_event.bits.Gun1_N == YES)
+                    outputRelay[0].relay_event.bits.Gun1_N = NO;
+            }
+            if(index == 1)
+            {
+                if(regRelay[0].relay_event.bits.Gun2_P == YES)
+                    outputRelay[0].relay_event.bits.Gun2_P = NO;
+                if (regRelay[0].relay_event.bits.Gun2_N == YES)
+                    outputRelay[0].relay_event.bits.Gun2_N = NO;
+            }
+	    }
 	}
 	else if ((_chargingData[index]->SystemStatus >= S_PREPARING_FOR_EVSE &&
 			_chargingData[index]->SystemStatus <= S_CHARGING))
 	{
 		if (_chargingData[index]->RelayWeldingCheck == YES)
 		{
-			if(index == 0)
-			{
-				if(regRelay[0].relay_event.bits.Gun1_N == NO)
-					outputRelay[0].relay_event.bits.Gun1_N = YES;
-				if (regRelay[0].relay_event.bits.Gun1_P == NO)
-					outputRelay[0].relay_event.bits.Gun1_P = YES;
-			}
-			if(index == 1)
-			{
-				if(regRelay[1].relay_event.bits.Gun2_N == NO)
-					outputRelay[1].relay_event.bits.Gun2_N = YES;
-				if (regRelay[1].relay_event.bits.Gun2_P == NO)
-					outputRelay[1].relay_event.bits.Gun2_P = YES;
-			}
+	        if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+	        {
+	            // two relay board
+                if(index == 0)
+                {
+                    if(regRelay[0].relay_event.bits.Gun1_N == NO)
+                        outputRelay[0].relay_event.bits.Gun1_N = YES;
+                    if (regRelay[0].relay_event.bits.Gun1_P == NO)
+                        outputRelay[0].relay_event.bits.Gun1_P = YES;
+                }
+                if(index == 1)
+                {
+                    if(regRelay[1].relay_event.bits.Gun2_N == NO)
+                        outputRelay[1].relay_event.bits.Gun2_N = YES;
+                    if (regRelay[1].relay_event.bits.Gun2_P == NO)
+                        outputRelay[1].relay_event.bits.Gun2_P = YES;
+                }
+	        }
+	        else
+	        {
+	            // only one relay board
+                if(index == 0)
+                {
+                    if(regRelay[0].relay_event.bits.Gun1_N == NO)
+                        outputRelay[0].relay_event.bits.Gun1_N = YES;
+                    if (regRelay[0].relay_event.bits.Gun1_P == NO)
+                        outputRelay[0].relay_event.bits.Gun1_P = YES;
+                }
+                if(index == 1)
+                {
+                    if(regRelay[0].relay_event.bits.Gun2_N == NO)
+                        outputRelay[0].relay_event.bits.Gun2_N = YES;
+                    if (regRelay[0].relay_event.bits.Gun2_P == NO)
+                        outputRelay[0].relay_event.bits.Gun2_P = YES;
+                }
+	        }
 		}
 	}
 	else if ((_chargingData[index]->SystemStatus >= S_TERMINATING &&
@@ -1104,20 +1134,42 @@ void SetK1K2RelayStatus(byte index)
 	{
 		if ((_chargingData[index]->PresentChargingCurrent * 10) <= SEFETY_SWITCH_RELAY_CUR)
 		{
-			if(index == 0)
-			{
-				if(regRelay[0].relay_event.bits.Gun1_P == YES)
-					outputRelay[0].relay_event.bits.Gun1_P = NO;
-				if (regRelay[0].relay_event.bits.Gun1_N == YES)
-					outputRelay[0].relay_event.bits.Gun1_N = NO;
-			}
-			if(index == 1)
-			{
-				if(regRelay[1].relay_event.bits.Gun2_P == YES)
-					outputRelay[1].relay_event.bits.Gun2_P = NO;
-				if (regRelay[1].relay_event.bits.Gun2_N == YES)
-					outputRelay[1].relay_event.bits.Gun2_N = NO;
-			}
+		    if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+		    {
+		        // two relay board
+                if(index == 0)
+                {
+                    if(regRelay[0].relay_event.bits.Gun1_P == YES)
+                        outputRelay[0].relay_event.bits.Gun1_P = NO;
+                    if (regRelay[0].relay_event.bits.Gun1_N == YES)
+                        outputRelay[0].relay_event.bits.Gun1_N = NO;
+                }
+                if(index == 1)
+                {
+                    if(regRelay[1].relay_event.bits.Gun2_P == YES)
+                        outputRelay[1].relay_event.bits.Gun2_P = NO;
+                    if (regRelay[1].relay_event.bits.Gun2_N == YES)
+                        outputRelay[1].relay_event.bits.Gun2_N = NO;
+                }
+		    }
+		    else
+		    {
+		        // only one relay board
+                if(index == 0)
+                {
+                    if(regRelay[0].relay_event.bits.Gun1_P == YES)
+                        outputRelay[0].relay_event.bits.Gun1_P = NO;
+                    if (regRelay[0].relay_event.bits.Gun1_N == YES)
+                        outputRelay[0].relay_event.bits.Gun1_N = NO;
+                }
+                if(index == 1)
+                {
+                    if(regRelay[0].relay_event.bits.Gun2_P == YES)
+                        outputRelay[0].relay_event.bits.Gun2_P = NO;
+                    if (regRelay[0].relay_event.bits.Gun2_N == YES)
+                        outputRelay[0].relay_event.bits.Gun2_N = NO;
+                }
+		    }
 		}
 	}
 	else if (_chargingData[index]->SystemStatus == S_CCS_PRECHARGE_ST0)
@@ -1139,19 +1191,32 @@ void SetParalleRelayStatus()
 				((_chargingData[0]->SystemStatus == S_IDLE || _chargingData[0]->SystemStatus == S_MAINTAIN || _chargingData[0]->SystemStatus == S_FAULT) &&
 				(_chargingData[1]->SystemStatus == S_IDLE || _chargingData[1]->SystemStatus == S_MAINTAIN || _chargingData[0]->SystemStatus == S_FAULT)))
 		{
-			// 初始化~ 不搭橋接
-			if (regRelay[0].relay_event.bits.Gun1_Parallel_P == YES)
-				outputRelay[0].relay_event.bits.Gun1_Parallel_P = NO;
-			if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES)
-				outputRelay[0].relay_event.bits.Gun1_Parallel_N = NO;
-			if (regRelay[0].relay_event.bits.Gun2_Parallel_P == YES)
-				outputRelay[0].relay_event.bits.Gun2_Parallel_P = NO;
-			if (regRelay[0].relay_event.bits.Gun2_Parallel_N == YES)
-				outputRelay[0].relay_event.bits.Gun2_Parallel_N = NO;
-			if (regRelay[1].relay_event.bits.Gun1_Parallel_P == YES)
-				outputRelay[1].relay_event.bits.Gun1_Parallel_P = NO;
-			if (regRelay[1].relay_event.bits.Gun1_Parallel_N == YES)
-				outputRelay[1].relay_event.bits.Gun1_Parallel_N = NO;
+		    if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+		    {
+		        // two relay board
+                // 初始化~ 不搭橋接
+                if (regRelay[0].relay_event.bits.Gun1_Parallel_P == YES)
+                    outputRelay[0].relay_event.bits.Gun1_Parallel_P = NO;
+                if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES)
+                    outputRelay[0].relay_event.bits.Gun1_Parallel_N = NO;
+                if (regRelay[0].relay_event.bits.Gun2_Parallel_P == YES)
+                    outputRelay[0].relay_event.bits.Gun2_Parallel_P = NO;
+                if (regRelay[0].relay_event.bits.Gun2_Parallel_N == YES)
+                    outputRelay[0].relay_event.bits.Gun2_Parallel_N = NO;
+                if (regRelay[1].relay_event.bits.Gun1_Parallel_P == YES)
+                    outputRelay[1].relay_event.bits.Gun1_Parallel_P = NO;
+                if (regRelay[1].relay_event.bits.Gun1_Parallel_N == YES)
+                    outputRelay[1].relay_event.bits.Gun1_Parallel_N = NO;
+		    }
+		    else
+		    {
+		        // only one relay board
+		        // 初始化~ 不搭橋接
+                if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES)
+                    outputRelay[0].relay_event.bits.Gun1_Parallel_N = NO;
+                if (regRelay[0].relay_event.bits.Gun2_Parallel_N == YES)
+                    outputRelay[0].relay_event.bits.Gun2_Parallel_N = NO;
+		    }
 		}
 		else
 		{
@@ -1163,70 +1228,122 @@ void SetParalleRelayStatus()
 				{
 					if (ShmSysConfigAndInfo->SysInfo.ReAssignedFlag < _REASSIGNED_RELAY_M_TO_A)
 					{
-						// 最大充 - 搭上橋接
-						if (regRelay[0].relay_event.bits.Gun1_Parallel_N == NO)
-							outputRelay[0].relay_event.bits.Gun1_Parallel_N = YES;
-						if (regRelay[0].relay_event.bits.Gun1_Parallel_P == NO)
-							outputRelay[0].relay_event.bits.Gun1_Parallel_P = YES;
-						if (regRelay[0].relay_event.bits.Gun2_Parallel_N == NO)
-							outputRelay[0].relay_event.bits.Gun2_Parallel_N = YES;
-						if (regRelay[0].relay_event.bits.Gun2_Parallel_P == NO)
-							outputRelay[0].relay_event.bits.Gun2_Parallel_P = YES;
-						if (regRelay[1].relay_event.bits.Gun1_Parallel_N == NO)
-							outputRelay[1].relay_event.bits.Gun1_Parallel_N = YES;
-						if (regRelay[1].relay_event.bits.Gun1_Parallel_P == NO)
-							outputRelay[1].relay_event.bits.Gun1_Parallel_P = YES;
+			            if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+			            {
+			                // two relay board
+                            // 最大充 - 搭上橋接
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_N == NO)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_N = YES;
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_P == NO)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_P = YES;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_N == NO)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_N = YES;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_P == NO)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_P = YES;
+                            if (regRelay[1].relay_event.bits.Gun1_Parallel_N == NO)
+                                outputRelay[1].relay_event.bits.Gun1_Parallel_N = YES;
+                            if (regRelay[1].relay_event.bits.Gun1_Parallel_P == NO)
+                                outputRelay[1].relay_event.bits.Gun1_Parallel_P = YES;
+			            }
+			            else
+			            {
+			                // only one relay board
+                            // 最大充 - 搭上橋接
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_N == NO)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_N = YES;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_N == NO)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_N = YES;
+			            }
 					}
 					else
 					{
-						// 平均充 - 不搭
-						if (regRelay[0].relay_event.bits.Gun1_Parallel_P == YES)
-							outputRelay[0].relay_event.bits.Gun1_Parallel_P = NO;
-						if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES)
-							outputRelay[0].relay_event.bits.Gun1_Parallel_N = NO;
-						if (regRelay[0].relay_event.bits.Gun2_Parallel_P == YES)
-							outputRelay[0].relay_event.bits.Gun2_Parallel_P = NO;
-						if (regRelay[0].relay_event.bits.Gun2_Parallel_N == YES)
-							outputRelay[0].relay_event.bits.Gun2_Parallel_N = NO;
-						if (regRelay[1].relay_event.bits.Gun1_Parallel_P == YES)
-							outputRelay[1].relay_event.bits.Gun1_Parallel_P = NO;
-						if (regRelay[1].relay_event.bits.Gun1_Parallel_N == YES)
-							outputRelay[1].relay_event.bits.Gun1_Parallel_N = NO;
+			            if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+			            {
+			                // two relay board
+                            // 平均充 - 不搭
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_P == YES)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_P = NO;
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_N = NO;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_P == YES)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_P = NO;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_N == YES)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_N = NO;
+                            if (regRelay[1].relay_event.bits.Gun1_Parallel_P == YES)
+                                outputRelay[1].relay_event.bits.Gun1_Parallel_P = NO;
+                            if (regRelay[1].relay_event.bits.Gun1_Parallel_N == YES)
+                                outputRelay[1].relay_event.bits.Gun1_Parallel_N = NO;
+			            }
+			            else
+			            {
+			                // only one relay board
+                            // 平均充 - 不搭
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_N = NO;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_N == YES)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_N = NO;
+			            }
 					}
 				}
 				else if (ShmSysConfigAndInfo->SysInfo.MainChargingMode == _MAIN_CHARGING_MODE_AVER)
 				{
 					if (ShmSysConfigAndInfo->SysInfo.ReAssignedFlag < _REASSIGNED_RELAY_A_TO_M)
 					{
-						// 平均充 - 不搭
-						if (regRelay[0].relay_event.bits.Gun1_Parallel_P == YES)
-							outputRelay[0].relay_event.bits.Gun1_Parallel_P = NO;
-						if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES)
-							outputRelay[0].relay_event.bits.Gun1_Parallel_N = NO;
-						if (regRelay[0].relay_event.bits.Gun2_Parallel_P == YES)
-							outputRelay[0].relay_event.bits.Gun2_Parallel_P = NO;
-						if (regRelay[0].relay_event.bits.Gun2_Parallel_N == YES)
-							outputRelay[0].relay_event.bits.Gun2_Parallel_N = NO;
-						if (regRelay[1].relay_event.bits.Gun1_Parallel_P == YES)
-							outputRelay[1].relay_event.bits.Gun1_Parallel_P = NO;
-						if (regRelay[1].relay_event.bits.Gun1_Parallel_N == YES)
-							outputRelay[1].relay_event.bits.Gun1_Parallel_N = NO;
+			            if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+			            {
+			                // two relay board
+                            // 平均充 - 不搭
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_P == YES)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_P = NO;
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_N = NO;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_P == YES)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_P = NO;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_N == YES)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_N = NO;
+                            if (regRelay[1].relay_event.bits.Gun1_Parallel_P == YES)
+                                outputRelay[1].relay_event.bits.Gun1_Parallel_P = NO;
+                            if (regRelay[1].relay_event.bits.Gun1_Parallel_N == YES)
+                                outputRelay[1].relay_event.bits.Gun1_Parallel_N = NO;
+			            }
+			            else
+			            {
+			                // only one relay board
+                            // 平均充 - 不搭
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_N == YES)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_N = NO;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_N == YES)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_N = NO;
+			            }
 					}
 					else
 					{
-						// 最大充 - 搭上橋接
-						if (regRelay[0].relay_event.bits.Gun1_Parallel_N == NO)
-							outputRelay[0].relay_event.bits.Gun1_Parallel_N = YES;
-						if (regRelay[0].relay_event.bits.Gun1_Parallel_P == NO)
-							outputRelay[0].relay_event.bits.Gun1_Parallel_P = YES;
-						if (regRelay[0].relay_event.bits.Gun2_Parallel_N == NO)
-							outputRelay[0].relay_event.bits.Gun2_Parallel_N = YES;
-						if (regRelay[0].relay_event.bits.Gun2_Parallel_P == NO)
-							outputRelay[0].relay_event.bits.Gun2_Parallel_P = YES;
-						if (regRelay[1].relay_event.bits.Gun1_Parallel_N == NO)
-							outputRelay[1].relay_event.bits.Gun1_Parallel_N = YES;
-						if (regRelay[1].relay_event.bits.Gun1_Parallel_P == NO)
-							outputRelay[1].relay_event.bits.Gun1_Parallel_P = YES;
+			            if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+			            {
+			                // two relay board
+                            // 最大充 - 搭上橋接
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_N == NO)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_N = YES;
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_P == NO)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_P = YES;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_N == NO)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_N = YES;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_P == NO)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_P = YES;
+                            if (regRelay[1].relay_event.bits.Gun1_Parallel_N == NO)
+                                outputRelay[1].relay_event.bits.Gun1_Parallel_N = YES;
+                            if (regRelay[1].relay_event.bits.Gun1_Parallel_P == NO)
+                                outputRelay[1].relay_event.bits.Gun1_Parallel_P = YES;
+			            }
+			            else
+			            {
+			                // only one relay board
+                            // 最大充 - 搭上橋接
+                            if (regRelay[0].relay_event.bits.Gun1_Parallel_N == NO)
+                                outputRelay[0].relay_event.bits.Gun1_Parallel_N = YES;
+                            if (regRelay[0].relay_event.bits.Gun2_Parallel_N == NO)
+                                outputRelay[0].relay_event.bits.Gun2_Parallel_N = YES;
+			            }
 					}
 				}
 			}
@@ -1234,6 +1351,32 @@ void SetParalleRelayStatus()
 	}
 }
 
+void SetAcContactorStatus(void)
+{
+    if(ShmChargerInfo->Control.RelayCtrl.bits.AcContactor == YES &&
+            ShmChargerInfo->Control.RelayCtrl.bits.AcContactorForceOff == NO)
+    {
+        outputRelay[0].relay_event.bits.AC_Contactor = YES;
+    }
+    else
+    {
+        outputRelay[0].relay_event.bits.AC_Contactor = NO;
+    }
+
+    if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+    {
+        if(ShmChargerInfo->Control.RelayCtrl.bits.AcContactor == YES &&
+            ShmChargerInfo->Control.RelayCtrl.bits.AcContactorForceOff == NO)
+        {
+            outputRelay[1].relay_event.bits.AC_Contactor = YES;
+        }
+        else
+        {
+            outputRelay[1].relay_event.bits.AC_Contactor = NO;
+        }
+    }
+}
+
 void CheckAlarmOccur()
 {
 	bool isErr = false;
@@ -2448,11 +2591,19 @@ int main(void)
 	memset(&outputRelay[0], 0x00, sizeof(Relay));
 	memset(&outputRelay[1], 0x00, sizeof(Relay));
 
-	if(Config_Relay_Output(Uart5Fd, Addr.DO360_RC1, &outputRelay[0]) != PASS)
-		PRINTF_FUNC("Config_Relay1_Output fail");
-	if(Config_Relay_Output(Uart5Fd, Addr.DO360_RC2, &outputRelay[1]) != PASS)
-		PRINTF_FUNC("Config_Relay2_Output fail");
+	if(ShmChargerInfo->Control.SysCtrl.bits.RelayBoardDisable == false)
+	{
+        if(Config_Relay_Output(Uart5Fd, Addr.DO360_RC1, &outputRelay[0]) != PASS)
+            PRINTF_FUNC("Config_Relay1_Output fail");
+
+        if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
+        {
+            if(Config_Relay_Output(Uart5Fd, Addr.DO360_RC2, &outputRelay[1]) != PASS)
+                PRINTF_FUNC("Config_Relay2_Output fail");
+        }
+	}
 
+	_RelaySelfTestOK = NO;
 	cur_led_color.Connect_1_Red = COLOR_MIN_LV;
 	cur_led_color.Connect_1_Green = COLOR_MIN_LV;
 	cur_led_color.Connect_1_Blue = COLOR_MIN_LV;
@@ -2466,55 +2617,72 @@ int main(void)
 	    if(!ShmSysConfigAndInfo->SysInfo.FirmwareUpdate)
 	    {
             // 程序開始之前~ 必須先確定 FW 版本與硬體版本,確認後!!~ 該模組才算是真正的 Initial Comp.
-            if (ShmRelayModuleData[0]->SelfTest_Comp == NO)
+            if(ShmChargerInfo->Control.SysCtrl.bits.RelayBoardDisable == false)
             {
-                // clena fw version
-                memset(ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev, 0x00, 32);
+                if (ShmRelayModuleData[0]->SelfTest_Comp == NO)
+                {
+                    // clena fw version
+                    memset(ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev, 0x00, 32);
 
-                GetFwAndHwVersion_Relay();
-                SetRtcData_Relay(0);
-                sleep(1);
+                    GetFwAndHwVersion_Relay();
+                    SetRtcData_Relay(0);
+                    sleep(1);
 
-                if(strlen((char *)ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev))
-                {
-                    ShmRelayModuleData[0]->SelfTest_Comp = YES;
+                    if(strlen((char *)ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev))
+                    {
+                        ShmRelayModuleData[0]->SelfTest_Comp = YES;
+
+                        if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable == false)
+                        {
+                            _RelaySelfTestOK = YES;
+                        }
+                    }
                 }
-            }
 
-            // DO360 RC2
-            if (ShmRelayModuleData[1]->SelfTest_Comp == NO)
-            {
-                // clena fw version
-                memset(ShmSysConfigAndInfo->SysInfo.Relay2ModuleFwRev, 0x00, 32);
+                // DO360 RC2
+                if (ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable == true &&
+                        ShmRelayModuleData[1]->SelfTest_Comp == NO)
+                {
+                    // clena fw version
+                    memset(ShmSysConfigAndInfo->SysInfo.Relay2ModuleFwRev, 0x00, 32);
 
-                GetFwAndHwVersion_Relay2();
-                SetRtcData_Relay(1);
-                sleep(1);
+                    GetFwAndHwVersion_Relay2();
+                    SetRtcData_Relay(1);
+                    sleep(1);
 
-                if (strlen((char *)ShmSysConfigAndInfo->SysInfo.Relay2ModuleFwRev) != 0)
-                {
-                    ShmRelayModuleData[1]->SelfTest_Comp = YES;
+                    if (strlen((char *)ShmSysConfigAndInfo->SysInfo.Relay2ModuleFwRev) != 0)
+                    {
+                        ShmRelayModuleData[1]->SelfTest_Comp = YES;
+
+                        if(ShmRelayModuleData[0]->SelfTest_Comp == YES)
+                        {
+                            _RelaySelfTestOK = YES;
+                        }
+                    }
                 }
             }
 
-            if (ShmFanModuleData->SelfTest_Comp == NO)
+            if(ShmChargerInfo->Control.SysCtrl.bits.FanBoardDisable == false)
             {
-                // clena fw version
-                memset(ShmSysConfigAndInfo->SysInfo.FanModuleFwRev, 0x00, 32);
+                if (ShmFanModuleData->SelfTest_Comp == NO)
+                {
+                    // clena fw version
+                    memset(ShmSysConfigAndInfo->SysInfo.FanModuleFwRev, 0x00, 32);
 
-                GetFwAndHwVersion_Fan();
-                SetModelName_Fan();
-                SetRtcData_Fan();
-                sleep(1);
-                gettimeofday(&_priority_time, NULL);
+                    GetFwAndHwVersion_Fan();
+                    SetModelName_Fan();
+                    SetRtcData_Fan();
+                    sleep(1);
+                    gettimeofday(&_priority_time, NULL);
 
-                if(strlen((char *)ShmSysConfigAndInfo->SysInfo.FanModuleFwRev) != 0)
-                {
-                    ShmFanModuleData->SelfTest_Comp = YES;
+                    if(strlen((char *)ShmSysConfigAndInfo->SysInfo.FanModuleFwRev) != 0)
+                    {
+                        ShmFanModuleData->SelfTest_Comp = YES;
+                    }
                 }
             }
 
-            if (ShmRelayModuleData[0]->SelfTest_Comp == YES && ShmRelayModuleData[1]->SelfTest_Comp == YES)
+            if(_RelaySelfTestOK == YES)
             {
                 // ==============優先權最高 10 ms ==============
                 // 輸出電壓
@@ -2523,20 +2691,6 @@ int main(void)
                 // 三相輸入電壓
                 GetPresentInputVol();
 
-                // 讀取當前 AC relay 狀態
-                if(regRelay[0].relay_event.bits.AC_Contactor != ShmSysConfigAndInfo->SysInfo.AcContactorStatus)
-                {
-                    PRINTF_FUNC("[%d]AC Contact Relay already %s", 0,
-                        ShmSysConfigAndInfo->SysInfo.AcContactorStatus == YES ? "On" : "Off");
-                }
-                if(regRelay[1].relay_event.bits.AC_Contactor != ShmSysConfigAndInfo->SysInfo.AcContactorStatus)
-                {
-                    PRINTF_FUNC("[%d]AC Contact Relay already %s", 1,
-                        ShmSysConfigAndInfo->SysInfo.AcContactorStatus == YES ? "On" : "Off");
-                }
-                regRelay[0].relay_event.bits.AC_Contactor = ShmSysConfigAndInfo->SysInfo.AcContactorStatus;
-                regRelay[1].relay_event.bits.AC_Contactor = ShmSysConfigAndInfo->SysInfo.AcContactorStatus;
-
                 //GetRelayOutputStatus();
 
                 for (int i = 0; i < gunCount; i++)
@@ -2587,29 +2741,14 @@ int main(void)
                 // 橋接 relay
                 SetParalleRelayStatus();
 
-                if(ShmChargerInfo->Control.RelayCtrl.bits.AcContactor == YES &&
-                        ShmChargerInfo->Control.RelayCtrl.bits.AcContactorForceOff == NO)
-                {
-                    outputRelay[0].relay_event.bits.AC_Contactor = YES;
-                    outputRelay[1].relay_event.bits.AC_Contactor = YES;
-                }
-                else
-                {
-                    outputRelay[0].relay_event.bits.AC_Contactor = NO;
-                    outputRelay[1].relay_event.bits.AC_Contactor = NO;
-                }
-
-                if (ShmPsuData->Work_Step >= _TEST_MODE && ShmPsuData->Work_Step <= _TEST_MODE)
-                {
-                    outputRelay[0].relay_event.bits.Gun1_N = outputRelay[0].relay_event.bits.Gun1_P = YES;
-                }
+                SetAcContactorStatus();
 
                 // 搭上/鬆開 Relay
                 if(IsNoneMatchRelayStatus(0))
                 {
                     if (Config_Relay_Output(Uart5Fd, Addr.DO360_RC1, &outputRelay[0]))
                     {
-                        //regRelay.relay_event.bits.AC_Contactor = ShmSysConfigAndInfo->SysInfo.AcContactorStatus;
+                        regRelay[0].relay_event.bits.AC_Contactor = outputRelay[0].relay_event.bits.AC_Contactor;
                         regRelay[0].relay_event.bits.CCS_Precharge = outputRelay[0].relay_event.bits.CCS_Precharge;
                         regRelay[0].relay_event.bits.Gun1_P = outputRelay[0].relay_event.bits.Gun1_P;
                         regRelay[0].relay_event.bits.Gun1_N = outputRelay[0].relay_event.bits.Gun1_N;
@@ -2619,48 +2758,27 @@ int main(void)
                         regRelay[0].relay_event.bits.Gun1_Parallel_N = outputRelay[0].relay_event.bits.Gun1_Parallel_N;
                         regRelay[0].relay_event.bits.Gun2_Parallel_P = outputRelay[0].relay_event.bits.Gun2_Parallel_P;
                         regRelay[0].relay_event.bits.Gun2_Parallel_N = outputRelay[0].relay_event.bits.Gun2_Parallel_N;
-
-//                        PRINTF_FUNC("Match Relay, AC = %x, g1_p = %x, g1_n = %x, g2_p = %x, g2_n = %x, pre = %x, bri_p = %x, bri_n = %x, bri2_p = %x, bri2_n = %x \n",
-//                                regRelay[0].relay_event.bits.AC_Contactor,
-//                                regRelay[0].relay_event.bits.Gun1_P,
-//                                regRelay[0].relay_event.bits.Gun1_N,
-//                                regRelay[0].relay_event.bits.Gun2_P,
-//                                regRelay[0].relay_event.bits.Gun2_N,
-//                                regRelay[0].relay_event.bits.CCS_Precharge,
-//                                regRelay[0].relay_event.bits.Gun1_Parallel_P,
-//                                regRelay[0].relay_event.bits.Gun1_Parallel_N,
-//                                regRelay[0].relay_event.bits.Gun2_Parallel_P,
-//                                regRelay[0].relay_event.bits.Gun2_Parallel_N);
                     }
                 }
 
-                // 搭上/鬆開 Relay
-                if(IsNoneMatchRelayStatus(1))
+                if(ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable)
                 {
-                    if (Config_Relay_Output(Uart5Fd, Addr.DO360_RC2, &outputRelay[1]))
+                    // 搭上/鬆開 Relay
+                    if(IsNoneMatchRelayStatus(1))
                     {
-                        //regRelay.relay_event.bits.AC_Contactor = ShmSysConfigAndInfo->SysInfo.AcContactorStatus;
-                        regRelay[1].relay_event.bits.CCS_Precharge = outputRelay[1].relay_event.bits.CCS_Precharge;
-                        regRelay[1].relay_event.bits.Gun1_P = outputRelay[1].relay_event.bits.Gun1_P;
-                        regRelay[1].relay_event.bits.Gun1_N = outputRelay[1].relay_event.bits.Gun1_N;
-                        regRelay[1].relay_event.bits.Gun2_P = outputRelay[1].relay_event.bits.Gun2_P;
-                        regRelay[1].relay_event.bits.Gun2_N = outputRelay[1].relay_event.bits.Gun2_N;
-                        regRelay[1].relay_event.bits.Gun1_Parallel_P = outputRelay[1].relay_event.bits.Gun1_Parallel_P;
-                        regRelay[1].relay_event.bits.Gun1_Parallel_N = outputRelay[1].relay_event.bits.Gun1_Parallel_N;
-                        regRelay[1].relay_event.bits.Gun2_Parallel_P = outputRelay[1].relay_event.bits.Gun2_Parallel_P;
-                        regRelay[1].relay_event.bits.Gun2_Parallel_N = outputRelay[1].relay_event.bits.Gun2_Parallel_N;
-
-//                        PRINTF_FUNC("Match Relay2, AC = %x, g1_p = %x, g1_n = %x, g2_p = %x, g2_n = %x, pre = %x, bri_p = %x, bri_n = %x, bri2_p = %x, bri2_n = %x \n",
-//                                regRelay[1].relay_event.bits.AC_Contactor,
-//                                regRelay[1].relay_event.bits.Gun1_P,
-//                                regRelay[1].relay_event.bits.Gun1_N,
-//                                regRelay[1].relay_event.bits.Gun2_P,
-//                                regRelay[1].relay_event.bits.Gun2_N,
-//                                regRelay[1].relay_event.bits.CCS_Precharge,
-//                                regRelay[1].relay_event.bits.Gun1_Parallel_P,
-//                                regRelay[1].relay_event.bits.Gun1_Parallel_N,
-//                                regRelay[1].relay_event.bits.Gun2_Parallel_P,
-//                                regRelay[1].relay_event.bits.Gun2_Parallel_N);
+                        if (Config_Relay_Output(Uart5Fd, Addr.DO360_RC2, &outputRelay[1]))
+                        {
+                            regRelay[1].relay_event.bits.AC_Contactor = outputRelay[1].relay_event.bits.AC_Contactor;
+                            regRelay[1].relay_event.bits.CCS_Precharge = outputRelay[1].relay_event.bits.CCS_Precharge;
+                            regRelay[1].relay_event.bits.Gun1_P = outputRelay[1].relay_event.bits.Gun1_P;
+                            regRelay[1].relay_event.bits.Gun1_N = outputRelay[1].relay_event.bits.Gun1_N;
+                            regRelay[1].relay_event.bits.Gun2_P = outputRelay[1].relay_event.bits.Gun2_P;
+                            regRelay[1].relay_event.bits.Gun2_N = outputRelay[1].relay_event.bits.Gun2_N;
+                            regRelay[1].relay_event.bits.Gun1_Parallel_P = outputRelay[1].relay_event.bits.Gun1_Parallel_P;
+                            regRelay[1].relay_event.bits.Gun1_Parallel_N = outputRelay[1].relay_event.bits.Gun1_Parallel_N;
+                            regRelay[1].relay_event.bits.Gun2_Parallel_P = outputRelay[1].relay_event.bits.Gun2_Parallel_P;
+                            regRelay[1].relay_event.bits.Gun2_Parallel_N = outputRelay[1].relay_event.bits.Gun2_Parallel_N;
+                        }
                     }
                 }
             }

+ 34 - 10
EVSE/Projects/DO360/Apps/Module_PrimaryComm.c

@@ -30,6 +30,7 @@
 #include	"../../define.h"
 #include	"PrimaryComm.h"
 #include 	<stdbool.h>
+#include    "Config.h"
 
 #define ARRAY_SIZE(A)		(sizeof(A) / sizeof(A[0]))
 #define PASS				1
@@ -44,6 +45,7 @@ typedef unsigned char 		byte;
 struct SysConfigAndInfo			*ShmSysConfigAndInfo;
 struct StatusCodeData 			*ShmStatusCodeData;
 struct PrimaryMcuData			*ShmPrimaryMcuData;
+ChargerInfoData                 *ShmChargerInfo;
 
 void trim(char *s);
 int mystrcmp(char *p1,char *p2);
@@ -214,10 +216,10 @@ int InitShareMemory()
     	DEBUG_ERROR("shmat ShmSysConfigAndInfo NG\n");
 		#endif
     	result = FAIL;
-   	 }
+   	}
 
-   	 //creat ShmStatusCodeData
-   	 if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0)
+   	//creat ShmStatusCodeData
+ 	if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0)
     {
 		#ifdef SystemLogMessage
    		DEBUG_ERROR("shmget ShmStatusCodeData NG\n");
@@ -242,12 +244,27 @@ int InitShareMemory()
 	}
 	else if ((ShmPrimaryMcuData = shmat(MeterSMId, NULL, 0)) == (void *) -1)
 	{
-		#ifdef ShmPrimaryMcuData
+		#ifdef SystemLogMessage
 		DEBUG_ERROR("shmat ShmPrimaryMcuData NG\n");
 		#endif
 		result = FAIL;
 	}
 
+    if((MeterSMId = shmget(SM_ChargerInfoKey, sizeof(ChargerInfoData), 0777)) < 0)
+    {
+        #ifdef SystemLogMessage
+        DEBUG_ERROR("shmat ChargerInfoData NG \n");
+        #endif
+        result = FAIL;
+    }
+    else if((ShmChargerInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1)
+    {
+        #ifdef SystemLogMessage
+        DEBUG_ERROR("shmat ChargerInfoData NG \n");
+        #endif
+        result = FAIL;
+    }
+
     return result;
 }
 
@@ -285,15 +302,15 @@ void GetInputGpioStatus()
 				_acStatus = gpio_in.AC_Connector;
 
 				// DO360 AC_Connector Status is the inverse of DS's
-				if(gpio_in.AC_Connector)
+				if(ShmChargerInfo->Control.PrimaryCtrl.bits.AcContactorReverse)
 				{
-					ShmSysConfigAndInfo->SysInfo.AcContactorStatus = 0;
-					ShmPrimaryMcuData->InputDet.bits.AcContactorDetec = 0;
+				    ShmSysConfigAndInfo->SysInfo.AcContactorStatus = gpio_in.AC_Connector ? 0 : 1;
+				    ShmPrimaryMcuData->InputDet.bits.AcContactorDetec = gpio_in.AC_Connector ? 0 : 1;
 				}
 				else
 				{
-					ShmSysConfigAndInfo->SysInfo.AcContactorStatus = 1;
-					ShmPrimaryMcuData->InputDet.bits.AcContactorDetec = 1;
+                    ShmSysConfigAndInfo->SysInfo.AcContactorStatus = gpio_in.AC_Connector ? 1 : 0;
+                    ShmPrimaryMcuData->InputDet.bits.AcContactorDetec = gpio_in.AC_Connector ? 1 : 0;
 				}
 				PRINTF_FUNC("Ac Contactor Status %s", ShmPrimaryMcuData->InputDet.bits.AcContactorDetec > 0 ? "On" : "Off");
 			}
@@ -307,7 +324,14 @@ void GetInputGpioStatus()
 		ShmPrimaryMcuData->InputDet.bits.SpdDetec = gpio_in.SPD;
 
 		// DO360 Door Status is the inverse of DS's
-		ShmPrimaryMcuData->InputDet.bits.DoorOpen = gpio_in.Door_Open > 0 ? 0 : 1;
+		if(ShmChargerInfo->Control.PrimaryCtrl.bits.DoorSensorReverse)
+		{
+		    ShmPrimaryMcuData->InputDet.bits.DoorOpen = gpio_in.Door_Open ? 0 : 1;
+		}
+		else
+		{
+		    ShmPrimaryMcuData->InputDet.bits.DoorOpen = gpio_in.Door_Open ? 1 : 0;
+		}
 
 		// Bypass door open
 		//ShmPrimaryMcuData->InputDet.bits.DoorOpen = 0;

+ 464 - 25
EVSE/Projects/DO360/Apps/ReadCmdline.c

@@ -38,6 +38,7 @@
 #include 	<stdbool.h>
 #include 	"../../define.h"
 #include    "Config.h"
+#include    "Module_EvComm.h"
 
 typedef unsigned char			byte;
 #define PASS				1
@@ -60,6 +61,10 @@ struct FanModuleData			*ShmFanModuleData;
 struct RelayModuleData			*ShmRelayModuleData;
 struct LedModuleData			*ShmLedModuleData;
 struct PsuData 					*ShmPsuData;
+ChargerInfoData                 *ShmChargerInfo;
+PsuPositionInfoData             *ShmPsuPosition;
+PsuGroupingInfoData             *ShmPsuGrouping;
+PsuGroupCollectionData          *GroupCollection;
 
 struct ChargingInfoData 		*_chargingData[CONNECTOR_QUANTITY];
 struct ChargingInfoData 		*ac_chargingInfo[AC_QUANTITY];
@@ -226,6 +231,21 @@ int InitShareMemory()
    		result = FAIL;
    	}
 
+    if ((MeterSMId = shmget(SM_ChargerInfoKey, sizeof(ChargerInfoData),  IPC_CREAT | 0777)) < 0)
+    {
+        result = FAIL;
+    }
+    else if ((ShmChargerInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1)
+    {
+        result = FAIL;
+    }
+    if(result == PASS)
+    {
+        ShmPsuPosition = &ShmChargerInfo->PsuPosition;
+        ShmPsuGrouping = &ShmChargerInfo->PsuGrouping;
+        GroupCollection = &ShmChargerInfo->PsuGrouping.GroupCollection[0];
+    }
+
     return result;
 }
 
@@ -266,6 +286,11 @@ void RunStatusProc(char *v1, char *v2)
 			printf ("index = %x, status = %x (%d)\n", _index, _chargingData[_index]->SystemStatus, _chargingData[_index]->IsAvailable);
 			printf ("SystemTimeoutFlag = %d, PageIndex = %d\n",
 					ShmSysConfigAndInfo->SysInfo.SystemTimeoutFlag, ShmSysConfigAndInfo->SysInfo.PageIndex);
+			printf("ConnectorAlarmCode = %s \n", _chargingData[_index]->ConnectorAlarmCode);
+			printf("EvConnAlarmCode = %s \n", _chargingData[_index]->EvConnAlarmCode);
+			printf("RemotenAlarmCode = %s \n", ShmSysConfigAndInfo->SysInfo.ConnectorInfo[_index].RemotenAlarmCode);
+			//printf("ConnectorAlarmCode = %s \n", ShmOCPP16Data->StatusNotification[index].ErrorCode);
+			//printf("ConnectorAlarmCode = %s \n", ShmOCPP16Data->StatusNotification[index].VendorErrorCode);
 		}
 		else
 		{
@@ -387,22 +412,41 @@ void RunSelfProc()
 
 void GetFwVerProc(char *v1)
 {
-	if (strcmp(v1, "407") == 0)
-	{
-		printf("407 FW Version = %s \n", ShmPrimaryMcuData->version);
-	}
-	else if (strcmp(v1, "0") == 0 || strcmp(v1, "1") == 0)
-	{
-		int _index = atoi(v1);
-
-		if (_index == 0)
-			printf("Gun 0 FW Version = %s \n", ShmSysConfigAndInfo->SysInfo.Connector1FwRev);
-		else if (_index == 1)
-			printf("Gun 1 FW Version = %s \n", ShmSysConfigAndInfo->SysInfo.Connector2FwRev);
+    if (strcmp(v1, "407") == 0)
+    {
+        printf("407 FW Version = %s \n", ShmPrimaryMcuData->version);
+    }
+    else if (strcmp(v1, "0") == 0 || strcmp(v1, "1") == 0 || strcmp(v1, "2") == 0 || strcmp(v1, "3") == 0)
+    {
+        int _index = atoi(v1);
+
+        if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[_index].Enable)
+        {
+            int dispenser = ShmSysConfigAndInfo->SysInfo.ConnectorInfo[_index].ParentDispensetIndex;
+            int ParentIndex = ShmSysConfigAndInfo->SysInfo.ConnectorInfo[_index].GeneralChargingData.Index;
+
+            if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenser].LocalStatus != _DS_None &&
+                ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenser].LocalStatus != _DS_Timeout)
+            {
+                if(ParentIndex == 0)
+                {
+                    printf("Gun %d FW Version = %s \n", _index, ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenser].Connector1FwRev);
+                }
+                else
+                {
+                    printf("Gun %d FW Version = %s \n", _index, ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenser].Connector1FwRev);
+                }
+            }
+        }
+        else
+        {
+            printf("Gun %d is disable\r\n", _index);
+        }
 	}
 	else if (strcmp(v1, "rb") == 0)
 	{
-		printf("RB Version = %s \n", ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev);
+        printf("RB1 Version = %s \n", ShmSysConfigAndInfo->SysInfo.RelayModuleFwRev);
+        printf("RB2 Version = %s \n", ShmSysConfigAndInfo->SysInfo.Relay2ModuleFwRev);
 	}
 	else if (strcmp(v1, "fan") == 0)
 	{
@@ -416,14 +460,6 @@ void GetFwVerProc(char *v1)
 	{
 		printf("LED Version = %s \n", ShmSysConfigAndInfo->SysInfo.LedModuleFwRev);
 	}
-	else if (strcmp(v1, "ac") == 0)
-	{
-		if (!FindAcChargingInfoData(0, &ac_chargingInfo[0]))
-		{
-			printf("FindChargingInfoData (AC) false \n");
-		}
-		printf("AC Version = %s \n", ac_chargingInfo[0]->version);
-	}
 }
 
 void CreateOneError(char *v1)
@@ -461,10 +497,34 @@ void FwUpdateFlagProc()
 
 void CheckAcStatus(char *v1)
 {
-	if (strcmp(v1, "-1") == 0|| strcmp(v1, "") == 0)
-	{
-		printf("AC Status = %d \n", ShmSysConfigAndInfo->SysInfo.AcContactorStatus);
-	}
+    if (strcmp(v1, "-1") == 0 || strcmp(v1, "") == 0)
+    {
+        printf("  AC Contactor Ctrl: %d\r\n", ShmChargerInfo->Control.RelayCtrl.bits.AcContactor);
+        printf(" AC ForceOff Status: %d\r\n", ShmChargerInfo->Control.RelayCtrl.bits.AcContactorForceOff);
+        printf("AC Contactor Status: %d\r\n", ShmSysConfigAndInfo->SysInfo.AcContactorStatus);
+    }
+    else
+    {
+        if(system("pidof -s main > /dev/null") != 0)
+        {
+            if(strcmp(v1, "0") == 0)
+            {
+                ShmChargerInfo->Control.RelayCtrl.bits.AcContactor = false;
+                ShmChargerInfo->Control.RelayCtrl.bits.AcContactorForceOff = false;
+                printf("Set AC Contactor Off\r\n");
+            }
+            else
+            {
+                ShmChargerInfo->Control.RelayCtrl.bits.AcContactor = true;
+                ShmChargerInfo->Control.RelayCtrl.bits.AcContactorForceOff = false;
+                printf("Set AC Contactor On\r\n");
+            }
+        }
+        else
+        {
+            printf("main task is running\r\n");
+        }
+    }
 }
 
 void SetCableChkStatus(char *v1, char *v2)
@@ -1014,6 +1074,362 @@ void RunUnconditionalChargeIndex1(char *v1, char *v2, char *v3)
     }
 }
 
+void SetGroupRoleIdle(byte group)
+{
+    if(group < GENERAL_GUN_QUANTITY)
+    {
+        GroupCollection[group].Role = _GROLE_IDLE;
+        GroupCollection[group].GroupMemberQuantity = 0;
+        memset(GroupCollection[group].GroupMember, 0x00, ARRAY_SIZE(GroupCollection[group].GroupMember));
+        GroupCollection[group].TargetGroup = 0;
+        //printf("\r\n Reset %02X To Idle", group);
+    }
+}
+
+void SetGroupRoleMaster(byte group)
+{
+    if(group < GENERAL_GUN_QUANTITY)
+    {
+        GroupCollection[group].Role = _GROLE_MASTER;
+        //printf("\r\n Set %02X As Master", group);
+    }
+}
+
+void AddGroupCollection(byte group, byte target)
+{
+    if(group < GENERAL_GUN_QUANTITY && target < GENERAL_GUN_QUANTITY)
+    {
+        GroupCollection[target].GroupMember[GroupCollection[target].GroupMemberQuantity++] = group;
+        //printf("\r\n Add %02X To Group %02X (Quantity %d)", group, target, GroupCollection[target].GroupMemberQuantity);
+    }
+}
+
+void RemoveGroupCollection(byte group, byte target)
+{
+    int location = 0, slave = 0;
+    bool find = false;
+
+    if(group < GENERAL_GUN_QUANTITY && target < GENERAL_GUN_QUANTITY)
+    {
+        for(int i = 0; i < GroupCollection[target].GroupMemberQuantity; i++)
+        {
+            if(group == GroupCollection[target].GroupMember[i])
+            {
+                GroupCollection[target].GroupMember[i] = 0;
+                location = i;
+                find = true;
+                break;
+            }
+        }
+        if(find)
+        {
+            for(int i = location + 1; i < GroupCollection[target].GroupMemberQuantity; i++)
+            {
+                slave = GroupCollection[target].GroupMember[i];
+                GroupCollection[target].GroupMember[i] = 0;
+                GroupCollection[target].GroupMember[i - 1] = slave;
+            }
+            GroupCollection[target].GroupMemberQuantity--;
+            //printf("\r\n Remove %02X From Group %02X (Quantity %d)", group, target, GroupCollection[target].GroupMemberQuantity);
+        }
+    }
+}
+
+void SetGroupRoleSlave(byte group, byte target)
+{
+    if(group < GENERAL_GUN_QUANTITY && target < GENERAL_GUN_QUANTITY)
+    {
+        GroupCollection[group].Role = _GROLE_SLAVE;
+        GroupCollection[group].TargetGroup = target + 1;
+        //printf("\r\n Set %02X As Slave", group);
+    }
+}
+
+void ShowGroupingInfo(void)
+{
+    byte target = 0;
+
+    for(int i = 0; i < 4; i++)
+    {
+        target = ShmPsuGrouping->Layout[i];
+
+        printf("\r\nGun[%d] Role: %d, Quantity: %d", target + 1, GroupCollection[target].Role, GroupCollection[target].GroupMemberQuantity);
+        if(GroupCollection[target].Role == 1)
+        {
+            printf(",");
+            printf(" %02X", target);
+            for(int j = 0; j < GroupCollection[target].GroupMemberQuantity; j++)
+            {
+                printf(" %02X", GroupCollection[target].GroupMember[j]);
+            }
+        }
+        if(GroupCollection[target].Role == 2)
+        {
+            printf(", TargetGroup: %02X", GroupCollection[target].TargetGroup - 1);
+        }
+    }
+    printf("\r\n\r\n");
+}
+
+void PsuGroupSwitchToIdle(byte group)
+{
+    int master = 0, quantity = 0, location = 0, total = 0;
+
+    if(GroupCollection[group].Role != _GROLE_SLAVE)
+    {
+        return;
+    }
+
+    master = GroupCollection[group].TargetGroup - 1;
+    quantity = GroupCollection[master].GroupMemberQuantity;
+    //printf("\r\n Search %02X From Group %02X", group, master);
+    for(int i = 0; i < quantity; i++)
+    {
+        if(total == 0)
+        {
+            if(group == GroupCollection[master].GroupMember[i])
+            {
+                location = i;
+                total++;
+                //printf("\r\n Find Switch Idle Start Location %d", location);
+            }
+        }
+        else
+        {
+            // find other group in the same direction
+            if(ShmPsuGrouping->Location[GroupCollection[master].GroupMember[location]] < ShmPsuGrouping->Location[master])
+            {
+                if(ShmPsuGrouping->Location[GroupCollection[master].GroupMember[i]] < ShmPsuGrouping->Location[master])
+                {
+                    total++;
+                }
+            }
+            if(ShmPsuGrouping->Location[GroupCollection[master].GroupMember[location]] > ShmPsuGrouping->Location[master])
+            {
+                if(ShmPsuGrouping->Location[GroupCollection[master].GroupMember[i]] > ShmPsuGrouping->Location[master])
+                {
+                    total++;
+                }
+            }
+        }
+    }
+
+    if(total > 0)
+    {
+        unsigned char collection[GENERAL_GUN_QUANTITY];
+        //printf("\r\n Total Switch Idle %d:", total);
+        memcpy(collection, GroupCollection[master].GroupMember, ARRAY_SIZE(GroupCollection[master].GroupMember));
+
+        for(int i = 0; i < total; i++)
+        {
+            //printf(" %02X", collection[i + location]);
+        }
+        for(int i = 0; i < total; i++)
+        {
+            RemoveGroupCollection(collection[i + location], master);
+            SetGroupRoleIdle(collection[i + location]);
+        }
+    }
+}
+
+int GetPsuGroupAvailable(byte group)
+{
+    int slave = 0, location = 0, available = 0;
+
+    // search from left
+    location = GroupCollection[group].Location - 1;
+    for(int i = location; i >= 0; i--)
+    {
+        slave = ShmPsuGrouping->Layout[i];
+        if(GroupCollection[slave].Role == _GROLE_IDLE)
+        {
+            available++;
+        }
+        else
+        {
+            break;
+        }
+    }
+    // search from right
+    location = GroupCollection[group].Location + 1;
+    for(int i = location; i < 4; i++)
+    {
+        slave = ShmPsuGrouping->Layout[i];
+        if(GroupCollection[slave].Role == _GROLE_IDLE)
+        {
+            available++;
+        }
+        else
+        {
+            break;
+        }
+    }
+    return available;
+}
+
+void PsuGroupShareCheck(byte group)
+{
+    int slave = 0, target = 0, location = 0, share = 0, total = 0;
+
+    total = GetPsuGroupAvailable(group) + 1;
+    //printf("\r\n Group %02X Available Quantity %d", group, total);
+
+    // search from left
+    location = GroupCollection[group].Location - 1;
+    for(int i = location; i >= 0; i--)
+    {
+        slave = ShmPsuGrouping->Layout[i];
+        if(GroupCollection[slave].Role == _GROLE_SLAVE)
+        {
+            target = GroupCollection[slave].TargetGroup - 1;
+            if(GroupCollection[target].GroupMemberQuantity > total)
+            {
+                share = (GroupCollection[target].GroupMemberQuantity + 1 - total) / 2;
+            }
+            //printf("\r\n Find Group %02X Have %d Resource Can Shared %d From Left", target, GroupCollection[target].GroupMemberQuantity, share);
+            break;
+        }
+        else
+        {
+            break;
+        }
+    }
+
+    if(share == 0)
+    {
+        // search from right
+        location = GroupCollection[group].Location + 1;
+        for(int i = location; i < 4; i++)
+        {
+            slave = ShmPsuGrouping->Layout[i];
+            if(GroupCollection[slave].Role == _GROLE_SLAVE)
+            {
+                target = GroupCollection[slave].TargetGroup - 1;
+                if(GroupCollection[target].GroupMemberQuantity > total)
+                {
+                    share = (GroupCollection[target].GroupMemberQuantity + 1 - total) / 2;
+                }
+                //printf("\r\n Find Group %02X Have %d Resource Can Shared %d From Left", target, GroupCollection[target].GroupMemberQuantity, share);
+                break;
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+
+    if(share > 0)
+    {
+        //printf("\r\n Grab %02X", slave);
+        PsuGroupSwitchToIdle(slave);
+    }
+    else
+    {
+        //printf("\r\n No Need To Grab");
+    }
+}
+
+void PsuGroupStartCharging(byte group)
+{
+    int slave = 0, location = 0;
+
+    if(GroupCollection[group].Role != _GROLE_IDLE)
+    {
+        return;
+    }
+
+    PsuGroupShareCheck(group);
+
+    // search from left
+    location = GroupCollection[group].Location - 1;
+    for(int i = location; i >= 0; i--)
+    {
+        slave = ShmPsuGrouping->Layout[i];
+        if(GroupCollection[slave].Role == _GROLE_IDLE)
+        {
+            //printf("\r\n Find %02X From Left", slave);
+            SetGroupRoleSlave(slave, group);
+            AddGroupCollection(slave, group);
+        }
+        else
+        {
+            break;
+        }
+    }
+
+    // search from right
+    location = GroupCollection[group].Location + 1;
+    for(int i = location; i < 4; i++)
+    {
+        slave = ShmPsuGrouping->Layout[i];
+        if(GroupCollection[slave].Role == _GROLE_IDLE)
+        {
+            //printf("\r\n Find %02X From Right", slave);
+            SetGroupRoleSlave(slave, group);
+            AddGroupCollection(slave, group);
+        }
+        else
+        {
+            break;
+        }
+    }
+    SetGroupRoleMaster(group);
+}
+
+void PsuGroupStopCharging(byte group)
+{
+    if(GroupCollection[group].Role != _GROLE_MASTER)
+    {
+        return;
+    }
+
+    for(int i = 0; i < GroupCollection[group].GroupMemberQuantity; i++)
+    {
+        SetGroupRoleIdle(GroupCollection[group].GroupMember[i]);
+    }
+    SetGroupRoleIdle(group);
+}
+
+void RunPsuGrouping(char *v1, char *v2)
+{
+    int group = 0, charging = 0;
+
+    group = atoi(v1);
+    charging = atoi(v2);
+
+    group -= 1;
+
+    if(group >= 0 && group < 4)
+    {
+        if(charging > 0)
+        {
+            if(GroupCollection[group].Role == _GROLE_IDLE || GroupCollection[group].Role == _GROLE_SLAVE)
+            {
+                if(GroupCollection[group].Role == _GROLE_SLAVE)
+                {
+                    PsuGroupSwitchToIdle(group);
+                }
+
+                PsuGroupStartCharging(group);
+            }
+        }
+        if(charging == 0)
+        {
+            if(GroupCollection[group].Role == _GROLE_MASTER)
+            {
+                PsuGroupStopCharging(group);
+            }
+            if(GroupCollection[group].Role == _GROLE_SLAVE)
+            {
+                PsuGroupSwitchToIdle(group);
+            }
+        }
+
+        ShowGroupingInfo();
+    }
+}
+
 int main(void)
 {
 	if(InitShareMemory() == FAIL)
@@ -1027,6 +1443,19 @@ int main(void)
 		return 0;
 	}
 
+	memset(&GroupCollection[0], 0x00, sizeof(PsuGroupCollectionData));
+	memset(&GroupCollection[1], 0x00, sizeof(PsuGroupCollectionData));
+	memset(&GroupCollection[2], 0x00, sizeof(PsuGroupCollectionData));
+	memset(&GroupCollection[3], 0x00, sizeof(PsuGroupCollectionData));
+	GroupCollection[0].Index = 0;
+	GroupCollection[1].Index = 1;
+	GroupCollection[2].Index = 2;
+	GroupCollection[3].Index = 3;
+	GroupCollection[0].Location = 0;
+	GroupCollection[1].Location = 3;
+	GroupCollection[2].Location = 1;
+	GroupCollection[3].Location = 2;
+
 	for(;;)
 	{
 		char word[128];
@@ -1208,6 +1637,16 @@ int main(void)
 			// 槍狀態
 			RunUnconditionalChargeIndex1(newString[1], newString[2], newString[3]);
 		}
+		else if(strcmp(newString[0], "group") == 0)
+		{
+		    if (strcmp(newString[1], "-1") == 0 || strcmp(newString[1], "-1") == 0)
+		    {
+		        ShowGroupingInfo();
+		        continue;
+		    }
+
+		    RunPsuGrouping(newString[1], newString[2]);
+		}
 		else
 			printf ("%s\n", msg);
 		usleep(100000);

+ 421 - 97
EVSE/Projects/DO360/Apps/main.c

@@ -75,8 +75,7 @@
 
 #define 	DB_FILE				"/Storage/ChargeLog/localCgargingRecord.db"
 
-#define 	uSEC_VAL				1000000
-#define		SELFTEST_TIMEOUT		45
+#define		SELFTEST_TIMEOUT		60
 #define		AUTHORIZE_TIMEOUT		30
 #define 	AUTHORIZE_COMP_TIMEOUT	1
 #define 	AUTHORIZE_FAIL_TIMEOUT	3
@@ -99,6 +98,8 @@
 #define     PSU_FAILURE_RESUME_TIME     180
 #define     MAX_AC_CONTACTOR_COUNT      3
 #define     STANDBY_TIME                600
+#define     INDICATION_BLINK_INTERVAL   1000
+#define     E4YOU_BLINK_INTERVAL        500
 
 char 	*valid_Internet[2] 	  = {"8.8.8.8", "180.76.76.76"};
 unsigned char mask_table[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
@@ -107,6 +108,9 @@ int	wtdFd = -1;
 byte _authorizeIndex = NO_DEFINE;
 BOOL _UpgradeNeedReboot = FALSE;
 
+const byte GroupLocation[GENERAL_GUN_QUANTITY] = {0, 3, 1, 2};
+const byte GroupLayout[GENERAL_GUN_QUANTITY]   = {0, 2, 3, 1};
+
 bool IsAuthorizingMode();
 void ClearAuthorizedFlag();
 bool isDetectPlugin();
@@ -163,6 +167,8 @@ struct RelayModuleData			*ShmRelayModuleData[2];
 struct LedModuleData			*ShmLedModuleData;
 struct OCPP16Data				*ShmOCPP16Data;
 ChargerInfoData                 *ShmChargerInfo;
+PsuPositionInfoData             *ShmPsuPosition;
+PsuGroupingInfoData             *ShmPsuGrouping;
 
 struct ChargingInfoData			*chargingInfo[CONNECTOR_QUANTITY];
 struct ChargingInfoData			*ac_chargingInfo[AC_QUANTITY];
@@ -193,7 +199,7 @@ bool isModelNameMatch = true;
 
 //int rfidFd = -1;
 //char* rfidPortName = "/dev/ttyS2";
-char* fwVersion = "D0.10.00.0000.00";
+char* fwVersion = "D0.11.00.0000.00";
 
 sqlite3 *localDb;
 bool isDb_ready;
@@ -657,7 +663,7 @@ int CreateShareMemory()
 		#endif
 		return 0;
 	}
-	// memset(ShmOCPP16Data,0,sizeof(struct OCPP16Data));
+	memset(ShmOCPP16Data,0,sizeof(struct OCPP16Data));
 
     //creat ShmOCPP16Data
     if ((MeterSMId = shmget(SM_ChargerInfoKey, sizeof(ChargerInfoData), IPC_CREAT | 0777)) < 0)
@@ -675,6 +681,8 @@ int CreateShareMemory()
         return 0;
     }
     memset(ShmChargerInfo, 0, sizeof(ChargerInfoData));
+    ShmPsuPosition = &ShmChargerInfo->PsuPosition;
+    ShmPsuGrouping = &ShmChargerInfo->PsuGrouping;
 
 	return 1;
 }
@@ -1390,11 +1398,17 @@ void InitEthernet()
 				}
 			}
 
+		    if(system("pidof -s OcppBackend > /dev/null") != 0)
+		    {
+		        ShmOCPP16Data->OcppConnStatus = false;
+		        ShmSysConfigAndInfo->SysInfo.OcppConnStatus = false;
+		    }
+
 			ShmSysConfigAndInfo->SysInfo.InternetConn = ethResult;
 
 			ShmStatusCodeData->InfoCode.InfoEvents.bits.WiFiDisable = ShmSysConfigAndInfo->SysConfig.AthInterface.WifiMode == false ? true : false;
 			ShmStatusCodeData->InfoCode.InfoEvents.bits.Telocom4GModuleDisable = ShmSysConfigAndInfo->SysConfig.TelecomInterface.TelcomEnabled == false ? true : false;
-			ShmStatusCodeData->InfoCode.InfoEvents.bits.BackendDisconnectedViaEthernet = ShmOCPP16Data->OcppConnStatus == false ? true : false;
+			ShmStatusCodeData->InfoCode.InfoEvents.bits.BackendDisconnectedViaEthernet = ShmSysConfigAndInfo->SysInfo.OcppConnStatus == false ? true : false;
 
 			UpdateConnectionIcon();
 			sleep(5);
@@ -1734,6 +1748,37 @@ int Initialization()
 	return PASS;
 }
 
+void InitialPsuGroupingAndLocation(void)
+{
+    memcpy(ShmPsuGrouping->Location, GroupLayout, sizeof(ShmPsuGrouping->Location));
+    memcpy(ShmPsuGrouping->Layout, GroupLayout, sizeof(ShmPsuGrouping->Layout));
+    for(int i = 0; i < CONNECTOR_QUANTITY; i++)
+    {
+        ShmPsuGrouping->GroupCollection[i].Index = i;
+        ShmPsuGrouping->GroupCollection[i].Location = ShmPsuGrouping->Location[i];
+    }
+}
+
+void InitialChargerSetting(void)
+{
+    if(ShmSysConfigAndInfo->SysConfig.ModelName[0] == 'D' && ShmSysConfigAndInfo->SysConfig.ModelName[1] == 'O')
+    {
+        ShmChargerInfo->Control.PrimaryCtrl.bits.AcContactorReverse = true;
+        ShmChargerInfo->Control.PrimaryCtrl.bits.DoorSensorReverse = true;
+        ShmChargerInfo->Control.SysCtrl.bits.LedBoardDisable = true;
+        ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable = true;
+
+        ShmChargerInfo->Control.SysCtrl.bits.StandardLedIndication = true;
+    }
+    if(ShmSysConfigAndInfo->SysConfig.ModelName[0] == 'D' && ShmSysConfigAndInfo->SysConfig.ModelName[1] == 'K')
+    {
+        ShmChargerInfo->Control.SysCtrl.bits.FanBoardDisable = true;
+        ShmChargerInfo->Control.SysCtrl.bits.LedBoardDisable = true;
+
+        ShmChargerInfo->Control.SysCtrl.bits.E4YOULedIndication = true;
+    }
+}
+
 bool InitialSystemDefaultConfig()
 {
 	bool result = true;
@@ -1847,9 +1892,12 @@ void SelfTestRun()
 			{
 				case _STEST_VERSION:
 				{
-					if (ShmFanModuleData->SelfTest_Comp &&
-							ShmRelayModuleData[0]->SelfTest_Comp && ShmRelayModuleData[0]->SelfTest_Comp &&
-							ShmPrimaryMcuData->SelfTest_Comp)
+					if ((ShmChargerInfo->Control.SysCtrl.bits.FanBoardDisable || ShmFanModuleData->SelfTest_Comp) &&
+					    (ShmChargerInfo->Control.SysCtrl.bits.RelayBoardDisable || ShmRelayModuleData[0]->SelfTest_Comp) &&
+                        (ShmChargerInfo->Control.SysCtrl.bits.RelayBoardDisable ||
+                        ShmChargerInfo->Control.SysCtrl.bits.SecondRelayBoardEnable == false ||
+                        ShmRelayModuleData[1]->SelfTest_Comp) &&
+                        ShmPrimaryMcuData->SelfTest_Comp)
 					{
 						ShmSysConfigAndInfo->SysInfo.SelfTestSeq = _STEST_PSU_DETECT;
 						PRINTF_FUNC("Self test version check ok");
@@ -2268,7 +2316,7 @@ void _DetectEvseChargingEnableTimeout(byte gunIndex)
 	PRINTF_FUNC("*********** _DetectEvseChargingEnableTimeout (GFD timeout) ***********\n");
 	//if (chargingInfo[gunIndex]->GroundFaultStatus != GFD_PASS)
 	{
-		setChargerMode(gunIndex, MODE_IDLE);
+		setChargerMode(gunIndex, MODE_TERMINATING);
 		_AutoReturnTimeout();
 	}
 }
@@ -2276,7 +2324,7 @@ void _DetectEvseChargingEnableTimeout(byte gunIndex)
 void _PrepareTimeout(byte gunIndex)
 {
 	PRINTF_FUNC("*********** _PrepareTimeout ***********\n");
-	setChargerMode(gunIndex, MODE_IDLE);
+	setChargerMode(gunIndex, MODE_TERMINATING);
 	ShmStatusCodeData->AlarmCode.AlarmEvents.bits.PsuNoResource = YES;
 	_AutoReturnTimeout();
 }
@@ -2823,6 +2871,25 @@ void StandbyCheck(void)
     }
 }
 
+void CheckDisconnectionStatusRecovery(void)
+{
+    bool recovery = true;
+
+    // check all connector
+    for(int i = 0; i < CONNECTOR_QUANTITY; i++)
+    {
+        if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].Parameter.bits.Disconnection)
+        {
+            recovery = false;
+        }
+    }
+
+    if(recovery)
+    {
+        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DisconnectedFromDo = false;
+    }
+}
+
 //===============================================
 // 確認各小板偵測的錯誤狀況
 //===============================================
@@ -2846,6 +2913,38 @@ void CheckErrorOccurStatus(byte index)
         chargingInfo[index]->ChargingStopFlag.bits.AlarmStop = true;
     }
 
+    if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[index].Parameter.bits.TimeoutStopRequest)
+    {
+        ShmSysConfigAndInfo->SysInfo.ConnectorInfo[index].Parameter.bits.TimeoutStopRequest = false;
+        ShmSysConfigAndInfo->SysInfo.ConnectorInfo[index].Parameter.bits.Disconnection = true;
+        ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DisconnectedFromDo = true;
+
+        if(chargingInfo[index]->SystemStatus == S_PREPARNING ||
+            chargingInfo[index]->SystemStatus == S_PREPARING_FOR_EV ||
+            chargingInfo[index]->SystemStatus == S_PREPARING_FOR_EVSE ||
+            chargingInfo[index]->SystemStatus == S_CHARGING)
+        {
+            if(strncmp((char *)chargingInfo[index]->ConnectorAlarmCode, "", 6) == EQUAL)
+            {
+                memcpy(chargingInfo[index]->ConnectorAlarmCode, "012304", 6);
+            }
+            if(!chargingInfo[index]->ChargingStopFlag.bits.AlarmStop)
+            {
+                PRINTF_FUNC("Connector %d Disconnect Alarm Occur %s", index + 1, chargingInfo[index]->ConnectorAlarmCode);
+            }
+            chargingInfo[index]->ChargingStopFlag.bits.AlarmStop = true;
+        }
+        else if(chargingInfo[index]->SystemStatus == S_REASSIGN_CHECK ||
+                chargingInfo[index]->SystemStatus == S_REASSIGN)
+        {
+            setChargerMode(index, MODE_IDLE);
+        }
+    }
+    if(ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DisconnectedFromDo)
+    {
+        CheckDisconnectionStatusRecovery();
+    }
+
     if(ShmStatusCodeData->AlarmCode.AlarmEvents.bits.EmergencyStopTrip == YES ||
         ShmStatusCodeData->AlarmCode.AlarmEvents.bits.DoorOpen)
     {
@@ -3928,6 +4027,22 @@ BOOL WaitAllDispenserUpgradeCompleted(void)
     return completed;
 }
 
+bool IsModelNameMatch(char *model)
+{
+    if(ShmSysConfigAndInfo->SysConfig.ModelName[0] == model[0] &&
+        ShmSysConfigAndInfo->SysConfig.ModelName[1] == model[1] &&
+        ShmSysConfigAndInfo->SysConfig.ModelName[7] == model[7] &&
+        ShmSysConfigAndInfo->SysConfig.ModelName[8] == model[8] &&
+        ShmSysConfigAndInfo->SysConfig.ModelName[9] == model[9] &&
+        ShmSysConfigAndInfo->SysConfig.ModelName[11] == model[11] &&
+        ShmSysConfigAndInfo->SysConfig.ModelName[12] == model[12] &&
+        ShmSysConfigAndInfo->SysConfig.ModelName[13] == model[13])
+    {
+        return true;
+    }
+    return false;
+}
+
 char CheckUpdateProcess()
 {
     char model[32];
@@ -3982,7 +4097,8 @@ char CheckUpdateProcess()
 				Type = (((unsigned int)ptr[16]) << 24 | ((unsigned int)ptr[17]) << 16 | ((unsigned int)ptr[18]) << 8 | ((unsigned int)ptr[19]));
 			    PRINTF_FUNC("Typed...%x", Type);
 
-			    if(strcmp(model, (char *)ShmSysConfigAndInfo->SysConfig.ModelName) == EQUAL)
+			    //if(strcmp(model, (char *)ShmSysConfigAndInfo->SysConfig.ModelName) == EQUAL)
+			    if(IsModelNameMatch(model))
 			    {
                     switch (Type)
                     {
@@ -4255,7 +4371,8 @@ void CheckConnectionTimeout(void)
 	}
 	else
 	{
-		if(strcmp((char *)ShmOCPP16Data->ConfigurationTable.CoreProfile[ConnectionTimeOut].ItemData,"") != 0)
+		if(ShmOCPP16Data->OcppConnStatus &&
+            strcmp((char *)ShmOCPP16Data->ConfigurationTable.CoreProfile[ConnectionTimeOut].ItemData,"") != 0)
 		{
 			_connectionTimeout = atoi((char *)ShmOCPP16Data->ConfigurationTable.CoreProfile[ConnectionTimeOut].ItemData);
 			if(_connectionTimeout <= 0)
@@ -5636,6 +5753,13 @@ void CheckTask()
 	if(strcmp((char *)ShmSysConfigAndInfo->SysConfig.OcppServerURL, "") != EQUAL &&
 			strcmp((char *)ShmSysConfigAndInfo->SysConfig.ChargeBoxId, "") != EQUAL)
 	{
+        if((time((time_t*)NULL) - ShmOCPP16Data->procDogTime) > 180)
+        {
+            DEBUG_ERROR_MSG("OcppBackend watch dog timeout task restart.\n");
+            ShmOCPP16Data->procDogTime =  time((time_t*)NULL);
+            system("pkill OcppBackend");
+        }
+
 		if(system("pidof -s OcppBackend > /dev/null") != 0)
 		{
 			DEBUG_ERROR_MSG("OcppBackend not running, restart it.\r\n");
@@ -5965,7 +6089,231 @@ void DispenserConnectionRetryHandler(void)
     ShmSysConfigAndInfo->SysInfo.DispenserInfo.Currency = _currency;
 }
 
-struct timeval _dispenserDisconnection_time, _dispenserConnectionRetry_time;
+struct timeval _dispenserConnectionRetry_time;
+
+void DispenserReconnectionProcess(void)
+{
+    if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.DispenserQuantity == 0)
+    {
+        if(ShmSysConfigAndInfo->SysInfo.CabinetSetting.bits.DispenserDisconnection == 0)
+        {
+            ShmSysConfigAndInfo->SysInfo.CabinetSetting.bits.DispenserDisconnection = 1;
+            gettimeofday(&_dispenserConnectionRetry_time, NULL);
+        }
+        if((GetTimeoutValue(_dispenserConnectionRetry_time) / uSEC_VAL) >= DISPENSER_RECONNECTE_TIME)
+        {
+            DispenserConnectionRetryHandler();
+            gettimeofday(&_dispenserConnectionRetry_time, NULL);
+        }
+    }
+    else
+    {
+        ShmSysConfigAndInfo->SysInfo.CabinetSetting.bits.DispenserDisconnection = 0;
+    }
+}
+
+// connector: connector infex, 0 ~ 3
+void SetLedIndicationStatus(unsigned char connector, unsigned char indication)
+{
+    int greenOffset = 0, redOffset = 0;
+
+    if(ShmChargerInfo->Control.SysCtrl.bits.StandardLedIndication)
+    {
+        greenOffset = 2;
+        redOffset = 3;
+    }
+    if(ShmChargerInfo->Control.SysCtrl.bits.E4YOULedIndication)
+    {
+        if(connector == 0)
+        {
+            greenOffset = 4;
+            redOffset = 5;
+        }
+        else if(connector == 1)
+        {
+            greenOffset = 2;
+            redOffset = 3;
+        }
+        else
+        {
+            return;
+        }
+    }
+
+    switch(indication)
+    {
+        case _LED_INDICATION_OFF:
+            ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] &= ~(0x01 << greenOffset);       // green off
+            ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] &= ~(0x01 << redOffset);         // red off
+            break;
+        case _LED_INDICATION_RUN:
+            ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] |= (0x01 << greenOffset);        // green on
+            ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] &= ~(0x01 << redOffset);         // red off
+            break;
+        case _LED_INDICATION_FAULT:
+            ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] &= ~(0x01 << greenOffset);       // green off
+            ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] |= (0x01 << redOffset);          // red on
+            break;
+        case _LED_INDICATION_ON:
+            ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] |= (0x01 << greenOffset);        // green on
+            ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] |= (0x01 << redOffset);          // red on
+            break;
+        case _LED_INDICATION_GREEN_TOGGLE:
+            if(ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] & (0x01 << greenOffset))
+            {
+                ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] &= ~(0x01 << greenOffset);   // green off
+            }
+            else
+            {
+                ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] |= (0x01 << greenOffset);    // green on
+            }
+            break;
+        case _LED_INDICATION_RED_TOGGLE:
+            if(ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] & (0x01 << redOffset))
+            {
+                ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] &= ~(0x01 << redOffset);     // red off
+            }
+            else
+            {
+                ShmPrimaryMcuData->OutputDrv.OutputDrvValue[0] |= (0x01 << redOffset);      // red on
+            }
+            break;
+    }
+}
+
+struct timeval _standardIndication_time;
+unsigned char _standardLedStatus, _preStandardLedStatus;
+unsigned char _e4youLedStatus[2], _preE4youLedStatus[2];
+struct timeval _e4youIndication_time[2];
+
+void LedIndicationProcess(void)
+{
+    if(ShmChargerInfo->Control.SysCtrl.bits.StandardLedIndication)
+    {
+        if(ShmSysConfigAndInfo->SysWarningInfo.Level == 2)
+        {
+            _standardLedStatus = _STANDARD_LED_Alarm;
+            if(_preStandardLedStatus != _standardLedStatus)
+            {
+                _preStandardLedStatus = _standardLedStatus;
+            }
+
+            SetLedIndicationStatus(0, _LED_INDICATION_FAULT);
+        }
+        else
+        {
+            if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.DispenserQuantity == 0)
+            {
+                _standardLedStatus = _STANDARD_LED_Disconnection;
+                if(_preStandardLedStatus != _standardLedStatus)
+                {
+                    _preStandardLedStatus = _standardLedStatus;
+
+                    // reset led status & indication time
+                    gettimeofday(&_standardIndication_time, NULL);
+                    SetLedIndicationStatus(0, _LED_INDICATION_OFF);
+                }
+
+                if((GetTimeoutValue(_standardIndication_time) / mSEC_VAL) >= INDICATION_BLINK_INTERVAL)
+                {
+                    SetLedIndicationStatus(0, _LED_INDICATION_GREEN_TOGGLE);
+                    SetLedIndicationStatus(0, _LED_INDICATION_RED_TOGGLE);
+                    gettimeofday(&_standardIndication_time, NULL);
+                }
+            }
+            else
+            {
+                _standardLedStatus = _STANDARD_LED_Running;
+                if(_preStandardLedStatus != _standardLedStatus)
+                {
+                    _preStandardLedStatus = _standardLedStatus;
+                }
+
+                SetLedIndicationStatus(0, _LED_INDICATION_RUN);
+            }
+        }
+    }
+    if(ShmChargerInfo->Control.SysCtrl.bits.E4YOULedIndication)
+    {
+        for(int i = 0; i < 2; i++)
+        {
+            if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].Enable == false)
+            {
+                _e4youLedStatus[i] = _E4YOU_LED_Disconnection;
+                if(_preE4youLedStatus[i] != _e4youLedStatus[i])
+                {
+                    _preE4youLedStatus[i] = _e4youLedStatus[i];
+
+                    // reset led status & indication time
+                    gettimeofday(&_e4youIndication_time[i], NULL);
+                    SetLedIndicationStatus(i, _LED_INDICATION_OFF);
+                }
+
+                if((GetTimeoutValue(_e4youIndication_time[i]) / mSEC_VAL) >= INDICATION_BLINK_INTERVAL)
+                {
+                    gettimeofday(&_e4youIndication_time[i], NULL);
+                    SetLedIndicationStatus(i, _LED_INDICATION_GREEN_TOGGLE);
+                    SetLedIndicationStatus(i, _LED_INDICATION_RED_TOGGLE);
+                }
+            }
+            else if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].GeneralChargingData.SystemStatus == S_IDLE ||
+                ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].GeneralChargingData.SystemStatus == S_MAINTAIN ||
+                ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].GeneralChargingData.SystemStatus == S_RESERVATION)
+            {
+                _e4youLedStatus[i] = _E4YOU_LED_Idle;
+                if(_preE4youLedStatus[i] != _e4youLedStatus[i])
+                {
+                    _preE4youLedStatus[i] = _e4youLedStatus[i];
+                }
+
+                SetLedIndicationStatus(i, _LED_INDICATION_RUN);
+            }
+            else if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].GeneralChargingData.SystemStatus >= S_REASSIGN_CHECK &&
+                    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].GeneralChargingData.SystemStatus <= S_COMPLETE)
+            {
+                _e4youLedStatus[i] = _E4YOU_LED_Charging;
+                if(_preE4youLedStatus[i] != _e4youLedStatus[i])
+                {
+                    _preE4youLedStatus[i] = _e4youLedStatus[i];
+
+                    // reset led status & indication time
+                    gettimeofday(&_e4youIndication_time[i], NULL);
+                    SetLedIndicationStatus(i, _LED_INDICATION_OFF);
+                }
+
+                if((GetTimeoutValue(_e4youIndication_time[i]) / mSEC_VAL) >= E4YOU_BLINK_INTERVAL)
+                {
+                    gettimeofday(&_e4youIndication_time[i], NULL);
+                    SetLedIndicationStatus(i, _LED_INDICATION_GREEN_TOGGLE);
+                }
+            }
+            else if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].GeneralChargingData.SystemStatus == S_FAULT ||
+                    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[i].GeneralChargingData.SystemStatus == S_ALARM)
+            {
+                _e4youLedStatus[i] = _E4YOU_LED_Alarm;
+                if(_preE4youLedStatus[i] != _e4youLedStatus[i])
+                {
+                    _preE4youLedStatus[i] = _e4youLedStatus[i];
+                }
+
+                SetLedIndicationStatus(i, _LED_INDICATION_FAULT);
+            }
+        }
+    }
+}
+
+void CheckConnectorDisconnectionRecovery(unsigned char connector)
+{
+    unsigned char dispenser = 0;
+
+    dispenser = ShmSysConfigAndInfo->SysInfo.ConnectorInfo[connector].ParentDispensetIndex;
+
+    if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.Dispenser[dispenser].LocalStatus != _DS_Timeout)
+    {
+        PRINTF_FUNC("Connector %d Disconnection Recovery", connector + 1);
+        ShmSysConfigAndInfo->SysInfo.ConnectorInfo[connector].Parameter.bits.Disconnection = false;
+    }
+}
 
 int main(void)
 {
@@ -6008,6 +6356,8 @@ int main(void)
 		isModelNameMatch = false;
 
 	Initialization();
+	InitialPsuGroupingAndLocation();
+	InitialChargerSetting();
 	PRINTF_FUNC("Spawn all Task. \n");
 	SpawnTask();
 
@@ -6022,11 +6372,26 @@ int main(void)
 		KillAllTask();
 		StopProcessingLoop();
 	}
-	PRINTF_FUNC("Module Name & HW info correct. Initialize.......\n");
+	PRINTF_FUNC("Module Name & HW info correct. Initialize.......");
 	CreateTimeoutFork();
 	PRINTF_FUNC("Self test.");
-	ShmPrimaryMcuData->OutputDrv.bits.SystemLed1Drv = true;         // green led
-	ShmPrimaryMcuData->OutputDrv.bits.SystemLed2Drv = true;         // red led
+
+    if(ShmChargerInfo->Control.SysCtrl.bits.StandardLedIndication)
+    {
+        _standardLedStatus = _STANDARD_LED_None;
+        _preStandardLedStatus = _STANDARD_LED_None;
+        SetLedIndicationStatus(0, _LED_INDICATION_ON);
+    }
+    if(ShmChargerInfo->Control.SysCtrl.bits.E4YOULedIndication)
+    {
+        _e4youLedStatus[0] = _E4YOU_LED_None;
+        _e4youLedStatus[1] = _E4YOU_LED_None;
+        _preE4youLedStatus[0] = _E4YOU_LED_None;
+        _preE4youLedStatus[1] = _E4YOU_LED_None;
+        SetLedIndicationStatus(0, _LED_INDICATION_ON);
+        SetLedIndicationStatus(1, _LED_INDICATION_ON);
+    }
+
 	SelfTestRun();
 //	StopSystemTimeoutDet();
 	SetAcContactor(ON);
@@ -6075,13 +6440,6 @@ int main(void)
 
 	ChangeLcmByIndex(_LCM_IDLE);
 	sleep(1);
-	//***** 須新增的偵測 *****//
-	// 1. Thernal - 控制風扇轉速
-	// 2. ouput fuse - 控制風扇轉速
-//	CreateRfidFork();
-	// Create Watchdog
-	//CreateWatchdog();
-	ShmPrimaryMcuData->OutputDrv.bits.SystemLed2Drv = false;
 
 	// Main loop
 	PRINTF_FUNC("****************************Main Loop********************************** \n");
@@ -6122,53 +6480,10 @@ int main(void)
 			}
 
 			gettimeofday(&_cmdMainPriority_time, NULL);
-
-//			PRINTF_FUNC("Gun 1(%d): %d(%s), Gun 2(%d): %d(%s), OCPP: %s",
-//                chargingInfo[0]->Index, chargingInfo[0]->SystemStatus, chargingInfo[0]->IsAvailable ? "Operative" : "Inoperative",
-//                chargingInfo[1]->Index, chargingInfo[1]->SystemStatus, chargingInfo[1]->IsAvailable ? "Operative" : "Inoperative",
-//                ShmOCPP16Data->OcppConnStatus ? "On" : "Off");
-
-//			PRINTF_FUNC("Gun 1: %d(%d), Gun 2: %d(%d), Gun 3: %d(%d), Gun 4: %d(%d)",
-//                chargingInfo[0]->Index, chargingInfo[0]->Type,
-//                chargingInfo[1]->Index, chargingInfo[1]->Type,
-//                chargingInfo[2]->Index, chargingInfo[2]->Type,
-//                chargingInfo[3]->Index, chargingInfo[3]->Type);
 		}
 
-		if(ShmSysConfigAndInfo->SysWarningInfo.Level == 2)
-		{
-            ShmPrimaryMcuData->OutputDrv.bits.SystemLed1Drv = false;
-            ShmPrimaryMcuData->OutputDrv.bits.SystemLed2Drv = true;
-		}
-		else
-		{
-            if(ShmSysConfigAndInfo->SysInfo.DispenserInfo.DispenserQuantity == 0)
-            {
-                if(ShmSysConfigAndInfo->SysInfo.CabinetSetting.bits.DispenserDisconnection == 0)
-                {
-                    ShmSysConfigAndInfo->SysInfo.CabinetSetting.bits.DispenserDisconnection = 1;
-                    gettimeofday(&_dispenserConnectionRetry_time, NULL);
-                }
-                if((GetTimeoutValue(_dispenserConnectionRetry_time) / uSEC_VAL) >= DISPENSER_RECONNECTE_TIME)
-                {
-                    DispenserConnectionRetryHandler();
-                    gettimeofday(&_dispenserConnectionRetry_time, NULL);
-                }
-
-                if((GetTimeoutValue(_dispenserDisconnection_time) / 1000) >= 1000)
-                {
-                    ShmPrimaryMcuData->OutputDrv.bits.SystemLed2Drv = ShmPrimaryMcuData->OutputDrv.bits.SystemLed1Drv ? false : true;
-                    ShmPrimaryMcuData->OutputDrv.bits.SystemLed1Drv = ShmPrimaryMcuData->OutputDrv.bits.SystemLed1Drv ? false : true;
-                    gettimeofday(&_dispenserDisconnection_time, NULL);
-                }
-            }
-            else
-            {
-                ShmSysConfigAndInfo->SysInfo.CabinetSetting.bits.DispenserDisconnection = 0;
-                ShmPrimaryMcuData->OutputDrv.bits.SystemLed1Drv = true;
-                ShmPrimaryMcuData->OutputDrv.bits.SystemLed2Drv = false;
-            }
-		}
+		DispenserReconnectionProcess();
+		LedIndicationProcess();
 
 		CheckMiscCommandRequirement();
 		CheckDispenserVersionUpdateRequirement();
@@ -6179,19 +6494,6 @@ int main(void)
 			CheckErrorOccurStatus(gun_index);
 			ChkOcppStatus(gun_index);
 
-			if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.TimeoutStopRequest)
-			{
-			    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.TimeoutStopRequest = false;
-
-			    if(chargingInfo[gun_index]->SystemStatus == S_PREPARNING ||
-                    chargingInfo[gun_index]->SystemStatus == S_PREPARING_FOR_EV ||
-                    chargingInfo[gun_index]->SystemStatus == S_PREPARING_FOR_EVSE ||
-                    chargingInfo[gun_index]->SystemStatus == S_CHARGING)
-			    {
-			        ChargingTerminalProcess(gun_index);
-			    }
-			}
-
 			//PRINTF_FUNC("index = %d, ErrorCode = %s \n", gun_index, ShmOCPP16Data->StatusNotification[gun_index].ErrorCode);
 			switch(chargingInfo[gun_index]->SystemStatus)
 			{
@@ -6269,21 +6571,40 @@ int main(void)
 
 					if (ShmSysConfigAndInfo->SysWarningInfo.Level == 2 ||
                         ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].RemoteStatus == _CRS_Alarm ||
-                        ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.FaultStatusRequest)
+                        ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.FaultStatusRequest ||
+                        ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.Disconnection)
 					{
-					    ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.FaultStatusRequest = false;
+                        if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.FaultStatusRequest)
+                        {
+                            if(strncmp((char *)chargingInfo[gun_index]->ConnectorAlarmCode, "", 6) == EQUAL)
+                            {
+                                memcpy(chargingInfo[gun_index]->ConnectorAlarmCode, ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].RemotenAlarmCode, 6);
+                            }
+                        }
+                        ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.FaultStatusRequest = false;
 
-				        if(strncmp((char *)chargingInfo[gun_index]->ConnectorAlarmCode, "", 6) == EQUAL)
-				        {
-				            memcpy(chargingInfo[gun_index]->ConnectorAlarmCode, ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].RemotenAlarmCode, 6);
-				        }
+                        if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.Disconnection)
+                        {
+                            if(strncmp((char *)chargingInfo[gun_index]->ConnectorAlarmCode, "", 6) == EQUAL)
+                            {
+                                memcpy(chargingInfo[gun_index]->ConnectorAlarmCode, "012304", 6);
+                                PRINTF_FUNC("Connector %d Disconnect Occur", gun_index + 1);
+                            }
+                        }
 
-					    if(chargingInfo[gun_index]->SystemStatus != S_FAULT)
-					    {
+                        if(chargingInfo[gun_index]->SystemStatus != S_FAULT)
+                        {
                             ClearDetectPluginFlag();
                             UpdateErrorCodeToOcpp(gun_index);
                             setChargerMode(gun_index, MODE_FAULT);
 					    }
+					    else
+					    {
+					        if(ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.Disconnection)
+					        {
+					            CheckConnectorDisconnectionRecovery(gun_index);
+					        }
+					    }
 					}
 					else
 					{
@@ -6590,6 +6911,7 @@ int main(void)
 					    PRINTF_FUNC("==================     S_PREPARNING (%x)     ================== \n", gun_index);
 						StopGunInfoTimeoutDet(gun_index);
 						StartGunInfoTimeoutDet(gun_index, Timeout_Preparing);
+						SetAcContactor(ON);
 					}
 
 					if (ShmPsuData->SystemPresentPsuQuantity > 0 && ShmPsuData->SystemAvailablePower > 10 &&
@@ -6712,9 +7034,6 @@ int main(void)
 						OcppStartTransation(gun_index);
 					}
 
-					if (ShmOCPP16Data->CpMsg.bits[gun_index].StartTransactionConf)
-						ShmOCPP16Data->CpMsg.bits[gun_index].StartTransactionConf = NO;
-
 					ftime(&endChargingTime[gun_index]);
 					chargingInfo[gun_index]->PresentChargedDuration = DiffTimeb(startChargingTime[gun_index], endChargingTime[gun_index]);
 
@@ -6763,13 +7082,19 @@ int main(void)
                     }
 					else if(CheckBackendChargingTimeout(gun_index) ||
                         CheckBackendChargingEnergy(gun_index) ||
-                        strcmp((char *)ShmOCPP16Data->StartTransaction[gun_index].ResponseIdTagInfo.Status, "Invalid") == EQUAL)
+                        (ShmOCPP16Data->CpMsg.bits[gun_index].StartTransactionConf &&
+                        strcmp((char *)ShmOCPP16Data->StartTransaction[gun_index].ResponseIdTagInfo.Status, "Invalid") == EQUAL))
 					{
 					    PRINTF_FUNC("********** Gun %d Backend Stop (S_CHARGING) **********", gun_index);
 						// 板端或後臺要求停止
 						ChargingTerminalProcess(gun_index);
 					}
 
+                    if(ShmOCPP16Data->CpMsg.bits[gun_index].StartTransactionConf)
+                    {
+                        ShmOCPP16Data->CpMsg.bits[gun_index].StartTransactionConf = NO;
+                    }
+
 					// LCM => Charging
 					if (ShmSysConfigAndInfo->SysInfo.CurGunSelected == gun_index)
 						ShmSysConfigAndInfo->SysInfo.ConnectorPage = _LCM_CHARGING;
@@ -6816,9 +7141,8 @@ int main(void)
                             }
                         }
 
-                        if((chargingInfo[gun_index]->ConnectorPlugIn == NO || ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Enable == false) &&
-                            ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.PsuReleasable &&
-                            ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].RemoteStatus == _CRS_Idle)
+                        if((ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].RemoteStatus == _CRS_Idle || ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Enable == false) &&
+                            ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.PsuReleasable)
                         {
                             setChargerMode(gun_index, MODE_IDLE);
                         }
@@ -6854,7 +7178,7 @@ int main(void)
 					    }
 					}
 
-					if((chargingInfo[gun_index]->ConnectorPlugIn == NO || ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Enable == false) &&
+					if((ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].RemoteStatus == _CRS_Idle || ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Enable == false) &&
                         ShmSysConfigAndInfo->SysInfo.ConnectorInfo[gun_index].Parameter.bits.PsuReleasable)
 					{
 						setChargerMode(gun_index, MODE_IDLE);

BIN
EVSE/Projects/DO360/Images/ramdisk.gz


+ 10 - 6
EVSE/Projects/define.h

@@ -342,7 +342,7 @@ struct TeleConfigData
 	unsigned char		TelcomSimImsi[16];			//default: Null
 	unsigned char		TelcomSimIccid[22];			//default: Null
 	unsigned char		TelcomSimStatus;			//0: no SIM card is found, 1: valid SIM card, 2: invalid SIM card
-	unsigned char		TelcomModemMode;			//0: No services, 1: CDMA, 2: GSM/GPRS, 3: WCDMA, 4: GSM/WCDMA, 5: TD_SCDMA, 6: HSPA, 7: LTE 9: Unknown
+	unsigned char       TelcomModemMode;            //0: No services, 1: CDMA, 2: GSM/GPRS, 3: WCDMA, 4: GSM/WCDMA, 5: TD_SCDMA, 6: HSPA, 7: LTE 9: Unknown
 	unsigned char		TelcomIpAddress[16];		//default: Null
 	unsigned char		TelcomNetworkConn;			//0: disconnected, 1: connected
 	unsigned char		TelcomEnabled;				//0: disable, 1: enable
@@ -463,7 +463,7 @@ struct SysConfigData
 	struct LED				LedInfo;					// LED configuration info
 	unsigned char			ShowInformation;
 	unsigned char           isReqFirstUpgrade;          //EVSE is request first upgrade from PH server
-	unsigned char			isEnableLocalPowerSharging; //0: Disable power sharing	1: Enable power sharing
+	unsigned char           isEnableLocalPowerSharging; //0: Disable power sharing  1: Enable power sharing
 };
 
 struct ChargingInfoData
@@ -557,6 +557,8 @@ typedef union
     unsigned int SettingValue;
     struct
     {
+        unsigned int DuplicateIp:1;
+        unsigned int DuplicateIpConfirm:1;
         unsigned int DispenserConfigSync:1;             // 0: not synced,   1: synced
         unsigned int MiscNeedAnnouncement:1;            // 0: no need,      1: need send misc command
         unsigned int NeedDispenserVerInfo:1;            // 0: no need,      1: need dispenser to report it's version info
@@ -576,7 +578,7 @@ typedef union
         unsigned int EthernetStatusRequest:1;           // 0: no request,   1: ethernet connection status has changed           ( cabinet -> dispenser)
         unsigned int WiFiStatusRequest:1;               // 0: no request,   1: wifi connection status has changed               ( cabinet -> dispenser)
         unsigned int TelcomModemStatusRequest:1;        // 0: no request,   1: 4g connection status has changed                 ( cabinet -> dispenser)
-        unsigned int res:13;
+        unsigned int res:11;
     }bits;
 }DispenserSettingFlag;
 
@@ -620,6 +622,7 @@ struct DispenserModule
     unsigned char           AuthResult;         // 0: _AuthResult_None, 1: _AuthResult_Valid,   2: _AuthResult_Invalid
     DispenserSettingFlag    Setting;
     char                    FwFileName[128];
+    unsigned char           ConnectionChannel;
 };
 
 struct ConnectionInfoData
@@ -692,7 +695,8 @@ typedef union
         unsigned int  NormalStopRequest:1;              // 0: no effect,    1: connector normal stop request                    ( dispenser -> cabinet)
         unsigned int  AlarmStopRequest:1;               // 0: no effect,    1: connector alarm stop request                     ( dispenser -> cabinet)
         unsigned int  FaultStatusRequest:1;
-        unsigned int  res:11;
+        unsigned int  Disconnection:1;
+        unsigned int  res:10;
     }bits;
 }ConnectorParameter;
 
@@ -4206,7 +4210,7 @@ struct OCPP16Data
     unsigned char                           OcppConnStatus;         //0: disconnected, 1: connected
     unsigned int                            Timeout_Secs;
     unsigned short                          Ping_Pong_Interval;
-    unsigned int 							procDogTime;			// Process watch dog refresh timer
+    unsigned int                            procDogTime;            // Process watch dog refresh timer
     union
     {
         //Operations Initiated by Charge Point
@@ -5477,7 +5481,7 @@ struct OCPP20Data
 	unsigned char 							OcppConnStatus;			//0: disconnected, 1: connected
 	unsigned int 							Timeout_Secs;
 	unsigned short 							Ping_Pong_Interval;
-	unsigned int 							procDogTime;			// Process watch dog refresh timer
+	unsigned int                            procDogTime;            // Process watch dog refresh timer
 	struct ReportDataType                   ControllerComponentVariable[CtrlrVariable_CNT];
 	struct NetworkConnectionProfile_20      NetworkConnectionProfile[10];