Complete AuthorizeRemoteTxRequests support in remote start transaction
authorJérôme Benoit <jerome.benoit@sap.com>
Sun, 13 Jun 2021 19:47:35 +0000 (21:47 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Sun, 13 Jun 2021 19:47:35 +0000 (21:47 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
README.md
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts

index 09c06140e3e2f44e4f25520a0689c6f1b904f558..0a24123dd0f4f9fe9bc99c1573cb7b26198a131c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -233,10 +233,11 @@ All kind of OCPP parameters are supported in a charging station template. The li
 - :x: StopTransactionOnInvalidId (type: boolean) (units: -)
 - :x: StopTxnAlignedData (type: CSL) (units: -)
 - :x: StopTxnSampledData (type: CSL) (units: -)
-- :x: SupportedFeatureProfiles (type: CSL) (units: -)
+- :white_check_mark: SupportedFeatureProfiles (type: CSL) (units: -)
 - :x: TransactionMessageAttempts (type: integer) (units: times)
 - :x: TransactionMessageRetryInterval (type: integer) (units: seconds)
 - :x: UnlockConnectorOnEVSideDisconnect (type: boolean) (units: -)
+- :white_check_mark: WebSocketPingInterval (type: integer) (units: seconds)
 
 #### Firmware Management Profile
 
@@ -244,7 +245,7 @@ All kind of OCPP parameters are supported in a charging station template. The li
 
 #### Local Auth List Management Profile
 
-- :x: LocalAuthListEnabled (type: boolean) (units: -)
+- :white_check_mark: LocalAuthListEnabled (type: boolean) (units: -)
 - :x: LocalAuthListMaxLength (type: integer) (units: -)
 - :x: SendLocalListMaxLength (type: integer) (units: -)
 
index feffa05da837ce929da0dce630a6a9f5601f2c87..10806bd295bf93134f8ec3734c597378b2788fba 100644 (file)
@@ -503,13 +503,13 @@ export default class ChargingStation {
         break;
     }
     // OCPP parameters
+    if (!this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles)) {
+      this.addConfigurationKey(StandardParametersKey.SupportedFeatureProfiles, `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.Local_Auth_List_Management},${SupportedFeatureProfiles.Smart_Charging}`);
+    }
     this.addConfigurationKey(StandardParametersKey.NumberOfConnectors, this.getNumberOfConnectors().toString(), true);
     if (!this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData)) {
       this.addConfigurationKey(StandardParametersKey.MeterValuesSampledData, MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER);
     }
-    if (!this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles)) {
-      this.addConfigurationKey(StandardParametersKey.SupportedFeatureProfiles, SupportedFeatureProfiles.Core);
-    }
     if (!this.getConfigurationKey(StandardParametersKey.ConnectorPhaseRotation)) {
       const connectorPhaseRotation = [];
       for (const connector in this.connectors) {
@@ -527,6 +527,13 @@ export default class ChargingStation {
       }
       this.addConfigurationKey(StandardParametersKey.ConnectorPhaseRotation, connectorPhaseRotation.toString());
     }
+    if (!this.getConfigurationKey(StandardParametersKey.AuthorizeRemoteTxRequests)) {
+      this.addConfigurationKey(StandardParametersKey.AuthorizeRemoteTxRequests, 'true');
+    }
+    if (!this.getConfigurationKey(StandardParametersKey.LocalAuthListEnabled)
+        && this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles).value.includes(SupportedFeatureProfiles.Local_Auth_List_Management)) {
+      this.addConfigurationKey(StandardParametersKey.LocalAuthListEnabled, 'false');
+    }
     this.stationInfo.powerDivider = this.getPowerDivider();
     if (this.getEnableStatistics()) {
       this.performanceStatistics = new PerformanceStatistics(this.stationInfo.chargingStationId);
index ba1f2f7ef274bc034d7a8311b47e11ecd86cd170..7c32cb7897065880d450255537fdbd5f86a68c60 100644 (file)
@@ -255,40 +255,59 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
   }
 
   private async handleRequestRemoteStartTransaction(commandPayload: RemoteStartTransactionRequest): Promise<DefaultResponse> {
-    const transactionConnectorID: number = commandPayload.connectorId ? commandPayload.connectorId : 1;
+    const transactionConnectorID: number = commandPayload.connectorId ?? 1;
+    await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorID, OCPP16ChargePointStatus.PREPARING);
+    this.chargingStation.getConnector(transactionConnectorID).status = OCPP16ChargePointStatus.PREPARING;
     if (this.chargingStation.isChargingStationAvailable() && this.chargingStation.isConnectorAvailable(transactionConnectorID)) {
-      if (this.chargingStation.getAuthorizeRemoteTxRequests() && this.chargingStation.getLocalAuthListEnabled() && this.chargingStation.hasAuthorizedTags()) {
+      if (this.chargingStation.getAuthorizeRemoteTxRequests()) {
+        let authorized = false;
         // Check if authorized
-        if (this.chargingStation.authorizedTags.find((value) => value === commandPayload.idTag)) {
-          await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorID, OCPP16ChargePointStatus.PREPARING);
-          this.chargingStation.getConnector(transactionConnectorID).status = OCPP16ChargePointStatus.PREPARING;
+        if (this.chargingStation.getLocalAuthListEnabled() && this.chargingStation.hasAuthorizedTags()
+            && this.chargingStation.authorizedTags.find((value) => value === commandPayload.idTag)) {
+          authorized = true;
           if (commandPayload.chargingProfile && commandPayload.chargingProfile.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
             this.chargingStation.setChargingProfile(transactionConnectorID, commandPayload.chargingProfile);
-            logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at start transaction, dump their stack: %j`, this.chargingStation.getConnector(transactionConnectorID).chargingProfiles);
+            logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnector(transactionConnectorID).chargingProfiles);
           } else if (commandPayload.chargingProfile && commandPayload.chargingProfile.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
+            await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorID, OCPP16ChargePointStatus.AVAILABLE);
+            this.chargingStation.getConnector(transactionConnectorID).status = OCPP16ChargePointStatus.AVAILABLE;
+            logger.warn(`${this.chargingStation.logPrefix()} Not allowed to set ${commandPayload.chargingProfile.chargingProfilePurpose} charging profile(s) at remote start transaction`);
             return Constants.OCPP_RESPONSE_REJECTED;
           }
-          // Authorization successful start transaction
+        }
+        if (!authorized) {
+          const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(commandPayload.idTag);
+          if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
+            authorized = true;
+          }
+        }
+        if (authorized) {
+          // Authorization successful, start transaction
           await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
           logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorID.toString() + ' for idTag ' + commandPayload.idTag);
           return Constants.OCPP_RESPONSE_ACCEPTED;
         }
+        await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorID, OCPP16ChargePointStatus.AVAILABLE);
+        this.chargingStation.getConnector(transactionConnectorID).status = OCPP16ChargePointStatus.AVAILABLE;
         logger.warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on connector Id ' + transactionConnectorID.toString() + ', idTag ' + commandPayload.idTag);
         return Constants.OCPP_RESPONSE_REJECTED;
       }
-      await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorID, OCPP16ChargePointStatus.PREPARING);
-      this.chargingStation.getConnector(transactionConnectorID).status = OCPP16ChargePointStatus.PREPARING;
       if (commandPayload.chargingProfile && commandPayload.chargingProfile.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
         this.chargingStation.setChargingProfile(transactionConnectorID, commandPayload.chargingProfile);
-        logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at start transaction, dump their stack: %j`, this.chargingStation.getConnector(commandPayload.connectorId).chargingProfiles);
+        logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnector(commandPayload.connectorId).chargingProfiles);
       } else if (commandPayload.chargingProfile && commandPayload.chargingProfile.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
+        await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorID, OCPP16ChargePointStatus.AVAILABLE);
+        this.chargingStation.getConnector(transactionConnectorID).status = OCPP16ChargePointStatus.AVAILABLE;
+        logger.warn(`${this.chargingStation.logPrefix()} Not allowed to set ${commandPayload.chargingProfile.chargingProfilePurpose} charging profile(s) at remote start transaction`);
         return Constants.OCPP_RESPONSE_REJECTED;
       }
-      // No local authorization check required => start transaction
+      // No authorization check required, start transaction
       await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
       logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorID.toString() + ' for idTag ' + commandPayload.idTag);
       return Constants.OCPP_RESPONSE_ACCEPTED;
     }
+    await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorID, OCPP16ChargePointStatus.AVAILABLE);
+    this.chargingStation.getConnector(transactionConnectorID).status = OCPP16ChargePointStatus.AVAILABLE;
     logger.warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on unavailable connector Id ' + transactionConnectorID.toString() + ', idTag ' + commandPayload.idTag);
     return Constants.OCPP_RESPONSE_REJECTED;
   }