Handle availability and registration properly.
authorJérôme Benoit <jerome.benoit@sap.com>
Sun, 17 Jan 2021 16:44:15 +0000 (17:44 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Sun, 17 Jan 2021 16:44:15 +0000 (17:44 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
package-lock.json
package.json
src/charging-station/AutomaticTransactionGenerator.ts
src/charging-station/ChargingStation.ts
src/utils/Utils.ts

index 4d7eb0fe1c2a26b8f89b1f912d3efd50de27703c..6f1557626086c8507ad3e57c1508287fb45234d6 100644 (file)
       }
     },
     "@eslint/eslintrc": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz",
-      "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==",
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.3.0.tgz",
+      "integrity": "sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg==",
       "dev": true,
       "requires": {
         "ajv": "^6.12.4",
         "ignore": "^4.0.6",
         "import-fresh": "^3.2.1",
         "js-yaml": "^3.13.1",
-        "lodash": "^4.17.19",
+        "lodash": "^4.17.20",
         "minimatch": "^3.0.4",
         "strip-json-comments": "^3.1.1"
       },
           "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
           "dev": true
         },
+        "lodash": {
+          "version": "4.17.20",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+          "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+          "dev": true
+        },
         "strip-json-comments": {
           "version": "3.1.1",
           "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
       "dev": true
     },
     "bufferutil": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.2.tgz",
-      "integrity": "sha512-AtnG3W6M8B2n4xDQ5R+70EXvOpnXsFYg/AK2yTZd+HQ/oxAdz+GI+DvjmhBw3L0ole+LJ0ngqY4JMbDzkfNzhA==",
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.3.tgz",
+      "integrity": "sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw==",
       "optional": true,
       "requires": {
         "node-gyp-build": "^4.2.0"
       }
     },
     "eslint": {
-      "version": "7.17.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.17.0.tgz",
-      "integrity": "sha512-zJk08MiBgwuGoxes5sSQhOtibZ75pz0J35XTRlZOk9xMffhpA9BTbQZxoXZzOl5zMbleShbGwtw+1kGferfFwQ==",
+      "version": "7.18.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.18.0.tgz",
+      "integrity": "sha512-fbgTiE8BfUJZuBeq2Yi7J3RB3WGUQ9PNuNbmgi6jt9Iv8qrkxfy19Ds3OpL1Pm7zg3BtTVhvcUZbIRQ0wmSjAQ==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
-        "@eslint/eslintrc": "^0.2.2",
+        "@eslint/eslintrc": "^0.3.0",
         "ajv": "^6.10.0",
         "chalk": "^4.0.0",
         "cross-spawn": "^7.0.2",
         "js-yaml": "^3.13.1",
         "json-stable-stringify-without-jsonify": "^1.0.1",
         "levn": "^0.4.1",
-        "lodash": "^4.17.19",
+        "lodash": "^4.17.20",
         "minimatch": "^3.0.4",
         "natural-compare": "^1.4.0",
         "optionator": "^0.9.1",
             "type-check": "~0.4.0"
           }
         },
+        "lodash": {
+          "version": "4.17.20",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+          "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
+          "dev": true
+        },
         "lru-cache": {
           "version": "6.0.0",
           "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
       "dev": true
     },
     "utf-8-validate": {
-      "version": "5.0.3",
-      "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.3.tgz",
-      "integrity": "sha512-jtJM6fpGv8C1SoH4PtG22pGto6x+Y8uPprW0tw3//gGFhDDTiuksgradgFN6yRayDP4SyZZa6ZMGHLIa17+M8A==",
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.4.tgz",
+      "integrity": "sha512-MEF05cPSq3AwJ2C7B7sHAA6i53vONoZbMGX8My5auEVm6W+dJ2Jd/TZPyGJ5CH42V2XtbI5FD28HeHeqlPzZ3Q==",
       "optional": true,
       "requires": {
         "node-gyp-build": "^4.2.0"
index e3c0084c501f6f2f81e1f5afc77a04fb18cffc0b..d0cc70d283dd6d3ba0f763504991652157b26357 100644 (file)
@@ -54,8 +54,8 @@
     "ws": "^7.4.2"
   },
   "optionalDependencies": {
-    "bufferutil": "^4.0.2",
-    "utf-8-validate": "^5.0.3"
+    "bufferutil": "^4.0.3",
+    "utf-8-validate": "^5.0.4"
   },
   "devDependencies": {
     "@types/node": "^14.14.21",
@@ -66,7 +66,7 @@
     "@typescript-eslint/parser": "^4.13.0",
     "clinic": "^8.0.1",
     "cross-env": "^7.0.3",
-    "eslint": "^7.17.0",
+    "eslint": "^7.18.0",
     "grunt": "^1.3.0",
     "grunt-ts": "^6.0.0-beta.22",
     "mbt": "^1.1.0",
index 6e33ae69132eec2fd5e2e78c7edaec3b7a6f84f0..29f571e961315eb9b1f7ec8fe9eed4618d1d8cad 100644 (file)
@@ -63,14 +63,22 @@ export default class AutomaticTransactionGenerator {
 
   async startConnector(connectorId: number): Promise<void> {
     do {
+      if (this._timeToStop) {
+        logger.error(this._logPrefix(connectorId) + ' Entered in transaction loop while a request to stop it was made');
+        break;
+      }
+      if (!this._chargingStation._isRegistered()) {
+        logger.error(this._logPrefix(connectorId) + ' Entered in transaction loop while the charging station is not registered');
+        break;
+      }
+      if (!this._chargingStation._isChargingStationAvailable() || !this._chargingStation._isConnectorAvailable(connectorId)) {
+        logger.error(this._logPrefix(connectorId) + ' Entered in transaction loop while the charging station or connector is unavailable');
+        break;
+      }
       const wait = Utils.getRandomInt(this._chargingStation.stationInfo.AutomaticTransactionGenerator.maxDelayBetweenTwoTransactions,
         this._chargingStation.stationInfo.AutomaticTransactionGenerator.minDelayBetweenTwoTransactions) * 1000;
       logger.info(this._logPrefix(connectorId) + ' wait for ' + Utils.milliSecondsToHHMMSS(wait));
       await Utils.sleep(wait);
-      if (this._timeToStop) {
-        logger.debug(this._logPrefix(connectorId) + ' Entered in transaction loop while a request to stop it was made');
-        break;
-      }
       const start = Math.random();
       let skip = 0;
       if (start < this._chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) {
@@ -114,7 +122,7 @@ export default class AutomaticTransactionGenerator {
   }
 
   // eslint-disable-next-line consistent-this
-  async startTransaction(connectorId: number, self: AutomaticTransactionGenerator): Promise<StartTransactionResponse> {
+  private async startTransaction(connectorId: number, self: AutomaticTransactionGenerator): Promise<StartTransactionResponse> {
     if (self._chargingStation.hasAuthorizedTags()) {
       const tagId = self._chargingStation.getRandomTagId();
       logger.info(self._logPrefix(connectorId) + ' start transaction for tagID ' + tagId);
@@ -125,7 +133,7 @@ export default class AutomaticTransactionGenerator {
   }
 
   // eslint-disable-next-line consistent-this
-  async stopTransaction(connectorId: number, self: AutomaticTransactionGenerator): Promise<StopTransactionResponse> {
+  private async stopTransaction(connectorId: number, self: AutomaticTransactionGenerator): Promise<StopTransactionResponse> {
     return await self._chargingStation.sendStopTransaction(self._chargingStation.getConnector(connectorId).transactionId);
   }
 }
index 5bf359b632f9c906923b7304daac47ebfedd28ea..8c0b04c14eef985978a83d24ff2f4151e7d80247 100644 (file)
@@ -304,6 +304,10 @@ export default class ChargingStation {
     return this.getConnector(id).availability === AvailabilityType.OPERATIVE;
   }
 
+  _isChargingStationAvailable(): boolean {
+    return this.getConnector(0).availability === AvailabilityType.OPERATIVE;
+  }
+
   _getTemplateMaxNumberOfConnectors(): number {
     return Object.keys(this._stationInfo.Connectors).length;
   }
@@ -1013,7 +1017,11 @@ export default class ChargingStation {
       return;
     }
     if (payload.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
-      await this.sendStatusNotification(transactionConnectorId, ChargePointStatus.AVAILABLE);
+      if (!this._isChargingStationAvailable() || !this._isConnectorAvailable(transactionConnectorId)) {
+        await this.sendStatusNotification(transactionConnectorId, ChargePointStatus.UNAVAILABLE);
+      } else {
+        await this.sendStatusNotification(transactionConnectorId, ChargePointStatus.AVAILABLE);
+      }
       if (this._stationInfo.powerSharedByConnectors) {
         this._stationInfo.powerDivider--;
       }
@@ -1230,7 +1238,6 @@ export default class ChargingStation {
     return Constants.OCPP_CHARGING_PROFILE_RESPONSE_ACCEPTED;
   }
 
-  // FIXME: Handle properly the transaction started case
   handleRequestChangeAvailability(commandPayload: ChangeAvailabilityRequest): ChangeAvailabilityResponse {
     const connectorId: number = commandPayload.connectorId;
     if (!this.getConnector(connectorId)) {
@@ -1245,13 +1252,12 @@ export default class ChargingStation {
           response = Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
         }
         this.getConnector(Utils.convertToInt(connector)).availability = commandPayload.type;
-        void this.sendStatusNotification(Utils.convertToInt(connector), chargePointStatus);
+        response === Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED && this.sendStatusNotification(Utils.convertToInt(connector), chargePointStatus);
       }
       return response;
     } else if (connectorId > 0 && (this.getConnector(0).availability === AvailabilityType.OPERATIVE || (this.getConnector(0).availability === AvailabilityType.INOPERATIVE && commandPayload.type === AvailabilityType.INOPERATIVE))) {
       if (this.getConnector(connectorId)?.transactionStarted) {
         this.getConnector(connectorId).availability = commandPayload.type;
-        void this.sendStatusNotification(connectorId, chargePointStatus);
         return Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED;
       }
       this.getConnector(connectorId).availability = commandPayload.type;
@@ -1263,23 +1269,27 @@ export default class ChargingStation {
 
   async handleRequestRemoteStartTransaction(commandPayload: RemoteStartTransactionRequest): Promise<DefaultResponse> {
     const transactionConnectorID: number = commandPayload.connectorId ? commandPayload.connectorId : 1;
-    if (this._getAuthorizeRemoteTxRequests() && this._getLocalAuthListEnabled() && this.hasAuthorizedTags()) {
-      // Check if authorized
-      if (this._authorizedTags.find((value) => value === commandPayload.idTag)) {
-        await this.sendStatusNotification(transactionConnectorID, ChargePointStatus.PREPARING);
-        // Authorization successful start transaction
-        await this.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
-        logger.debug(this._logPrefix() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID.toString() + ' for idTag ' + commandPayload.idTag);
-        return Constants.OCPP_RESPONSE_ACCEPTED;
+    if (this._isChargingStationAvailable() && this._isConnectorAvailable(transactionConnectorID)) {
+      if (this._getAuthorizeRemoteTxRequests() && this._getLocalAuthListEnabled() && this.hasAuthorizedTags()) {
+        // Check if authorized
+        if (this._authorizedTags.find((value) => value === commandPayload.idTag)) {
+          await this.sendStatusNotification(transactionConnectorID, ChargePointStatus.PREPARING);
+          // Authorization successful start transaction
+          await this.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
+          logger.debug(this._logPrefix() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID.toString() + ' for idTag ' + commandPayload.idTag);
+          return Constants.OCPP_RESPONSE_ACCEPTED;
+        }
+        logger.error(this._logPrefix() + ' Remote starting transaction REJECTED on connector Id ' + transactionConnectorID.toString() + ', idTag ' + commandPayload.idTag);
+        return Constants.OCPP_RESPONSE_REJECTED;
       }
-      logger.error(this._logPrefix() + ' Remote starting transaction REJECTED, idTag ' + commandPayload.idTag);
-      return Constants.OCPP_RESPONSE_REJECTED;
+      await this.sendStatusNotification(transactionConnectorID, ChargePointStatus.PREPARING);
+      // No local authorization check required => start transaction
+      await this.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
+      logger.debug(this._logPrefix() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID.toString() + ' for idTag ' + commandPayload.idTag);
+      return Constants.OCPP_RESPONSE_ACCEPTED;
     }
-    await this.sendStatusNotification(transactionConnectorID, ChargePointStatus.PREPARING);
-    // No local authorization check required => start transaction
-    await this.sendStartTransaction(transactionConnectorID, commandPayload.idTag);
-    logger.debug(this._logPrefix() + ' Transaction remotely STARTED on ' + this._stationInfo.name + '#' + transactionConnectorID.toString() + ' for idTag ' + commandPayload.idTag);
-    return Constants.OCPP_RESPONSE_ACCEPTED;
+    logger.error(this._logPrefix() + ' Remote starting transaction REJECTED on unavailable connector Id ' + transactionConnectorID.toString() + ', idTag ' + commandPayload.idTag);
+    return Constants.OCPP_RESPONSE_REJECTED;
   }
 
   async handleRequestRemoteStopTransaction(commandPayload: RemoteStopTransactionRequest): Promise<DefaultResponse> {
index 878d49d63e473572527f35bcaf8d208e4fa04476..16cb7fa3fca34cd39f803607ec6b70fb3f8dfa78 100644 (file)
@@ -205,7 +205,7 @@ export default class Utils {
       }
     }
     if (!Utils.isUndefined(WebSocketCloseEventStatusString[code])) {
-      return WebSocketCloseEventStatusString[code];
+      return WebSocketCloseEventStatusString[code] as string;
     }
     return '(Unknown)';
   }