Various fixes at charging profiles handling:
authorJérôme Benoit <jerome.benoit@sap.com>
Sun, 27 Jun 2021 20:03:40 +0000 (22:03 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Sun, 27 Jun 2021 20:03:40 +0000 (22:03 +0200)
+ ensure set at remote start transaction
+ fix set with matching txProfile, etc.

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
src/types/ChargingStationTemplate.ts

index 6748a91a08b68e6d6b2d00a44aee11e3aacb5d46..45fee8adabd6fc4731f2cb5ecc1d73b61211c3f3 100644 (file)
--- a/README.md
+++ b/README.md
@@ -67,9 +67,10 @@ autoReconnectMaxRetries | | -1 (unlimited) | integer | connection retries to the
 reconnectExponentialDelay | true/false | false | boolean | connection delay retry to the OCPP-J server
 registrationMaxRetries | | -1 (unlimited) | integer | charging stations boot notification retries
 enableStatistics | true/false | true | boolean | enable charging stations statistics
+mayAuthorizeAtRemoteStart | true/false | true | boolean | always send authorize at remote start transaction when AuthorizeRemoteTxRequests is enabled
 beginEndMeterValues | true/false | false | boolean | enable Transaction.{Begin,End} MeterValues
 outOfOrderEndMeterValues | true/false | false | boolean | send Transaction.End MeterValues out of order
-meteringPerTransaction | true/false | true | boolean | disable metering on a per transaction basis
+meteringPerTransaction | true/false | true | boolean | enable metering history on a per transaction basis
 transactionDataMeterValues | true/false | false | boolean | enable transaction data MeterValues at stop transaction
 mainVoltageMeterValues | true/false | true | boolean | include charging station main voltage MeterValues on three phased charging stations
 phaseLineToLineVoltageMeterValues | true/false | true | boolean | include charging station line to line voltage MeterValues on three phased charging stations
index 30b21bdc357bd2ab65c70e90c4232b5352961750..71de426c4af701b5572320882b93015fc2a2e423 100644 (file)
@@ -91,6 +91,10 @@ export default class ChargingStation {
     return !Utils.isUndefined(this.stationInfo.enableStatistics) ? this.stationInfo.enableStatistics : true;
   }
 
+  public getMayAuthorizeAtRemoteStart(): boolean | undefined {
+    return this.stationInfo.mayAuthorizeAtRemoteStart ?? true;
+  }
+
   public getNumberOfPhases(): number | undefined {
     switch (this.getCurrentOutType()) {
       case CurrentType.AC:
@@ -371,18 +375,18 @@ export default class ChargingStation {
     }
   }
 
-  public setChargingProfile(connectorId: number, cp: ChargingProfile): boolean {
+  public setChargingProfile(connectorId: number, cp: ChargingProfile): void {
+    let cpReplaced = false;
     if (!Utils.isEmptyArray(this.getConnector(connectorId).chargingProfiles)) {
       this.getConnector(connectorId).chargingProfiles?.forEach((chargingProfile: ChargingProfile, index: number) => {
         if (chargingProfile.chargingProfileId === cp.chargingProfileId
             || (chargingProfile.stackLevel === cp.stackLevel && chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose)) {
           this.getConnector(connectorId).chargingProfiles[index] = cp;
-          return true;
+          cpReplaced = true;
         }
       });
     }
-    this.getConnector(connectorId).chargingProfiles?.push(cp);
-    return true;
+    !cpReplaced && this.getConnector(connectorId).chargingProfiles?.push(cp);
   }
 
   public resetTransactionOnConnector(connectorId: number): void {
index 03aae44255d144150f671b923156d3019b8140bb..f27e2f0272eb5d7495de71b6662c93be6ca3fc05 100644 (file)
@@ -256,63 +256,68 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
   }
 
   private async handleRequestRemoteStartTransaction(commandPayload: RemoteStartTransactionRequest): Promise<DefaultResponse> {
-    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()) {
-        let authorized = false;
-        // Check if authorized
-        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 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;
-          }
-        }
-        if (!authorized) {
-          const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(transactionConnectorId, commandPayload.idTag);
-          if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
+    const transactionConnectorId: number = commandPayload.connectorId;
+    if (transactionConnectorId) {
+      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()) {
+          let authorized = false;
+          // Check if authorized
+          if (this.chargingStation.getLocalAuthListEnabled() && this.chargingStation.hasAuthorizedTags()
+              && this.chargingStation.authorizedTags.find((value) => value === commandPayload.idTag)) {
             authorized = true;
           }
+          if (!authorized || (authorized && this.chargingStation.getMayAuthorizeAtRemoteStart())) {
+            const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(transactionConnectorId, commandPayload.idTag);
+            if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
+              authorized = true;
+            } else {
+              authorized = false;
+            }
+          }
+          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 await this.setRemoteStartChargingProfile(transactionConnectorId, commandPayload.chargingProfile)
+              ? Constants.OCPP_RESPONSE_ACCEPTED
+              : await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
+          }
+          return await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
         }
-        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;
-      }
-      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 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 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 await this.setRemoteStartChargingProfile(transactionConnectorId, commandPayload.chargingProfile)
+          ? Constants.OCPP_RESPONSE_ACCEPTED
+          : await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
       }
-      // 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;
+      return await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
     }
-    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 await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag);
+  }
+
+  private async notifyRemoteStartTransactionRejected(connectorId: number, idTag: string): Promise<DefaultResponse> {
+    await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE);
+    this.chargingStation.getConnector(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
+    logger.warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on connector Id ' + connectorId.toString() + ', availability: ' + this.chargingStation.getConnector(connectorId).availability + ', idTag ' + idTag);
     return Constants.OCPP_RESPONSE_REJECTED;
   }
 
+  private async setRemoteStartChargingProfile(connectorId: number, cp: OCPP16ChargingProfile): Promise<boolean> {
+    if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) {
+      this.chargingStation.setChargingProfile(connectorId, cp);
+      logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnector(connectorId).chargingProfiles);
+      return true;
+    } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) {
+      await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE);
+      this.chargingStation.getConnector(connectorId).status = OCPP16ChargePointStatus.AVAILABLE;
+      logger.warn(`${this.chargingStation.logPrefix()} Not allowed to set ${cp.chargingProfilePurpose} charging profile(s) at remote start transaction`);
+      return false;
+    }
+  }
+
   private async handleRequestRemoteStopTransaction(commandPayload: RemoteStopTransactionRequest): Promise<DefaultResponse> {
     const transactionId = commandPayload.transactionId;
     for (const connector in this.chargingStation.connectors) {
index fd1d988f02e73e40c185bc3f2dcf32fa6eb89e24..c65e94c68554c93abb127bc6a1a788472f212e24 100644 (file)
@@ -58,6 +58,7 @@ export default interface ChargingStationTemplate {
   reconnectExponentialDelay?: boolean;
   registrationMaxRetries?: number;
   enableStatistics?: boolean;
+  mayAuthorizeAtRemoteStart: boolean;
   beginEndMeterValues?: boolean;
   outOfOrderEndMeterValues?: boolean;
   meteringPerTransaction?: boolean;