|
@@ -79,7 +79,7 @@ int StoreLogMsg(const char *fmt, ...)
|
|
|
tm=localtime(&CurrentTime);
|
|
|
gettimeofday(&tv, NULL); // get microseconds, 10^-6
|
|
|
|
|
|
- sprintf(Buf,"echo -n \"[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s\" >> /Storage/SystemLog/[%04d.%02d]Module_InterCommLog",
|
|
|
+ sprintf(Buf,"echo -n \'[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s\' >> /Storage/SystemLog/[%04d.%02d]Module_InterCommLog",
|
|
|
tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,tv.tv_usec,
|
|
|
buffer,
|
|
|
tm->tm_year+1900,tm->tm_mon+1);
|
|
@@ -295,6 +295,9 @@ void ack_delay(unsigned char cmd)
|
|
|
case CMD_UPDATE_END:
|
|
|
usleep(300000);
|
|
|
break;
|
|
|
+ case CMD_QUERY_PTB_METER_MESSAGE:
|
|
|
+ usleep(500000);
|
|
|
+ break;
|
|
|
default:
|
|
|
usleep(100000);
|
|
|
break;
|
|
@@ -309,7 +312,7 @@ int tranceive(int fd, unsigned char* cmd, unsigned char cmd_len, unsigned char*
|
|
|
if(write(fd, cmd, cmd_len) >= cmd_len)
|
|
|
{
|
|
|
ack_delay(cmd[3]);
|
|
|
- len = read(fd, rx, 512);
|
|
|
+ len = read(fd, rx, 2048);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -1095,6 +1098,149 @@ unsigned char Query_MeterIc_CorrectionPara(unsigned char fd, unsigned char targe
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+unsigned char Query_AC_GUN_PLUGIN_TIMES(unsigned char fd, unsigned char targetAddr, Gun_Plugin_Times *Ret_Buf)
|
|
|
+{
|
|
|
+ unsigned char result = FAIL;
|
|
|
+ unsigned char tx[7] = {0xaa, 0x00, targetAddr, CMD_QUERY_GUN_PLUGIN_TIMES, 0x00, 0x00, 0x00};
|
|
|
+ unsigned char rx[512];
|
|
|
+ unsigned char chksum = 0x00;
|
|
|
+ unsigned char len = tranceive(fd, tx, sizeof(tx), rx);
|
|
|
+
|
|
|
+ if(len > 6)
|
|
|
+ {
|
|
|
+ if(len < 6+(rx[4] | rx[5]<<8))
|
|
|
+ return result;
|
|
|
+
|
|
|
+ for(int idx=0;idx<(rx[4] | rx[5]<<8);idx++)
|
|
|
+ {
|
|
|
+ chksum ^= rx[6+idx];
|
|
|
+ }
|
|
|
+
|
|
|
+ if((chksum == rx[6+(rx[4] | rx[5]<<8)]) &&
|
|
|
+ (rx[2] == tx[1]) &&
|
|
|
+ (rx[1] == tx[2]) &&
|
|
|
+ (rx[3] == tx[3]))
|
|
|
+ {
|
|
|
+ Ret_Buf-> GunPluginTimes = ((uint32_t)rx[6] | (((uint32_t)rx[7])<<8) | (((uint32_t)rx[8])<<16) | (((uint32_t)rx[9])<<24));
|
|
|
+ result = PASS;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+unsigned char Query_PTB_METER_MESSAGE(unsigned char fd, unsigned char targetAddr, Ptb_Meter *Ret_Buf, uint8_t CMD)
|
|
|
+{
|
|
|
+ unsigned char result = FAIL;
|
|
|
+ unsigned char tx[8] = {0};
|
|
|
+ unsigned char rx[2048] = {0};
|
|
|
+ unsigned char chksum = 0x00;
|
|
|
+
|
|
|
+ tx[0] = 0xaa;
|
|
|
+ tx[1] = 0x00;
|
|
|
+ tx[2] = targetAddr;
|
|
|
+ tx[3] = CMD_QUERY_PTB_METER_MESSAGE;
|
|
|
+ tx[4] = 0x01;
|
|
|
+ tx[5] = 0x00;
|
|
|
+ tx[6] = CMD;
|
|
|
+
|
|
|
+ for(int idx=0;idx<(tx[4] | tx[5]<<8);idx++)
|
|
|
+ chksum ^= tx[6+idx];
|
|
|
+
|
|
|
+ tx[7] = chksum;
|
|
|
+
|
|
|
+ int len = tranceive(fd, tx, sizeof(tx), rx);
|
|
|
+
|
|
|
+ if(len > 6)
|
|
|
+ {
|
|
|
+ if(len < 6+(rx[4] | rx[5]<<8))
|
|
|
+ return result;
|
|
|
+
|
|
|
+ chksum = 0x00;
|
|
|
+ for(int idx=0;idx<(rx[4] | rx[5]<<8);idx++)
|
|
|
+ {
|
|
|
+ chksum ^= rx[6+idx];
|
|
|
+ }
|
|
|
+
|
|
|
+ if((chksum == rx[6+(rx[4] | rx[5]<<8)]) &&
|
|
|
+ (rx[2] == tx[1]) &&
|
|
|
+ (rx[1] == tx[2]) &&
|
|
|
+ (rx[3] == tx[3]))
|
|
|
+ {
|
|
|
+ if(CMD == CMD_READ_OUTPUT_MESSAGE_FORMAT_ID)
|
|
|
+ {
|
|
|
+ memset(Ret_Buf->ReadOutputMessageFormatId, 0x00, ARRAY_SIZE(Ret_Buf->ReadOutputMessageFormatId));
|
|
|
+ if(rx[6] == CMD_READ_OUTPUT_MESSAGE_FORMAT_ID)
|
|
|
+ {
|
|
|
+ memcpy(&Ret_Buf->ReadOutputMessageFormatId[0], &rx[8], ((rx[4] | (rx[5]<<8))-2));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(CMD == CMD_READ_OUTPUT_MESSAGE_LENGTH)
|
|
|
+ {
|
|
|
+ Ret_Buf->ReadOutputMessageLength = 0;
|
|
|
+ if(rx[6] == CMD_READ_OUTPUT_MESSAGE_LENGTH)
|
|
|
+ Ret_Buf->ReadOutputMessageLength = (rx[7] | (rx[8]<<8));
|
|
|
+ }
|
|
|
+ else if(CMD == CMD_READ_OUTPUT_MESSAGE)
|
|
|
+ {
|
|
|
+ memset(Ret_Buf->ReadOutputMessage, 0x00, ARRAY_SIZE(Ret_Buf->ReadOutputMessage));
|
|
|
+ if((Ret_Buf->ReadOutputMessageLength == ((rx[4] | (rx[5]<<8))-1)) && (rx[6] == CMD_READ_OUTPUT_MESSAGE))
|
|
|
+ {
|
|
|
+ memcpy(&Ret_Buf->ReadOutputMessage[0], &rx[7], ((rx[4] | (rx[5]<<8))-1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(CMD == CMD_READ_OUTPUT_SIGNATURE_LENGTH)
|
|
|
+ {
|
|
|
+ Ret_Buf->ReadOuputSignatureLength = 0;
|
|
|
+ if(rx[6] == CMD_READ_OUTPUT_SIGNATURE_LENGTH)
|
|
|
+ Ret_Buf->ReadOuputSignatureLength = (rx[7] | (rx[8]<<8));
|
|
|
+ }
|
|
|
+ else if(CMD == CMD_READ_OUTPUT_SIGNATURE)
|
|
|
+ {
|
|
|
+ memset(Ret_Buf->ReadOutputSignature, 0x00, ARRAY_SIZE(Ret_Buf->ReadOutputSignature));
|
|
|
+ if((Ret_Buf->ReadOuputSignatureLength == ((rx[4] | (rx[5]<<8))-1)) && (rx[6] == CMD_READ_OUTPUT_SIGNATURE))
|
|
|
+ {
|
|
|
+ memcpy(&Ret_Buf->ReadOutputSignature[0], &rx[7], ((rx[4] | (rx[5]<<8))-1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(CMD == CMD_READ_PUBLIC_KEY_HEADER_LENGTH)
|
|
|
+ {
|
|
|
+ Ret_Buf->ReadPublicKeyHeaderLength = 0;
|
|
|
+ if(rx[6] == CMD_READ_PUBLIC_KEY_HEADER_LENGTH)
|
|
|
+ Ret_Buf->ReadPublicKeyHeaderLength = (rx[7] | (rx[8]<<8));
|
|
|
+ }
|
|
|
+ else if(CMD == CMD_READ_PUBLIC_KEY_HEADER)
|
|
|
+ {
|
|
|
+ memset(Ret_Buf->ReadPublicKeyHeader, 0x00, ARRAY_SIZE(Ret_Buf->ReadPublicKeyHeader));
|
|
|
+ if((Ret_Buf->ReadPublicKeyHeaderLength == ((rx[4] | (rx[5]<<8))-1)) && (rx[6] == CMD_READ_PUBLIC_KEY_HEADER))
|
|
|
+ {
|
|
|
+ memcpy(&Ret_Buf->ReadPublicKeyHeader[0], &rx[7], ((rx[4] | (rx[5]<<8))-1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if(CMD == CMD_READ_PUBLIC_KEY_LENGTH)
|
|
|
+ {
|
|
|
+ Ret_Buf->ReadPublicKeyLength = 0;
|
|
|
+ if(rx[6] == CMD_READ_PUBLIC_KEY_LENGTH)
|
|
|
+ Ret_Buf->ReadPublicKeyLength = (rx[7] | (rx[8]<<8));
|
|
|
+ }
|
|
|
+ else if(CMD == CMD_READ_PUBLIC_KEY)
|
|
|
+ {
|
|
|
+ memset(Ret_Buf->ReadPublicKey, 0x00, ARRAY_SIZE(Ret_Buf->ReadPublicKey));
|
|
|
+ if((Ret_Buf->ReadPublicKeyLength == ((rx[4] | (rx[5]<<8))-1)) && (rx[6] == CMD_READ_PUBLIC_KEY))
|
|
|
+ {
|
|
|
+ memcpy(&Ret_Buf->ReadPublicKey[0], &rx[7], ((rx[4] | (rx[5]<<8))-1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {}
|
|
|
+
|
|
|
+ result = PASS;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
unsigned char Config_Fan_Speed(unsigned char fd, unsigned char targetAddr, FanSpeed *Set_Buf)
|
|
|
{
|
|
|
unsigned char result = FAIL;
|
|
@@ -1513,37 +1659,6 @@ unsigned char Config_AC_MCU_RESET_REQUEST(unsigned char fd, unsigned char target
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-unsigned char Query_AC_GUN_PLUGIN_TIMES(unsigned char fd, unsigned char targetAddr, Gun_Plugin_Times *Ret_Buf)
|
|
|
-{
|
|
|
- unsigned char result = FAIL;
|
|
|
- unsigned char tx[7] = {0xaa, 0x00, targetAddr, CMD_QUERY_GUN_PLUGIN_TIMES, 0x00, 0x00, 0x00};
|
|
|
- unsigned char rx[512];
|
|
|
- unsigned char chksum = 0x00;
|
|
|
- unsigned char len = tranceive(fd, tx, sizeof(tx), rx);
|
|
|
-
|
|
|
- if(len > 6)
|
|
|
- {
|
|
|
- if(len < 6+(rx[4] | rx[5]<<8))
|
|
|
- return result;
|
|
|
-
|
|
|
- for(int idx=0;idx<(rx[4] | rx[5]<<8);idx++)
|
|
|
- {
|
|
|
- chksum ^= rx[6+idx];
|
|
|
- }
|
|
|
-
|
|
|
- if((chksum == rx[6+(rx[4] | rx[5]<<8)]) &&
|
|
|
- (rx[2] == tx[1]) &&
|
|
|
- (rx[1] == tx[2]) &&
|
|
|
- (rx[3] == tx[3]))
|
|
|
- {
|
|
|
- Ret_Buf-> GunPluginTimes = ((uint32_t)rx[6] | (((uint32_t)rx[7])<<8) | (((uint32_t)rx[8])<<16) | (((uint32_t)rx[9])<<24));
|
|
|
- result = PASS;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
-}
|
|
|
-
|
|
|
unsigned char Config_AC_MaxCurrent_And_CpPwmDuty(unsigned char fd, unsigned char targetAddr, Ac_Primary_Mcu_Cp_Pwm_Duty *Set_Buf)
|
|
|
{
|
|
|
unsigned char result = FAIL;
|
|
@@ -1760,6 +1875,51 @@ unsigned char Config_Aux_Power_Switch(unsigned char fd, unsigned char targetAddr
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
+unsigned char Config_Ptb_Meter_Permission_Status(unsigned char fd, unsigned char targetAddr, Ptb_Meter *Set_Buf)
|
|
|
+{
|
|
|
+ unsigned char result = FAIL;
|
|
|
+ unsigned char tx[8] = {0};
|
|
|
+ unsigned char rx[512];
|
|
|
+ unsigned char chksum = 0x00;
|
|
|
+
|
|
|
+ tx[0] = 0xaa;
|
|
|
+ tx[1] = 0x00;
|
|
|
+ tx[2] = targetAddr;
|
|
|
+ tx[3] = CMD_CONFIG_PTB_METER_PERMISSION;
|
|
|
+ tx[4] = 0x01;
|
|
|
+ tx[5] = 0x00;
|
|
|
+ tx[6] = Set_Buf->PtbMeterPermissionStatus;
|
|
|
+
|
|
|
+ for(int idx=0;idx<(tx[4] | tx[5]<<8);idx++)
|
|
|
+ chksum ^= tx[6+idx];
|
|
|
+ tx[7] = chksum;
|
|
|
+
|
|
|
+ unsigned char len = tranceive(fd, tx, sizeof(tx), rx);
|
|
|
+
|
|
|
+ if(len > 6)
|
|
|
+ {
|
|
|
+ if(len < 6+(rx[4] | rx[5]<<8))
|
|
|
+ return result;
|
|
|
+
|
|
|
+ chksum = 0x00;
|
|
|
+ for(int idx=0;idx<(rx[4] | rx[5]<<8);idx++)
|
|
|
+ {
|
|
|
+ chksum ^= rx[6+idx];
|
|
|
+ }
|
|
|
+
|
|
|
+ if((chksum == rx[6+(rx[4] | rx[5]<<8)]) &&
|
|
|
+ (rx[2] == tx[1]) &&
|
|
|
+ (rx[1] == tx[2]) &&
|
|
|
+ (rx[3] == tx[3]) &&
|
|
|
+ ((rx[6] == 0x00) || (rx[6] == 0x01)))
|
|
|
+ {
|
|
|
+ result = PASS;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
unsigned char Update_Start(unsigned char fd, unsigned char targetAddr, unsigned int crc32)
|
|
|
{
|
|
|
unsigned char result = FAIL;
|
|
@@ -1958,7 +2118,7 @@ int main(void)
|
|
|
// High priority polling log print out
|
|
|
//==========================================================
|
|
|
//==================================================
|
|
|
- // Case 1: Config primary MCU LED
|
|
|
+ // Case 1: Configuration primary MCU LED
|
|
|
//==================================================
|
|
|
if((previousCharger.gun_info[gun_index].primaryMcuLed.mode != ShmCharger->gun_info[gun_index].primaryMcuLed.mode) ||
|
|
|
(previousCharger.gun_info[gun_index].primaryMcuLed.alarm_code != ShmCharger->gun_info[gun_index].primaryMcuLed.alarm_code))
|
|
@@ -1974,7 +2134,7 @@ int main(void)
|
|
|
}
|
|
|
|
|
|
//==================================================
|
|
|
- // Case 2: Config primary Legacy request
|
|
|
+ // Case 2: Configuration primary Legacy request
|
|
|
//==================================================
|
|
|
if(previousCharger.gun_info[gun_index].legacyRequest.isLegacyRequest != ShmCharger->gun_info[gun_index].legacyRequest.isLegacyRequest)
|
|
|
{
|
|
@@ -1987,7 +2147,7 @@ int main(void)
|
|
|
}
|
|
|
|
|
|
//==================================================
|
|
|
- // Case 2: Config primary Relay
|
|
|
+ // Case 2: Configuration primary Relay
|
|
|
//==================================================
|
|
|
if((previousCharger.gun_info[gun_index].primaryMcuState.relayState.relay_status[0][0] != ShmCharger->gun_info[gun_index].primaryMcuState.relayState.relay_status[0][0]) ||
|
|
|
(previousCharger.gun_info[gun_index].primaryMcuState.relayState.relay_status[0][1] != ShmCharger->gun_info[gun_index].primaryMcuState.relayState.relay_status[0][1]) ||
|
|
@@ -2206,6 +2366,119 @@ int main(void)
|
|
|
previousCharger.gun_info[gun_index].outputCurrent.L3N_L31[0] = ShmCharger->gun_info[gun_index].outputCurrent.L3N_L31[0];
|
|
|
}
|
|
|
|
|
|
+ //==================================================
|
|
|
+ // Case 12: Configuration PTB permission status
|
|
|
+ //==================================================
|
|
|
+ if(ShmSysConfigAndInfo->SysConfig.ModelName[3] == 'P')
|
|
|
+ {
|
|
|
+ if(previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.PtbMeterPermissionStatus != ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.PtbMeterPermissionStatus)
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("***** High priority polling : Case 12 *****\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+
|
|
|
+ if(ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.PtbMeterPermissionStatus == PERMISSION_START_CHARGING)
|
|
|
+ {
|
|
|
+ DEBUG_INFO("MCU-%d set permission to PTB meter: %d [Start Charging] \n", gun_index, ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.PtbMeterPermissionStatus);
|
|
|
+ }
|
|
|
+ else if(ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.PtbMeterPermissionStatus == PERMISSION_END_CHARGING)
|
|
|
+ {
|
|
|
+ DEBUG_INFO("MCU-%d set permission to PTB meter: %d [End Charging] \n", gun_index, ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.PtbMeterPermissionStatus);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {}
|
|
|
+
|
|
|
+ previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.PtbMeterPermissionStatus = ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.PtbMeterPermissionStatus;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //==================================================
|
|
|
+ // Case 13: Case 13; Query PTB meter messages
|
|
|
+ //==================================================
|
|
|
+ if(ShmSysConfigAndInfo->SysConfig.ModelName[3] == 'P')
|
|
|
+ {
|
|
|
+ if((strcmp((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageFormatId, (char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageFormatId) != 0))
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("** High priority polling : Case 13-0x10 ***\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("MCU-%d get PTB meter output message format id: %s \n", gun_index, &ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageFormatId);
|
|
|
+ memcpy((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageFormatId,(char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageFormatId,ARRAY_SIZE(ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageFormatId));
|
|
|
+ }
|
|
|
+
|
|
|
+ if(previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageLength != ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageLength)
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("** High priority polling : Case 13-0x11 ***\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("MCU-%d get PTB meter output message length: %d \n", gun_index, ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageLength);
|
|
|
+ previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageLength = ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessageLength;
|
|
|
+ }
|
|
|
+
|
|
|
+ if((strcmp((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessage, (char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessage) != 0))
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("** High priority polling : Case 13-0x12 ***\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("MCU-%d get PTB meter output message: %s \n", gun_index, &ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessage);
|
|
|
+ memcpy((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessage,(char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessage,ARRAY_SIZE(ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputMessage));
|
|
|
+ }
|
|
|
+
|
|
|
+ if(previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOuputSignatureLength != ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOuputSignatureLength)
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("** High priority polling : Case 13-0x13 ***\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("MCU-%d get PTB meter output signature length: %d \n", gun_index, ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOuputSignatureLength);
|
|
|
+ previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOuputSignatureLength = ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOuputSignatureLength;
|
|
|
+ }
|
|
|
+
|
|
|
+ if((strcmp((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputSignature, (char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputSignature) != 0))
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("** High priority polling : Case 13-0x14 ***\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("MCU-%d get PTB meter output signature: %s \n", gun_index, &ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputSignature);
|
|
|
+ memcpy((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputSignature,(char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputSignature,ARRAY_SIZE(ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadOutputSignature));
|
|
|
+ }
|
|
|
+
|
|
|
+ if(previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeaderLength != ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeaderLength)
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("** High priority polling : Case 13-0x15 ***\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("MCU-%d get PTB meter public key header length: %d \n", gun_index, ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeaderLength);
|
|
|
+ previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeaderLength = ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeaderLength;
|
|
|
+ }
|
|
|
+
|
|
|
+ if((strcmp((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeader, (char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeader) != 0))
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("** High priority polling : Case 13-0x16 ***\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("MCU-%d get PTB meter public key header: %s \n", gun_index, &ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeader);
|
|
|
+ memcpy((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeader,(char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeader,ARRAY_SIZE(ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyHeader));
|
|
|
+ }
|
|
|
+
|
|
|
+ if(previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyLength != ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyLength)
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("** High priority polling : Case 13-0x17 ***\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("MCU-%d get PTB meter public key length: %d \n", gun_index, ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyLength);
|
|
|
+ previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyLength = ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKeyLength;
|
|
|
+ }
|
|
|
+
|
|
|
+ if((strcmp((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKey, (char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKey) != 0))
|
|
|
+ {
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("** High priority polling : Case 13-0x18 ***\n");
|
|
|
+ DEBUG_INFO("*******************************************\n");
|
|
|
+ DEBUG_INFO("MCU-%d get PTB meter public key: %s \n", gun_index, &ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKey);
|
|
|
+ memcpy((char*)previousCharger.gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKey,(char*)ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKey,ARRAY_SIZE(ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters.ReadPublicKey));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
//==========================================================
|
|
|
// Low priority polling log print out
|
|
|
//==========================================================
|
|
@@ -2262,7 +2535,7 @@ int main(void)
|
|
|
}
|
|
|
|
|
|
//==================================================
|
|
|
- // Case 11: Config primary RTC time
|
|
|
+ // Case 11: Configuration primary RTC time
|
|
|
//==================================================
|
|
|
if(previousCharger.gun_info[gun_index].rtc.min != ShmCharger->gun_info[gun_index].rtc.min)
|
|
|
{
|
|
@@ -2289,12 +2562,12 @@ int main(void)
|
|
|
ShmCharger->gun_info[gun_index].rtc.min,
|
|
|
ShmCharger->gun_info[gun_index].rtc.sec);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
previousCharger.gun_info[gun_index].rtc.min = ShmCharger->gun_info[gun_index].rtc.min;
|
|
|
}
|
|
|
|
|
|
//==================================================
|
|
|
- // Case 15: Config led breathe timing
|
|
|
+ // Case 15: Configuration led breathe timing
|
|
|
//==================================================
|
|
|
if((previousCharger.gun_info[gun_index].setBreatheLedTiming.set_Led_Action_Authed_Fade_In != ShmCharger->gun_info[gun_index].setBreatheLedTiming.set_Led_Action_Authed_Fade_In) ||
|
|
|
(previousCharger.gun_info[gun_index].setBreatheLedTiming.set_Led_Action_Authed_Fade_Out != ShmCharger->gun_info[gun_index].setBreatheLedTiming.set_Led_Action_Authed_Fade_Out) ||
|
|
@@ -2325,7 +2598,7 @@ int main(void)
|
|
|
}
|
|
|
|
|
|
//==================================================
|
|
|
- // Case 17: Config led brightness
|
|
|
+ // Case 17: Configuration led brightness
|
|
|
//==================================================
|
|
|
if((previousCharger.gun_info[gun_index].setLedBrightness.sector_1 != ShmCharger->gun_info[gun_index].setLedBrightness.sector_1) ||
|
|
|
(previousCharger.gun_info[gun_index].setLedBrightness.sector_2 != ShmCharger->gun_info[gun_index].setLedBrightness.sector_2) ||
|
|
@@ -2392,9 +2665,9 @@ int main(void)
|
|
|
* High priority communication
|
|
|
*/
|
|
|
|
|
|
- //===============================
|
|
|
- // Case 1 : Config primary MCU LED
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
+ // Case 1 : Configuration primary MCU LED
|
|
|
+ //========================================
|
|
|
ShmCharger->gun_info[gun_index].primaryMcuLed.alarm_code = ShmCharger->gun_info[gun_index].systemAlarmCode.SystemAlarmCode;
|
|
|
if(Config_AC_MCU_LED(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].primaryMcuLed))
|
|
|
{
|
|
@@ -2409,9 +2682,9 @@ int main(void)
|
|
|
failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
}
|
|
|
|
|
|
- //===============================
|
|
|
- // Case 2 : Config primary Legacy request
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
+ // Case 2 : Configuration primary Legacy request
|
|
|
+ //========================================
|
|
|
if(Config_AC_MCU_LEGACY_REQUEST(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1),&ShmCharger->gun_info[gun_index].legacyRequest))
|
|
|
{
|
|
|
failCount[gun_index] = 0;
|
|
@@ -2425,9 +2698,9 @@ int main(void)
|
|
|
failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
}
|
|
|
|
|
|
- //===============================
|
|
|
- // Case 2-X : Config primary Relay
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
+ // Case 2-X : Configuration primary Relay
|
|
|
+ //========================================
|
|
|
if(Config_Relay_Output(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1),&ShmCharger->gun_info[gun_index].primaryMcuState.relayState))
|
|
|
{
|
|
|
failCount[gun_index] = 0;
|
|
@@ -2441,9 +2714,9 @@ int main(void)
|
|
|
failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
}
|
|
|
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
// Case 3 : Query primary MCU status
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
if(Query_AC_MCU_Status(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].primaryMcuState))
|
|
|
{
|
|
|
ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PilotState = ShmCharger->gun_info[gun_index].primaryMcuState.cp_state;
|
|
@@ -2521,9 +2794,9 @@ int main(void)
|
|
|
failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
}
|
|
|
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
// Case 4 : Query primary MCU Alarm code
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
if(Query_AC_MCU_Alarm(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].primaryMcuAlarm))
|
|
|
{
|
|
|
//DEBUG_INFO("MCU-%d get alarm code success.\n",gun_index);
|
|
@@ -2541,9 +2814,9 @@ int main(void)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
// Case 5 : Query primary MCU BLE config
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
if(Query_Ble_Config(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].bleConfigData))
|
|
|
{
|
|
|
ShmSysConfigAndInfo->SysConfig.Bluetooth.isLogin = ShmCharger->gun_info[gun_index].bleConfigData.isLogin;
|
|
@@ -2561,9 +2834,9 @@ int main(void)
|
|
|
failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
}
|
|
|
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
// Case 6 : Query primary MCU ble login id
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
if(Query_Ble_Central_ID(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].bleLoginCentralId))
|
|
|
{
|
|
|
memcpy(ShmSysConfigAndInfo->SysConfig.Bluetooth.LoginCentralID, ShmCharger->gun_info[gun_index].bleLoginCentralId.id, sizeof ShmSysConfigAndInfo->SysConfig.Bluetooth.LoginCentralID);
|
|
@@ -2579,9 +2852,9 @@ int main(void)
|
|
|
failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
}*/
|
|
|
|
|
|
- //===============================
|
|
|
- // Case 7 : Config primary MCU reset request
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
+ // Case 7 : Configuration primary MCU reset request
|
|
|
+ //========================================
|
|
|
if((access("/sys/class/gpio/gpio116/value", F_OK)) != -1)
|
|
|
{
|
|
|
if(ShmCharger->gun_info[gun_index].mcuResetRequest.isMcuResetRequest == ON)
|
|
@@ -2608,9 +2881,9 @@ int main(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- //===============================
|
|
|
- // Case 8 : Config primary set CP PWN duty
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
+ // Case 8 : Configuration primary set CP PWN duty
|
|
|
+ //========================================
|
|
|
if(ShmCharger->gun_info[gun_index].mcuFlag.isSetCpPwmDuty == ON)
|
|
|
{
|
|
|
//DEBUG_WARN("Case 8 : Config primary set CP PWN duty...%d\n", ShmCharger->gun_info[gun_index].primaryMcuCp_Pwn_Duty);
|
|
@@ -2629,9 +2902,9 @@ int main(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
// Case 9 : Query GPIO
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
if(Query_Gpio_Input(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].GPIO_Input))
|
|
|
{
|
|
|
failCount[gun_index] = 0;
|
|
@@ -2645,9 +2918,9 @@ int main(void)
|
|
|
failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
}
|
|
|
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
// Case 10 : Query primary MCU power consumption
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
if(Query_Power_Consumption(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].powerConsumptionTotal, &ShmCharger->gun_info[gun_index].powerConsumption[0], &ShmCharger->gun_info[gun_index].powerConsumption[1], &ShmCharger->gun_info[gun_index].powerConsumption[2]))
|
|
|
{
|
|
|
failCount[gun_index] = 0;
|
|
@@ -2660,21 +2933,21 @@ int main(void)
|
|
|
else
|
|
|
failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
}
|
|
|
-
|
|
|
- //===============================
|
|
|
+
|
|
|
+ //========================================
|
|
|
// Case 11 : Query present output current
|
|
|
- //===============================
|
|
|
+ //========================================
|
|
|
if(Query_Present_OutputCurrent(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].outputCurrent) == PASS)
|
|
|
{
|
|
|
#ifndef SIMULATION
|
|
|
ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingCurrent = (float)ShmCharger->gun_info[gun_index].outputCurrent.L1N_L12[0];
|
|
|
ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingCurrentL2 = (float)ShmCharger->gun_info[gun_index].outputCurrent.L2N_L23[0];
|
|
|
ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingCurrentL3 = (float)ShmCharger->gun_info[gun_index].outputCurrent.L3N_L31[0];
|
|
|
-
|
|
|
-
|
|
|
+
|
|
|
+
|
|
|
ShmCharger->gun_info[gun_index].acCcsInfo.EVSEPresentCurrent[0] = (float)ShmCharger->gun_info[gun_index].outputCurrent.L1N_L12[0];
|
|
|
ShmCharger->gun_info[gun_index].acCcsInfo.EVSEPresentCurrent[1] = (float)ShmCharger->gun_info[gun_index].outputCurrent.L1N_L12[0];
|
|
|
- ShmCharger->gun_info[gun_index].acCcsInfo.EVSEPresentCurrent[2] = (float)ShmCharger->gun_info[gun_index].outputCurrent.L1N_L12[0];
|
|
|
+ ShmCharger->gun_info[gun_index].acCcsInfo.EVSEPresentCurrent[2] = (float)ShmCharger->gun_info[gun_index].outputCurrent.L1N_L12[0];
|
|
|
#else //SIMULATION
|
|
|
ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].PresentChargingCurrent = (float)(ShmSysConfigAndInfo->SysInfo.AcChargingData[gun_index].SystemStatus==SYS_MODE_CHARGING?(((rand()%10)+((ShmCharger->gun_info[gun_index].targetCurrent*10)-5))/10.0):0);
|
|
|
if(ShmSysConfigAndInfo->SysConfig.AcPhaseCount == 3)
|
|
@@ -2693,6 +2966,100 @@ int main(void)
|
|
|
else
|
|
|
failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
}
|
|
|
+
|
|
|
+ //========================================
|
|
|
+ // Case 12: Configuration PTB permission status
|
|
|
+ //========================================
|
|
|
+ if(ShmSysConfigAndInfo->SysConfig.ModelName[3] == 'P')
|
|
|
+ {
|
|
|
+ if(ShmCharger->gun_info[gun_index].isSetPtbMeterPermisson == ON)
|
|
|
+ {
|
|
|
+ if(Config_Ptb_Meter_Permission_Status(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters) == PASS)
|
|
|
+ {
|
|
|
+ failCount[gun_index] = 0;
|
|
|
+ ShmCharger->gun_info[gun_index].isSetPtbMeterPermisson = OFF;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d set permission to PTB meter fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ if(failCount[gun_index]<USHRT_MAX)
|
|
|
+ failCount[gun_index]++;
|
|
|
+ else
|
|
|
+ failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //========================================
|
|
|
+ // Case 13; Query PTB meter messages
|
|
|
+ //========================================
|
|
|
+ if(ShmSysConfigAndInfo->SysConfig.ModelName[3] == 'P')
|
|
|
+ {
|
|
|
+ if((ShmCharger->gun_info[gun_index].isGetPtbMeterMessage == ON))
|
|
|
+ {
|
|
|
+ for(uint8_t idx = CMD_READ_OUTPUT_MESSAGE_FORMAT_ID; idx <= (CMD_READ_PUBLIC_KEY+1); idx++)
|
|
|
+ {
|
|
|
+ if(Query_PTB_METER_MESSAGE(Uart1Fd, (gun_index>0?ADDR_AC_PRIMARY_2:ADDR_AC_PRIMARY_1), &ShmCharger->gun_info[gun_index].Ptb_Meter_Parameters, idx) == PASS)
|
|
|
+ {
|
|
|
+ failCount[gun_index] = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if(idx == CMD_READ_OUTPUT_MESSAGE_FORMAT_ID)
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d get PTB meter output message format id fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ }
|
|
|
+ else if(idx == CMD_READ_OUTPUT_MESSAGE_LENGTH)
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d get PTB meter output message length fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ }
|
|
|
+ else if(idx == CMD_READ_OUTPUT_MESSAGE)
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d get PTB meter output message fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ }
|
|
|
+ else if(idx == CMD_READ_OUTPUT_SIGNATURE_LENGTH)
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d get PTB meter output signature length fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ }
|
|
|
+ else if(idx == CMD_READ_OUTPUT_SIGNATURE)
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d get PTB meter output signature fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ }
|
|
|
+ else if(idx == CMD_READ_PUBLIC_KEY_HEADER_LENGTH)
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d get PTB meter public key header length fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ }
|
|
|
+ else if(idx == CMD_READ_PUBLIC_KEY_HEADER)
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d get PTB meter public key header fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ }
|
|
|
+ else if(idx == CMD_READ_PUBLIC_KEY_LENGTH)
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d get PTB meter public key length fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ }
|
|
|
+ else if(idx == CMD_READ_PUBLIC_KEY)
|
|
|
+ {
|
|
|
+ DEBUG_WARN("MCU-%d get PTB meter public key fail...%d\n", gun_index, failCount[gun_index]);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {}
|
|
|
+
|
|
|
+ if(failCount[gun_index]<USHRT_MAX)
|
|
|
+ failCount[gun_index]++;
|
|
|
+ else
|
|
|
+ failCount[gun_index] = FAIL_SPEC_COMM;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(idx == (CMD_READ_PUBLIC_KEY+1))
|
|
|
+ {
|
|
|
+ ShmCharger->gun_info[gun_index].isGetPtbMeterMessage = OFF;
|
|
|
+ DEBUG_INFO("========================================\n");
|
|
|
+ DEBUG_INFO("Stop query PTB meter messages.\n");
|
|
|
+ DEBUG_INFO("========================================\n");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|