Преглед на файлове

2021-03-19 / Wendell
Actions
1. add E4YOU DK model function
2. modify CheckUpdateProcess model name check logic
3. modify back to idle logic when charging completed
4. add disconnect(dispenser) status code and send it to ocpp
5. add ocpp process watch dog check function
6. self test timeout extended to 60 seconds
7. modify system status idle when prepare timeout & gfd timeout
8. modify StartTransactionConf logic in charging status
9. fix conflic ip address connection issue
10.control ac contactor directly(regardless of feedback)

Files
1. As follow commit history

Image version : D0.11.XX.XXXX.XX

Wendell преди 4 години
родител
ревизия
4c0ce151d8

+ 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];
 

BIN
EVSE/rootfs/root/OcppBackend20