Allow to specify more OCPP command payload fields while using the UI
[e-mobility-charging-stations-simulator.git] / src / charging-station / ocpp / 1.6 / OCPP16ResponseService.ts
index f72f2c3ccc73b48cd3092920d6e027796a4f5284..0af8ad16f046f80318a8f47beb09c1606e95be2f 100644 (file)
@@ -1,4 +1,4 @@
-// Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
+// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
 
 import fs from 'fs';
 import path from 'path';
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'url';
 
 import type { JSONSchemaType } from 'ajv';
 
+import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
 import OCPPError from '../../../exception/OCPPError';
 import type { JsonObject, JsonType } from '../../../types/JsonType';
 import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointErrorCode';
@@ -16,39 +17,59 @@ import type {
   OCPP16MeterValuesResponse,
 } from '../../../types/ocpp/1.6/MeterValues';
 import {
-  OCPP16BootNotificationRequest,
+  type OCPP16BootNotificationRequest,
+  OCPP16IncomingRequestCommand,
   OCPP16RequestCommand,
-  OCPP16StatusNotificationRequest,
+  type OCPP16StatusNotificationRequest,
 } from '../../../types/ocpp/1.6/Requests';
-import {
-  DiagnosticsStatusNotificationResponse,
+import type {
+  ChangeAvailabilityResponse,
+  ChangeConfigurationResponse,
+  ClearChargingProfileResponse,
+  GetConfigurationResponse,
+  GetDiagnosticsResponse,
   OCPP16BootNotificationResponse,
+  OCPP16DataTransferResponse,
+  OCPP16DiagnosticsStatusNotificationResponse,
+  OCPP16FirmwareStatusNotificationResponse,
   OCPP16HeartbeatResponse,
-  OCPP16RegistrationStatus,
   OCPP16StatusNotificationResponse,
+  OCPP16TriggerMessageResponse,
+  OCPP16UpdateFirmwareResponse,
+  SetChargingProfileResponse,
+  UnlockConnectorResponse,
 } from '../../../types/ocpp/1.6/Responses';
 import {
   OCPP16AuthorizationStatus,
-  OCPP16AuthorizeRequest,
-  OCPP16AuthorizeResponse,
-  OCPP16StartTransactionRequest,
-  OCPP16StartTransactionResponse,
-  OCPP16StopTransactionRequest,
-  OCPP16StopTransactionResponse,
+  type OCPP16AuthorizeRequest,
+  type OCPP16AuthorizeResponse,
+  type OCPP16StartTransactionRequest,
+  type OCPP16StartTransactionResponse,
+  type OCPP16StopTransactionRequest,
+  type OCPP16StopTransactionResponse,
 } from '../../../types/ocpp/1.6/Transaction';
 import { ErrorType } from '../../../types/ocpp/ErrorType';
-import type { ResponseHandler } from '../../../types/ocpp/Responses';
+import { OCPPVersion } from '../../../types/ocpp/OCPPVersion';
+import {
+  type GenericResponse,
+  RegistrationStatusEnumType,
+  type ResponseHandler,
+} from '../../../types/ocpp/Responses';
 import Constants from '../../../utils/Constants';
 import logger from '../../../utils/Logger';
 import Utils from '../../../utils/Utils';
 import type ChargingStation from '../../ChargingStation';
 import { ChargingStationConfigurationUtils } from '../../ChargingStationConfigurationUtils';
 import OCPPResponseService from '../OCPPResponseService';
-import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
 
 const moduleName = 'OCPP16ResponseService';
 
 export default class OCPP16ResponseService extends OCPPResponseService {
+  public jsonIncomingRequestResponseSchemas: Map<
+    OCPP16IncomingRequestCommand,
+    JSONSchemaType<JsonObject>
+  >;
+
   private responseHandlers: Map<OCPP16RequestCommand, ResponseHandler>;
   private jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>;
 
@@ -56,7 +77,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
     if (new.target?.name === moduleName) {
       throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
     }
-    super();
+    super(OCPPVersion.VERSION_16);
     this.responseHandlers = new Map<OCPP16RequestCommand, ResponseHandler>([
       [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)],
       [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this)],
@@ -66,103 +87,155 @@ export default class OCPP16ResponseService extends OCPPResponseService {
       [OCPP16RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
       [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this)],
       [OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
+      [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this)],
+      [OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
     ]);
     this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
       [
         OCPP16RequestCommand.BOOT_NOTIFICATION,
-        JSON.parse(
-          fs.readFileSync(
-            path.resolve(
-              path.dirname(fileURLToPath(import.meta.url)),
-              '../../../assets/json-schemas/ocpp/1.6/BootNotificationResponse.json'
-            ),
-            'utf8'
-          )
-        ) as JSONSchemaType<OCPP16BootNotificationResponse>,
+        this.parseJsonSchemaFile<OCPP16BootNotificationResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/BootNotificationResponse.json'
+        ),
       ],
       [
         OCPP16RequestCommand.HEARTBEAT,
-        JSON.parse(
-          fs.readFileSync(
-            path.resolve(
-              path.dirname(fileURLToPath(import.meta.url)),
-              '../../../assets/json-schemas/ocpp/1.6/HeartbeatResponse.json'
-            ),
-            'utf8'
-          )
-        ) as JSONSchemaType<OCPP16HeartbeatResponse>,
+        this.parseJsonSchemaFile<OCPP16HeartbeatResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/HeartbeatResponse.json'
+        ),
       ],
       [
         OCPP16RequestCommand.AUTHORIZE,
-        JSON.parse(
-          fs.readFileSync(
-            path.resolve(
-              path.dirname(fileURLToPath(import.meta.url)),
-              '../../../assets/json-schemas/ocpp/1.6/AuthorizeResponse.json'
-            ),
-            'utf8'
-          )
-        ) as JSONSchemaType<OCPP16AuthorizeResponse>,
+        this.parseJsonSchemaFile<OCPP16AuthorizeResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/AuthorizeResponse.json'
+        ),
       ],
       [
         OCPP16RequestCommand.START_TRANSACTION,
-        JSON.parse(
-          fs.readFileSync(
-            path.resolve(
-              path.dirname(fileURLToPath(import.meta.url)),
-              '../../../assets/json-schemas/ocpp/1.6/StartTransactionResponse.json'
-            ),
-            'utf8'
-          )
-        ) as JSONSchemaType<OCPP16StartTransactionResponse>,
+        this.parseJsonSchemaFile<OCPP16StartTransactionResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/StartTransactionResponse.json'
+        ),
       ],
       [
         OCPP16RequestCommand.STOP_TRANSACTION,
-        JSON.parse(
-          fs.readFileSync(
-            path.resolve(
-              path.dirname(fileURLToPath(import.meta.url)),
-              '../../../assets/json-schemas/ocpp/1.6/StopTransactionResponse.json'
-            ),
-            'utf8'
-          )
-        ) as JSONSchemaType<OCPP16StopTransactionResponse>,
+        this.parseJsonSchemaFile<OCPP16StopTransactionResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/StopTransactionResponse.json'
+        ),
       ],
       [
         OCPP16RequestCommand.STATUS_NOTIFICATION,
-        JSON.parse(
-          fs.readFileSync(
-            path.resolve(
-              path.dirname(fileURLToPath(import.meta.url)),
-              '../../../assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json'
-            ),
-            'utf8'
-          )
-        ) as JSONSchemaType<OCPP16StatusNotificationResponse>,
+        this.parseJsonSchemaFile<OCPP16StatusNotificationResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json'
+        ),
       ],
       [
         OCPP16RequestCommand.METER_VALUES,
-        JSON.parse(
-          fs.readFileSync(
-            path.resolve(
-              path.dirname(fileURLToPath(import.meta.url)),
-              '../../../assets/json-schemas/ocpp/1.6/MeterValuesResponse.json'
-            ),
-            'utf8'
-          )
-        ) as JSONSchemaType<OCPP16MeterValuesResponse>,
+        this.parseJsonSchemaFile<OCPP16MeterValuesResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/MeterValuesResponse.json'
+        ),
       ],
       [
         OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
-        JSON.parse(
-          fs.readFileSync(
-            path.resolve(
-              path.dirname(fileURLToPath(import.meta.url)),
-              '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json'
-            ),
-            'utf8'
-          )
-        ) as JSONSchemaType<DiagnosticsStatusNotificationResponse>,
+        this.parseJsonSchemaFile<OCPP16DiagnosticsStatusNotificationResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json'
+        ),
+      ],
+      [
+        OCPP16RequestCommand.DATA_TRANSFER,
+        this.parseJsonSchemaFile<OCPP16DataTransferResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json'
+        ),
+      ],
+      [
+        OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
+        this.parseJsonSchemaFile<OCPP16FirmwareStatusNotificationResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json'
+        ),
+      ],
+    ]);
+    this.jsonIncomingRequestResponseSchemas = new Map([
+      [
+        OCPP16IncomingRequestCommand.RESET,
+        this.parseJsonSchemaFile<GenericResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/ResetResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.CLEAR_CACHE,
+        this.parseJsonSchemaFile<GenericResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/ClearCacheResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
+        this.parseJsonSchemaFile<ChangeAvailabilityResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
+        this.parseJsonSchemaFile<UnlockConnectorResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.GET_CONFIGURATION,
+        this.parseJsonSchemaFile<GetConfigurationResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
+        this.parseJsonSchemaFile<ChangeConfigurationResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
+        this.parseJsonSchemaFile<SetChargingProfileResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
+        this.parseJsonSchemaFile<ClearChargingProfileResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
+        this.parseJsonSchemaFile<GenericResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
+        this.parseJsonSchemaFile<GenericResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
+        this.parseJsonSchemaFile<GetDiagnosticsResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
+        this.parseJsonSchemaFile<OCPP16TriggerMessageResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.DATA_TRANSFER,
+        this.parseJsonSchemaFile<OCPP16DataTransferResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/DataTransferResponse.json'
+        ),
+      ],
+      [
+        OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
+        this.parseJsonSchemaFile<OCPP16UpdateFirmwareResponse>(
+          '../../../assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json'
+        ),
       ],
     ]);
     this.validatePayload.bind(this);
@@ -174,10 +247,13 @@ export default class OCPP16ResponseService extends OCPPResponseService {
     payload: JsonType,
     requestPayload: JsonType
   ): Promise<void> {
-    if (chargingStation.isRegistered() || commandName === OCPP16RequestCommand.BOOT_NOTIFICATION) {
+    if (
+      chargingStation.isRegistered() === true ||
+      commandName === OCPP16RequestCommand.BOOT_NOTIFICATION
+    ) {
       if (
-        this.responseHandlers.has(commandName) &&
-        OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName)
+        this.responseHandlers.has(commandName) === true &&
+        OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true
       ) {
         try {
           this.validatePayload(chargingStation, commandName, payload);
@@ -209,7 +285,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
           payload,
           null,
           2
-        )} while the charging station is not registered on the central server. `,
+        )} while the charging station is not registered on the central server.`,
         commandName,
         payload
       );
@@ -221,7 +297,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
     commandName: OCPP16RequestCommand,
     payload: JsonType
   ): boolean {
-    if (this.jsonSchemas.has(commandName)) {
+    if (this.jsonSchemas.has(commandName) === true) {
       return this.validateResponsePayload(
         chargingStation,
         commandName,
@@ -230,7 +306,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
       );
     }
     logger.warn(
-      `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command ${commandName} PDU validation`
+      `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`
     );
     return false;
   }
@@ -239,7 +315,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
     chargingStation: ChargingStation,
     payload: OCPP16BootNotificationResponse
   ): void {
-    if (payload.status === OCPP16RegistrationStatus.ACCEPTED) {
+    if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
       ChargingStationConfigurationUtils.addConfigurationKey(
         chargingStation,
         OCPP16StandardParametersKey.HeartbeatInterval,
@@ -258,11 +334,11 @@ export default class OCPP16ResponseService extends OCPPResponseService {
         ? chargingStation.restartHeartbeat()
         : chargingStation.startHeartbeat();
     }
-    if (Object.values(OCPP16RegistrationStatus).includes(payload.status)) {
+    if (Object.values(RegistrationStatusEnumType).includes(payload.status)) {
       const logMsg = `${chargingStation.logPrefix()} Charging station in '${
         payload.status
       }' state on the central server`;
-      payload.status === OCPP16RegistrationStatus.REJECTED
+      payload.status === RegistrationStatusEnumType.REJECTED
         ? logger.warn(logMsg)
         : logger.info(logMsg);
     } else {
@@ -335,8 +411,8 @@ export default class OCPP16ResponseService extends OCPPResponseService {
     }
     if (
       chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted === true &&
-      chargingStation.getAuthorizeRemoteTxRequests() &&
-      chargingStation.getLocalAuthListEnabled() &&
+      chargingStation.getAuthorizeRemoteTxRequests() === true &&
+      chargingStation.getLocalAuthListEnabled() === true &&
       chargingStation.hasAuthorizedTags() &&
       chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized === false
     ) {
@@ -352,8 +428,8 @@ export default class OCPP16ResponseService extends OCPPResponseService {
     }
     if (
       chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted === true &&
-      chargingStation.getAuthorizeRemoteTxRequests() &&
-      chargingStation.getMustAuthorizeAtRemoteStart() &&
+      chargingStation.getAuthorizeRemoteTxRequests() === true &&
+      chargingStation.getMustAuthorizeAtRemoteStart() === true &&
       chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized === false &&
       chargingStation.getConnectorStatus(connectorId).idTagAuthorized === false
     ) {
@@ -539,9 +615,9 @@ export default class OCPP16ResponseService extends OCPPResponseService {
       return;
     }
     if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
-      chargingStation.getBeginEndMeterValues() &&
-        !chargingStation.getOcppStrictCompliance() &&
-        chargingStation.getOutOfOrderEndMeterValues() &&
+      chargingStation.getBeginEndMeterValues() === true &&
+        chargingStation.getOcppStrictCompliance() === false &&
+        chargingStation.getOutOfOrderEndMeterValues() === true &&
         (await chargingStation.ocppRequestService.requestHandler<
           OCPP16MeterValuesRequest,
           OCPP16MeterValuesResponse
@@ -557,8 +633,8 @@ export default class OCPP16ResponseService extends OCPPResponseService {
           ],
         }));
       if (
-        !chargingStation.isChargingStationAvailable() ||
-        !chargingStation.isConnectorAvailable(transactionConnectorId)
+        chargingStation.isChargingStationAvailable() === false ||
+        chargingStation.isConnectorAvailable(transactionConnectorId) === false
       ) {
         await chargingStation.ocppRequestService.requestHandler<
           OCPP16StatusNotificationRequest,
@@ -606,4 +682,13 @@ export default class OCPP16ResponseService extends OCPPResponseService {
       );
     }
   }
+
+  private parseJsonSchemaFile<T extends JsonType>(relativePath: string): JSONSchemaType<T> {
+    return JSON.parse(
+      fs.readFileSync(
+        path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath),
+        'utf8'
+      )
+    ) as JSONSchemaType<T>;
+  }
 }