Selaa lähdekoodia

[Improve][Modularization][Module_Upgrade]

2020.12.16 / Folus Wen

Actions:
1. Module_Upgrade.c cancel delete source file in CCS & CANBUS upgrade API.

Files:
1. As follow commit history

Image version: D0.00.XX.XXXX.XX
Image checksum: XXXXXXXX

Hardware PWB P/N : XXXXXXX
Hardware Version : XXXXXXX
FolusWen 4 vuotta sitten
vanhempi
commit
2bfb77c923

+ 4 - 4
EVSE/Modularization/Module_Upgrade.c

@@ -1031,8 +1031,8 @@ int Upgrade_CAN(int canfd,unsigned int Type,unsigned char TargetAddr,char *Sourc
     }
     free(ptr);
 
-    sprintf(cmdBuf, "rm -f %s", SourcePath);
-    system(cmdBuf);
+    //sprintf(cmdBuf, "rm -f %s", SourcePath);
+    //system(cmdBuf);
 
     return result;
 }
@@ -1218,8 +1218,8 @@ int Upgrade_CCS(int canfd,unsigned int Type,unsigned char TargetAddr,char *Sourc
 
     DEBUG_INFO("Upgrade CCS board %d complete.\n", TargetAddr);
 
-    sprintf(cmdBuf, "rm -f %s", SourcePath);
-    system(cmdBuf);
+    //sprintf(cmdBuf, "rm -f %s", SourcePath);
+    //system(cmdBuf);
 
     return PASS;
 }

+ 54 - 26
EVSE/Projects/AW-CCS/Apps/CCS/Module_CCS.c

@@ -31,7 +31,8 @@ pid_t Error_Monitor_Pid = 0;
 int TcpAcceptFd;
 enum MsgFlowStatus V2gFlowStatus;
 int RawSock,UdpSock,TcpSock;
-unsigned char *V2gtpMsgRxBuf, *V2gtpMsgTxBuf;
+unsigned char V2gtpMsgRxBuf[V2GTP_MSG_RX_BUFFER_SIZE] = {0};
+unsigned char V2gtpMsgTxBuf[V2GTP_MSG_TX_BUFFER_SIZE] = {0};
 
 unsigned short Aag[64];
 struct MmeHeader SendMmePacket;
@@ -900,12 +901,12 @@ float ReadAdcVolt(unsigned char AdcChannel)
         if(fd > 0)
         {
             //system("echo 1 > /sys/class/gpio/gpio89/value");    //for test
-            for(AvgTimes = 0; AvgTimes < 3; AvgTimes++) //period = 60~91 ms(renice -10, , CurrentDemand())
+            for(AvgTimes = 0; AvgTimes < 3; AvgTimes++) //period = 60~91 ms(renice -10, , CurrentDemand()) /*+++ 20200909, vern, extend detection time for interference ---*/
             {
                 count = 0;
                 MinSample = 2306;
                 //system("echo 1 > /sys/class/gpio/gpio89/value");    //for test
-                while(count < 30)  //period = 21~42ms (renice -10, CurrentDemand())
+                while(count < 40)  //period = 21~42ms (renice -10, CurrentDemand()) /*+++ 20200909, vern, extend detection time for interference ---*/
                 {
                     //re-sampling period = 3~13ms (renice -10, SLAC())
                     //system("echo 1 > /sys/class/gpio/gpio89/value");    //for test
@@ -1927,8 +1928,9 @@ void Error_Monitor()
         }
 
         //Step 7: Check for ChargingPermission from TRUE to FALSE before V2G Messages
+        /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth),  1 for star charging (authorized)  ---*/
         if ((ShmInternalComm->ChargingPermission == FALSE) &&
-            (ShmInternalComm->ChargingPermission_pre == TRUE) &&
+            (ShmInternalComm->ChargingPermission_pre >= 1) &&
             (ccs->CpState >= 3) && (ccs->CpState <=5))
         {
             if (status >= CM_SLAC_PARM_REQ &&
@@ -2002,8 +2004,9 @@ void Error_Monitor()
         }
 
         //Step 9: Check 60V
+        /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
         if (CCS_ENERGY_TRANSFER_MODE == MODE_DC_EXTENDED &&
-            ShmInternalComm->ChargingPermission == TRUE &&
+            ShmInternalComm->ChargingPermission >= 1 &&
             status < CableCheckRequest)
         {
             if (EVCOMM_SYS_INFO.PresentChargingVoltage >= 60)  //60V
@@ -4065,7 +4068,8 @@ int SlacComm()
                 }
                 else    //RawSock: opened; Set Key: DONE
                 {
-                    if((CheckConnectorPlugIn() == TRUE) && (ShmInternalComm->ChargingPermission == TRUE))
+                	/*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
+                    if((CheckConnectorPlugIn() == TRUE) && (ShmInternalComm->ChargingPermission >= 1))
                     {
                         if(PwmStartTime <= 0)
                         {
@@ -5573,7 +5577,10 @@ int Proc_supportedAppProtocolRes(int AcceptFd)
     ccs_handshake.supportedAppProtocolRes.SchemaID = CCS_HANDSHAKE_PROTOCOLS.array[0].SchemaID;
     ccs_handshake.supportedAppProtocolRes.SchemaID_isUsed = 1u;
     int id = 0;
-    id = GetSchemaID_of_Protocol(V2GT_MSG_PROTOCOL_PREFERENCE); //output: EVCOMM_SYS_INFO.SupportedAppProtocol_result
+    /*+++ 20200808, vern, support both DIN and ISO +++*/
+    //id = GetSchemaID_of_Protocol(V2GT_MSG_PROTOCOL_PREFERENCE); //output: EVCOMM_SYS_INFO.SupportedAppProtocol_result
+    id = GetSchemaID_of_Protocol(V2GT_MSG_PROTOCOL_HIGHEST_PRIORITY); //output: EVCOMM_SYS_INFO.SupportedAppProtocol_result
+    /*--- 20200808, vern, support both DIN and ISO ---*/
     ccs_handshake.supportedAppProtocolRes.ResponseCode = EVCOMM_SYS_INFO.SupportedAppProtocol_result;   //updating the response code
     if (id < 0)
     {
@@ -5608,7 +5615,8 @@ int Proc_supportedAppProtocolRes(int AcceptFd)
     }
 
     //Check for Permission Changing from TRUE to FALSE
-    if (ShmInternalComm->ChargingPermission_pre == TRUE &&
+    /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
+    if (ShmInternalComm->ChargingPermission_pre >= 1 &&
         ShmInternalComm->ChargingPermission == FALSE)
     {
         SAVE_SYS_LOG_MSG_EVCOMM("%s\n", "[Error][DIN][supportedAppProtocolRes]Permission OFF");
@@ -5810,7 +5818,8 @@ int Proc_din_SessionSetupRes(int AcceptFd)
     }
 
     //Check for Permission Changing from TRUE to FALSE
-    if (ShmInternalComm->ChargingPermission_pre == TRUE &&
+    /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
+    if (ShmInternalComm->ChargingPermission_pre >= 1 &&
         ShmInternalComm->ChargingPermission == FALSE)
     {
         SAVE_SYS_LOG_MSG_EVCOMM("%s\n", "[Error][DIN][SessionSetupRes]Permission OFF");
@@ -5821,7 +5830,8 @@ int Proc_din_SessionSetupRes(int AcceptFd)
     // ====== [BODY (2/3) EVSEID ======
     //EVSEID = all zero
     memset(ccs_exi_doc_DIN.V2G_Message.Body.SessionSetupRes.EVSEID.bytes, 0, sizeof(ccs_exi_doc_DIN.V2G_Message.Body.SessionSetupRes.EVSEID.bytes));
-    ccs_exi_doc_DIN.V2G_Message.Body.SessionSetupRes.EVSEID.bytesLen = 15;  //max: DIN = 32, ISO1/ISO2 = 37 bytes
+    //vern, should be encode  by SN
+    ccs_exi_doc_DIN.V2G_Message.Body.SessionSetupRes.EVSEID.bytesLen = 1;  //max: DIN = 32, ISO1/ISO2 = 37 bytes
 
     // ====== [BODY (3/3) DateTimeNow ======
     ccs_exi_doc_DIN.V2G_Message.Body.SessionSetupRes.DateTimeNow_isUsed = 1u;
@@ -5943,7 +5953,17 @@ int Proc_iso1_SessionSetupRes(int AcceptFd)
     // ====== [BODY (2/3) EVSEID ======
     //EVSEID = all zero
     memset(ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.characters, 0, sizeof(ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.characters));
-    ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.charactersLen = 15;  //max: DIN = 32, ISO1/ISO2 = 37 bytes
+    /*+++ 20200808, vern, set default EVSEID +++*/
+    //vern, should be encoded  by SN
+    ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.characters[0]='Z';
+    ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.characters[1]='Z';
+    ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.characters[2]='0';
+    ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.characters[3]='0';
+    ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.characters[4]='0';
+    ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.characters[5]='0';
+    ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.characters[6]='0';
+    ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSEID.charactersLen = 7;  //max: DIN = 32, ISO1/ISO2 = 37 bytes
+    /*--- 20200808, vern, set default EVSEID ---*/
 
     // ====== [BODY (3/3) DateTimeNow ======
     ccs_exi_doc_ISO1.V2G_Message.Body.SessionSetupRes.EVSETimeStamp_isUsed = 1u;
@@ -6055,7 +6075,8 @@ int Proc_iso2_SessionSetupRes(int AcceptFd)
     }
 
     //Check for Permission Changing from TRUE to FALSE
-    if (ShmInternalComm->ChargingPermission_pre == TRUE &&
+    /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
+    if (ShmInternalComm->ChargingPermission_pre >= 1 &&
         ShmInternalComm->ChargingPermission == FALSE)
     {
         SAVE_SYS_LOG_MSG_EVCOMM("%s\n", "[Error][ISO2][SessionSetupRes]Permission OFF");
@@ -6288,7 +6309,8 @@ int Proc_din_ServiceDiscoveryRes(int AcceptFd)
     }
 
     //Check for Permission Changing from TRUE to FALSE
-    if (ShmInternalComm->ChargingPermission_pre == TRUE &&
+    /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
+    if (ShmInternalComm->ChargingPermission_pre >= 1 &&
         ShmInternalComm->ChargingPermission == FALSE)
     {
         SAVE_SYS_LOG_MSG_EVCOMM("%s\n", "[Error][DIN][ServiceDiscoveryRes]Permission OFF");
@@ -6411,7 +6433,8 @@ int Proc_iso1_ServiceDiscoveryRes(int AcceptFd)
     }
 
     //Check for Permission Changing from TRUE to FALSE
-    if (ShmInternalComm->ChargingPermission_pre == TRUE &&
+    /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
+    if (ShmInternalComm->ChargingPermission_pre >= 1 &&
         ShmInternalComm->ChargingPermission == FALSE)
     {
         SAVE_SYS_LOG_MSG_EVCOMM("%s\n", "[Error][ISO1][ServiceDiscoveryRes]Permission OFF");
@@ -6687,7 +6710,8 @@ int Proc_din_ServiceAndPaymentSelectionRes(int AcceptFd)
     }
 
     //Check for Permission Changing from TRUE to FALSE
-    if (ShmInternalComm->ChargingPermission_pre == TRUE &&
+    /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
+    if (ShmInternalComm->ChargingPermission_pre >= 1 &&
         ShmInternalComm->ChargingPermission == FALSE)
     {
         SAVE_SYS_LOG_MSG_EVCOMM("%s\n", "[Error][DIN][ServicePaymentSelectionRes]Permission OFF");
@@ -6840,7 +6864,8 @@ int Proc_iso1_ServiceAndPaymentSelectionRes(int AcceptFd)
     }
 
     //Check for Permission Changing from TRUE to FALSE
-    if (ShmInternalComm->ChargingPermission_pre == TRUE &&
+    /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
+    if (ShmInternalComm->ChargingPermission_pre >= 1 &&
         ShmInternalComm->ChargingPermission == FALSE)
     {
         SAVE_SYS_LOG_MSG_EVCOMM("%s\n", "[Error][ISO1][PaymentServiceSelectionRes]Permission OFF");
@@ -7090,8 +7115,9 @@ int Proc_iso1_AuthorizationRes(int AcceptFd)
 
     //[BODY (1/2)] ResponseCode
     ccs_exi_doc_ISO1.V2G_Message.Body.AuthorizationRes.ResponseCode = iso1responseCodeType_OK;
-    ccs_exi_doc_ISO1.V2G_Message.Body.AuthorizationRes.EVSEProcessing = iso1EVSEProcessingType_Ongoing; //0
-
+    /*+++ 20200808, vern, EVSEProcessing should be waiting for Customer during authrization +++*/
+    ccs_exi_doc_ISO1.V2G_Message.Body.AuthorizationRes.EVSEProcessing = iso1EVSEProcessingType_Ongoing_WaitingForCustomerInteraction; //0
+    /*--- 20200808, vern, should be waiting for Customer during authrization ---*/
 
     //[HEADER] Check Req SessionID
     if (Check_iso1_V2G_Rx_MSG_SessionID(&ccs_exi_doc_ISO1) < 0)
@@ -7167,7 +7193,8 @@ int Proc_iso1_AuthorizationRes(int AcceptFd)
     }
 
     //Check for Permission Changing from TRUE to FALSE
-    if (ShmInternalComm->ChargingPermission_pre == TRUE &&
+    /*+++ 20200808, vern, sync with Tesla CHAdeMO adaptor, 2 for start communication(not yet auth), 1 for star charging (authorized)  ---*/
+    if (ShmInternalComm->ChargingPermission_pre >= 1 &&
         ShmInternalComm->ChargingPermission == FALSE)
     {
         SAVE_SYS_LOG_MSG_EVCOMM("%s\n", "[Error][ISO1][AuthorizationRes]Permission OFF");
@@ -7931,7 +7958,9 @@ int Proc_iso1_ChargeParameterDiscoveryRes(int AcceptFd)
     sys = &ShmSysConfigAndInfo->SysInfo.CcsChargingData[0];
 
     res->ResponseCode = OK_ISO15118_2014;
+    /*+++ 20200808, vern, EVSEProcessing should be on-going during ChargeParameterDiscovery +++*/
     res->EVSEProcessing =  iso1EVSEProcessingType_Ongoing;
+    /*--- 20200808, vern, EVSEProcessing should be on-going during ChargeParameterDiscovery ---*/
     dc_para->DC_EVSEStatus.DC_EVSEStatusCode = iso1DC_EVSEStatusCodeType_EVSE_Ready;    //1
     ac_para->AC_EVSEStatus.RCD = ShmInternalComm->AC_RcdStatus; //0:no error, 1:error
     ac_para->AC_EVSEStatus.NotificationMaxDelay = 0;  //unit: 1s
@@ -7965,7 +7994,8 @@ int Proc_iso1_ChargeParameterDiscoveryRes(int AcceptFd)
 
 
     //[TC_SECC_VTB_ChargeParameterDiscovery_005]
-    if (sys->EvBatteryMaxCurrent < 0)
+    /*+++ 20200808, vern, should check the voltage and current to see if the range of battery parameter is accepted by charger ---*/
+    if ((sys->EvBatteryMaxCurrent < 0) ||(sys->EvBatteryMaxVoltage<150))
     {
         sprintf((char*)buf_log_evcomm,
                 "[ERROR]EvBatteryMaxCurrent is negative(%.02f) => End_Process",
@@ -11366,12 +11396,13 @@ int Proc_iso1_PowerDeliveryStopRes(int AcceptFd)
 
     //EVSE Status Code
     res->DC_EVSEStatus.EVSEStatusCode = iso1DC_EVSEStatusCodeType_EVSE_Ready;
+    res->DC_EVSEStatus.EVSEIsolationStatus = iso1isolationLevelType_Valid;  //1 /*+++ 20200808, vern, Isolation Status should be valid during 2nd PowerDelivert ---*/
 
     //Check for CSU command of "Stop by EVSE"
     if (sys->DC_EVSEStatus == EVSE_Shutdown)
     {
         //res->ResponseCode = FAILED_ISO15118_2014;
-        res->DC_EVSEStatus.EVSEStatusCode = iso1DC_EVSEStatusCodeType_EVSE_Shutdown;
+    	res->DC_EVSEStatus.EVSEStatusCode = iso1DC_EVSEStatusCodeType_EVSE_Ready; /*+++ 20200808, vern, Isolation Status should be valid during 2nd PowerDelivert ---*/
     }
     else if (sys->DC_EVSEStatus == EVSE_EmergencyShutdown)
     {
@@ -11381,7 +11412,7 @@ int Proc_iso1_PowerDeliveryStopRes(int AcceptFd)
     else if (ShmInternalComm->ChargingPermission == FALSE)
     {
         //res->ResponseCode = FAILED_ISO15118_2014;
-        res->DC_EVSEStatus.EVSEStatusCode = iso1DC_EVSEStatusCodeType_EVSE_Shutdown;
+    	res->DC_EVSEStatus.EVSEStatusCode = iso1DC_EVSEStatusCodeType_EVSE_Ready; /*+++ 20200808, vern, Isolation Status should be valid during 2nd PowerDelivert ---*/
     }
 
     //STEP 4: ============ Encode and Send Response Message ===========
@@ -16115,7 +16146,7 @@ int End_Process()
 
     AttenProfileCnt = 0;
     init_appHandEXIDocument(&ccs_handshake);
-    //Qca7kPowerReset();  //reset QCA7000
+    // Qca7kPowerReset();  //reset QCA7000 /* +++ 20200808, vern, should disconnected PLC connection after session stop ---*/
 
     #if (FIRMWARE_VERSION_COMPILE_SETTING_RELEASE_MODE == DISABLE)
     {
@@ -16357,10 +16388,7 @@ int main(int argc, char *argv[])
     SAVE_SYS_LOG_MSG_EVCOMM("%s\n", "init...");
 
     //Init V2G TCP/IPv6 packets buffer
-    V2gtpMsgRxBuf = (unsigned char *)malloc(V2GTP_MSG_RX_BUFFER_SIZE);
     memset(V2gtpMsgRxBuf, 0, V2GTP_MSG_RX_BUFFER_SIZE);
-
-    V2gtpMsgTxBuf = (unsigned char *)malloc(V2GTP_MSG_TX_BUFFER_SIZE);
     memset(V2gtpMsgTxBuf, 0, V2GTP_MSG_TX_BUFFER_SIZE);
 
     //Release State E Control

+ 21 - 0
EVSE/Projects/AW-CCS/Apps/CCS/v2g/api/api.c

@@ -2376,6 +2376,10 @@ void SHM_Read_din_ChargeParameterDiscoveryRes(struct dinEXIDocument *exi_doc_DIN
     list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].RelativeTimeInterval.start = 0;
     list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].RelativeTimeInterval.duration = 86400;	//24Hrs at least, unit:second
     list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].RelativeTimeInterval.duration_isUsed = 1u;
+    /*+++ 20200808, vern, add Pmax parameters in chargeParameters discovery +++*/
+    in->DC_EVSEChargeParameter.EVSEMaximumPowerLimit.Value*=10;
+    list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].PMax = in->DC_EVSEChargeParameter.EVSEMaximumPowerLimit.Value;
+	/*--- 20200808, vern, add Pmax parameters in chargeParameters discovery ---*/
     //list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].TimeInterval_isUsed = 1u;
     //list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].TimeInterval = 600;
     //list->SAScheduleTuple.array[0].SalesTariff.xxx
@@ -2475,6 +2479,11 @@ void SHM_Read_iso1_ChargeParameterDiscoveryRes(struct iso1EXIDocument *exi_doc_I
     list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].RelativeTimeInterval.duration_isUsed = 1u;
     //list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].TimeInterval_isUsed = 1u;
     //list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].TimeInterval = 86400;
+    /*+++ 20200808, vern, add Pmax parameters in chargeParameters discovery +++*/
+    list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].PMax.Unit = iso1unitSymbolType_W;	//iso1unitSymbolType_W
+    in->DC_EVSEChargeParameter.EVSEMaximumPowerLimit.Value*=10;   /*+++ 20200808, vern, lack one more 0 ---*/
+    list->SAScheduleTuple.array[0].PMaxSchedule.PMaxScheduleEntry.array[0].PMax.Value =in->DC_EVSEChargeParameter.EVSEMaximumPowerLimit.Value;	//30000 if DW/DM product
+    /*--- 20200808, vern, add Pmax parameters in chargeParameters discovery ---*/
     //list->SAScheduleTuple.array[0].SalesTariff.xxx
     //list->SAScheduleTuple.array[0].SalesTariff_isUsed = 0u;
 
@@ -2863,6 +2872,18 @@ void SHM_Read_iso1_CurrentDemandRes(struct iso1EXIDocument *exi_doc_ISO1, struct
 	in = &shm_ccs->V2GMessage_ISO15118_2014.CurrentDemandResponse;
 	exi_doc_ISO1->V2G_Message.Body.CurrentDemandRes_isUsed = 1u;
 
+	//vern, fill up necessary
+	out->EVSEID.charactersLen=7;
+	out->EVSEID.characters[0]='Z';
+	out->EVSEID.characters[1]='Z';
+	out->EVSEID.characters[2]='0';
+	out->EVSEID.characters[3]='0';
+	out->EVSEID.characters[4]='0';
+	out->EVSEID.characters[5]='0';
+	out->EVSEID.characters[6]='0';
+	out->SAScheduleTupleID=1;
+	out->MeterInfo_isUsed=0u;
+	out->ReceiptRequired_isUsed=0u;
 
 	//----- [BODY (1/10)] ResponseCode -----
     out->ResponseCode = (unsigned char) in->ResponseCode;