UI Protocol: add Authorize command support
authorJérôme Benoit <jerome.benoit@sap.com>
Thu, 8 Sep 2022 16:21:26 +0000 (18:21 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Thu, 8 Sep 2022 16:21:26 +0000 (18:21 +0200)
And also fix connector status init

Reference #169

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/assets/ui-protocol/Insomnia_CSSimulatorUIProtocol.json
src/charging-station/ChargingStation.ts
src/charging-station/ChargingStationWorkerBroadcastChannel.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ui-server/ui-services/UIService001.ts
src/types/UIProtocol.ts
src/types/WorkerBroadcastChannel.ts

index 61dded0312a687c82188141ce5c15ce250fe39f6..5478e0fec213f9ca302bae5e27d6e636b3352a10 100644 (file)
@@ -1,13 +1,13 @@
 {
   "_type": "export",
   "__export_format": 4,
-  "__export_date": "2022-09-05T20:42:59.578Z",
+  "__export_date": "2022-09-08T16:13:30.328Z",
   "__export_source": "insomnia.desktop.app:v2022.5.1",
   "resources": [
     {
       "_id": "req_606dcee139984772877def40fcbb5c76",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662410526425,
+      "modified": 1662652240497,
       "created": 1661789624987,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/listChargingStations",
       "name": "listChargingStations",
@@ -52,7 +52,7 @@
     {
       "_id": "req_7d5f9506e7ac49208a4f960a7740663e",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662373634204,
+      "modified": 1662653581303,
       "created": 1661789624990,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/startSimulator",
       "name": "startSimulator",
@@ -87,7 +87,7 @@
     {
       "_id": "req_59056be11534481c80a0b0da32e2a06a",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662367707319,
+      "modified": 1662653585962,
       "created": 1661789624994,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/stopSimulator",
       "name": "stopSimulator",
     {
       "_id": "req_aad7fd6db4c64869b60048b915010efc",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662372497848,
+      "modified": 1662649189382,
       "created": 1661789624998,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/startChargingStation",
       "name": "startChargingStation",
     {
       "_id": "req_d72d91cf3fb044179b8ae9d92a74f99c",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662372491437,
+      "modified": 1662653577920,
       "created": 1661789625002,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/stopChargingStation",
       "name": "stopChargingStation",
     {
       "_id": "req_747f458d196f4681b5fe15204b0067aa",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662367716472,
+      "modified": 1662653576909,
       "created": 1661789625005,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/openConnection",
       "name": "openConnection",
     {
       "_id": "req_401e6a62a33c4b6c90aaa2e019daab6d",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662367720232,
+      "modified": 1662653575192,
       "created": 1661789625014,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/closeConnection",
       "name": "closeConnection",
     {
       "_id": "req_2f757efe92fb4936ad4fa4b6763f9293",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662367718288,
+      "modified": 1662653573947,
       "created": 1661789625017,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/startTransaction",
       "name": "startTransaction",
     {
       "_id": "req_7c285fb6cb6948a08235a6c73cbeb1f9",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662367694077,
+      "modified": 1662653572192,
       "created": 1661789625020,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/stopTransaction",
       "name": "stopTransaction",
       "method": "POST",
       "body": {
         "mimeType": "application/json",
-        "text": "{\n\t\"hashIds\": [\n\t\t\"0058d8b50e422cce5bbd0c0a4ad13d5d657e8a88670dcf04c1b2b563fea3db5b96a3686278b374ed050e21baef89060e\"\n\t],\n\t\"transactionId\": 926436595\n}"
+        "text": "{\n\t\"hashIds\": [\n\t\t\"0058d8b50e422cce5bbd0c0a4ad13d5d657e8a88670dcf04c1b2b563fea3db5b96a3686278b374ed050e21baef89060e\"\n\t],\n\t\"transactionId\": 1906670842\n}"
       },
       "parameters": [],
       "headers": [
     {
       "_id": "req_b33c704fe3464dc5a5d3694abd9320d0",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662372502338,
+      "modified": 1662653571010,
       "created": 1661803778569,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/startAutomaticTransactionGenerator",
       "name": "startAutomaticTransactionGenerator",
     {
       "_id": "req_24c1c55fe3ba4ddb94702408f21a64df",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662372479288,
+      "modified": 1662653569672,
       "created": 1661803846882,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/stopAutomaticTransactionGenerator",
       "name": "stopAutomaticTransactionGenerator",
     {
       "_id": "req_6a78267706094fb59d85ed1531e07a55",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662410523458,
+      "modified": 1662653568405,
       "created": 1662330215407,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/statusNotification",
       "name": "statusNotification",
     {
       "_id": "req_61efafe9f4a14c268b948b9f9c5c4195",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
-      "modified": 1662410531123,
+      "modified": 1662653565100,
       "created": 1662409405256,
       "url": "{{baseUrl}}/{{protocol}}/{{version}}/heartbeat",
       "name": "heartbeat",
       "settingFollowRedirects": "global",
       "_type": "request"
     },
+    {
+      "_id": "req_9633f79d949d491e8b6892eed08bd198",
+      "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
+      "modified": 1662653567170,
+      "created": 1662648910935,
+      "url": "{{baseUrl}}/{{protocol}}/{{version}}/authorize",
+      "name": "authorize",
+      "description": "",
+      "method": "POST",
+      "body": {
+        "mimeType": "application/json",
+        "text": "{\n\t\"hashIds\": [\n\t\t\"0058d8b50e422cce5bbd0c0a4ad13d5d657e8a88670dcf04c1b2b563fea3db5b96a3686278b374ed050e21baef89060e\",\n\t\t\"331d024fea36e3e2483a0e5dc9376234241c8c099ad201a441437b23622c308555183f37cbc84a1818c1c45aaae50896\"\n\t],\n\t\"idTag\": \"test\"\n}"
+      },
+      "parameters": [],
+      "headers": [
+        {
+          "name": "Content-Type",
+          "value": "application/json",
+          "id": "pair_3224616dd6604605a1e48b71f6e9f795"
+        }
+      ],
+      "authentication": {
+        "type": "basic",
+        "useISO88591": false,
+        "disabled": false,
+        "username": "{{username}}",
+        "password": "{{password}}"
+      },
+      "metaSortKey": -999999450,
+      "isPrivate": false,
+      "settingStoreCookies": true,
+      "settingSendCookies": true,
+      "settingDisableRenderRequestBody": false,
+      "settingEncodeUrl": true,
+      "settingRebuildPath": true,
+      "settingFollowRedirects": "global",
+      "_type": "request"
+    },
     {
       "_id": "env_74b29d59b9f04298b97fc9750476a4ca",
       "parentId": "wrk_d64b10b1e0c14563a80484ee684b5205",
index bbe294b62ca07c7ed53292871829bd5dd9ee244d..d7b67e6a04b73aa960abc5767dec9338ea0038ee 100644 (file)
@@ -1218,7 +1218,11 @@ export default class ChargingStation {
     }
     // Initialize transaction attributes on connectors
     for (const connectorId of this.connectors.keys()) {
-      if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === false) {
+      if (
+        connectorId > 0 &&
+        (this.getConnectorStatus(connectorId).transactionStarted === undefined ||
+          this.getConnectorStatus(connectorId).transactionStarted === false)
+      ) {
         this.initializeConnectorStatus(connectorId);
       }
     }
index e36650bb3cb2ab1c4d2281e8bbd0dc2ac0cb7f63..154e1afb14bbb8631fdb570c3a95c2888e4516f2 100644 (file)
@@ -8,6 +8,8 @@ import {
 import type { HeartbeatResponse, StatusNotificationResponse } from '../types/ocpp/Responses';
 import {
   AuthorizationStatus,
+  AuthorizeRequest,
+  AuthorizeResponse,
   StartTransactionRequest,
   StartTransactionResponse,
   StopTransactionRequest,
@@ -31,6 +33,7 @@ const moduleName = 'ChargingStationWorkerBroadcastChannel';
 type CommandResponse =
   | StartTransactionResponse
   | StopTransactionResponse
+  | AuthorizeResponse
   | StatusNotificationResponse
   | HeartbeatResponse;
 
@@ -65,10 +68,7 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
           this.chargingStation.ocppRequestService.requestHandler<
             StartTransactionRequest,
             StartTransactionResponse
-          >(this.chargingStation, RequestCommand.START_TRANSACTION, {
-            connectorId: requestPayload.connectorId,
-            idTag: requestPayload.idTag,
-          }),
+          >(this.chargingStation, RequestCommand.START_TRANSACTION, requestPayload),
       ],
       [
         BroadcastChannelProcedureName.STOP_TRANSACTION,
@@ -77,13 +77,11 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
             StopTransactionRequest,
             StartTransactionResponse
           >(this.chargingStation, RequestCommand.STOP_TRANSACTION, {
-            transactionId: requestPayload.transactionId,
+            ...requestPayload,
             meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId(
               requestPayload.transactionId,
               true
             ),
-            idTag: requestPayload.idTag,
-            reason: requestPayload.reason,
           }),
       ],
       [
@@ -96,35 +94,29 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
         (requestPayload?: BroadcastChannelRequestPayload) =>
           this.chargingStation.stopAutomaticTransactionGenerator(requestPayload.connectorIds),
       ],
+      [
+        BroadcastChannelProcedureName.AUTHORIZE,
+        async (requestPayload?: BroadcastChannelRequestPayload) =>
+          this.chargingStation.ocppRequestService.requestHandler<
+            AuthorizeRequest,
+            AuthorizeResponse
+          >(this.chargingStation, RequestCommand.AUTHORIZE, requestPayload),
+      ],
       [
         BroadcastChannelProcedureName.STATUS_NOTIFICATION,
         async (requestPayload?: BroadcastChannelRequestPayload) =>
           this.chargingStation.ocppRequestService.requestHandler<
             StatusNotificationRequest,
             StatusNotificationResponse
-          >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, {
-            connectorId: requestPayload.connectorId,
-            errorCode: requestPayload.errorCode,
-            status: requestPayload.status,
-            ...(requestPayload.info && { info: requestPayload.info }),
-            ...(requestPayload.timestamp && { timestamp: requestPayload.timestamp }),
-            ...(requestPayload.vendorId && { vendorId: requestPayload.vendorId }),
-            ...(requestPayload.vendorErrorCode && {
-              vendorErrorCode: requestPayload.vendorErrorCode,
-            }),
-          }),
+          >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, requestPayload),
       ],
       [
         BroadcastChannelProcedureName.HEARTBEAT,
-        async (requestPayload?: BroadcastChannelRequestPayload) => {
-          delete requestPayload.hashId;
-          delete requestPayload.hashIds;
-          delete requestPayload.connectorIds;
-          return this.chargingStation.ocppRequestService.requestHandler<
+        async (requestPayload?: BroadcastChannelRequestPayload) =>
+          this.chargingStation.ocppRequestService.requestHandler<
             HeartbeatRequest,
             HeartbeatResponse
-          >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload);
-        },
+          >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload),
       ],
     ]);
     this.chargingStation = chargingStation;
@@ -165,10 +157,24 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
           status: ResponseStatus.SUCCESS,
         };
       } else {
-        responsePayload = {
-          hashId: this.chargingStation.stationInfo.hashId,
-          status: this.commandResponseToResponseStatus(command, commandResponse as CommandResponse),
-        };
+        const responseStatus = this.commandResponseToResponseStatus(
+          command,
+          commandResponse as CommandResponse
+        );
+        if (responseStatus === ResponseStatus.SUCCESS) {
+          responsePayload = {
+            hashId: this.chargingStation.stationInfo.hashId,
+            status: responseStatus,
+          };
+        } else {
+          responsePayload = {
+            hashId: this.chargingStation.stationInfo.hashId,
+            status: responseStatus,
+            command,
+            requestPayload,
+            commandResponse: commandResponse as CommandResponse,
+          };
+        }
       }
     } catch (error) {
       logger.error(
@@ -201,11 +207,24 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
     requestPayload: BroadcastChannelRequestPayload
   ): Promise<CommandResponse | void> {
     if (this.commandHandlers.has(command) === true) {
+      this.cleanRequestPayload(command, requestPayload);
       return this.commandHandlers.get(command)(requestPayload);
     }
     throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
   }
 
+  private cleanRequestPayload(
+    command: BroadcastChannelProcedureName,
+    requestPayload: BroadcastChannelRequestPayload
+  ): void {
+    delete requestPayload.hashId;
+    delete requestPayload.hashIds;
+    [
+      BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR,
+      BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
+    ].includes(command) === false && delete requestPayload.connectorIds;
+  }
+
   private commandResponseToResponseStatus(
     command: BroadcastChannelProcedureName,
     commandResponse: CommandResponse
@@ -213,9 +232,14 @@ export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadca
     switch (command) {
       case BroadcastChannelProcedureName.START_TRANSACTION:
       case BroadcastChannelProcedureName.STOP_TRANSACTION:
+      case BroadcastChannelProcedureName.AUTHORIZE:
         if (
-          (commandResponse as StartTransactionResponse | StopTransactionResponse)?.idTagInfo
-            ?.status === AuthorizationStatus.ACCEPTED
+          (
+            commandResponse as
+              | StartTransactionResponse
+              | StopTransactionResponse
+              | AuthorizeResponse
+          )?.idTagInfo?.status === AuthorizationStatus.ACCEPTED
         ) {
           return ResponseStatus.SUCCESS;
         }
index 3129c993ecb7abdadfbcad09a88a9b4cacd9b376..89f8ffd076a43f9e646f8c93549940cdd081cbe1 100644 (file)
@@ -289,20 +289,24 @@ export default class OCPP16ResponseService extends OCPPResponseService {
         break;
       }
     }
+    const isAuthorizeConnectorIdDefined = authorizeConnectorId !== undefined;
     if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
-      chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true;
+      isAuthorizeConnectorIdDefined &&
+        (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
       logger.debug(
-        `${chargingStation.logPrefix()} IdTag '${
-          requestPayload.idTag
-        }' authorized on connector ${authorizeConnectorId}`
+        `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' accepted${
+          isAuthorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''
+        }`
       );
     } else {
-      chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
-      delete chargingStation.getConnectorStatus(authorizeConnectorId).authorizeIdTag;
+      if (isAuthorizeConnectorIdDefined) {
+        chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
+        delete chargingStation.getConnectorStatus(authorizeConnectorId).authorizeIdTag;
+      }
       logger.debug(
-        `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' refused with status '${
+        `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' rejected with status '${
           payload.idTagInfo.status
-        }' on connector ${authorizeConnectorId}`
+        }'${isAuthorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''}`
       );
     }
   }
index 57c34c2239a6ee573d7462d5e57c96b1157f2cfc..4be6a2f2b16f1903a78c0941016a65d1ce594eb5 100644 (file)
@@ -43,6 +43,10 @@ export default class UIService001 extends AbstractUIService {
       ProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
       this.handleStopAutomaticTransactionGenerator.bind(this) as ProtocolRequestHandler
     );
+    this.requestHandlers.set(
+      ProcedureName.AUTHORIZE,
+      this.handleAuthorize.bind(this) as ProtocolRequestHandler
+    );
     this.requestHandlers.set(
       ProcedureName.STATUS_NOTIFICATION,
       this.handleStatusNotification.bind(this) as ProtocolRequestHandler
@@ -105,6 +109,10 @@ export default class UIService001 extends AbstractUIService {
     );
   }
 
+  private handleAuthorize(uuid: string, payload: RequestPayload): void {
+    this.sendBroadcastChannelRequest(uuid, BroadcastChannelProcedureName.AUTHORIZE, payload);
+  }
+
   private handleStatusNotification(uuid: string, payload: RequestPayload): void {
     this.sendBroadcastChannelRequest(
       uuid,
index 4b96262bc3126afaf3d6ad8693758b9f09f23dfa..75685141bb945eb12569aaaca795462eea23156d 100644 (file)
@@ -38,6 +38,7 @@ export enum ProcedureName {
   STOP_TRANSACTION = 'stopTransaction',
   START_AUTOMATIC_TRANSACTION_GENERATOR = 'startAutomaticTransactionGenerator',
   STOP_AUTOMATIC_TRANSACTION_GENERATOR = 'stopAutomaticTransactionGenerator',
+  AUTHORIZE = 'authorize',
   STATUS_NOTIFICATION = 'statusNotification',
   HEARTBEAT = 'heartbeat',
 }
index 788f7d68f864c63f3dce8f88e558d3fc6c94e496..be59789bf0882974a9ddf60a2eb4cd6e75640ae4 100644 (file)
@@ -16,6 +16,7 @@ export enum BroadcastChannelProcedureName {
   STOP_TRANSACTION = 'stopTransaction',
   START_AUTOMATIC_TRANSACTION_GENERATOR = 'startAutomaticTransactionGenerator',
   STOP_AUTOMATIC_TRANSACTION_GENERATOR = 'stopAutomaticTransactionGenerator',
+  AUTHORIZE = 'authorize',
   STATUS_NOTIFICATION = 'statusNotification',
   HEARTBEAT = 'heartbeat',
 }