Use a map a register response and incoming request handlers
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 29 Sep 2021 21:11:45 +0000 (23:11 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 29 Sep 2021 21:11:45 +0000 (23:11 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/1.6/OCPP16RequestService.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/types/ocpp/Requests.ts
src/types/ocpp/Responses.ts

index 9f00f9fd9e18a79b1a8a8012c52fac16ae0536d2..150a41b10ffdd8cd8797045959bcb1d08f075f1c 100644 (file)
@@ -1,15 +1,16 @@
 // Partial Copyright Jerome Benoit. 2021. All Rights Reserved.
 
-import { ChangeAvailabilityRequest, ChangeConfigurationRequest, ClearChargingProfileRequest, GetConfigurationRequest, GetDiagnosticsRequest, MessageTrigger, OCPP16AvailabilityType, OCPP16IncomingRequestCommand, OCPP16TriggerMessageRequest, RemoteStartTransactionRequest, RemoteStopTransactionRequest, ResetRequest, SetChargingProfileRequest, UnlockConnectorRequest } from '../../../types/ocpp/1.6/Requests';
+import { ChangeAvailabilityRequest, ChangeConfigurationRequest, ClearChargingProfileRequest, GetConfigurationRequest, GetDiagnosticsRequest, MessageTrigger, OCPP16AvailabilityType, OCPP16IncomingRequestCommand, OCPP16RequestCommand, OCPP16TriggerMessageRequest, RemoteStartTransactionRequest, RemoteStopTransactionRequest, ResetRequest, SetChargingProfileRequest, UnlockConnectorRequest } from '../../../types/ocpp/1.6/Requests';
 import { ChangeAvailabilityResponse, ChangeConfigurationResponse, ClearChargingProfileResponse, GetConfigurationResponse, GetDiagnosticsResponse, OCPP16TriggerMessageResponse, SetChargingProfileResponse, UnlockConnectorResponse } from '../../../types/ocpp/1.6/Responses';
 import { ChargingProfilePurposeType, OCPP16ChargingProfile } from '../../../types/ocpp/1.6/ChargingProfile';
 import { Client, FTPResponse } from 'basic-ftp';
-import { IncomingRequestCommand, RequestCommand } from '../../../types/ocpp/Requests';
 import { OCPP16AuthorizationStatus, OCPP16StopTransactionReason } from '../../../types/ocpp/1.6/Transaction';
 
+import ChargingStation from '../../ChargingStation';
 import Constants from '../../../utils/Constants';
 import { DefaultResponse } from '../../../types/ocpp/Responses';
 import { ErrorType } from '../../../types/ocpp/ErrorType';
+import { IncomingRequestHandler } from '../../../types/ocpp/Requests';
 import { MessageType } from '../../../types/ocpp/MessageType';
 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
 import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
@@ -25,14 +26,31 @@ import path from 'path';
 import tar from 'tar';
 
 export default class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
+  private incomingRequestHandlers: Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>;
+
+  constructor(chargingStation: ChargingStation) {
+    super(chargingStation);
+    this.incomingRequestHandlers = new Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>([
+      [OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)],
+      [OCPP16IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)],
+      [OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, this.handleRequestUnlockConnector.bind(this)],
+      [OCPP16IncomingRequestCommand.GET_CONFIGURATION, this.handleRequestGetConfiguration.bind(this)],
+      [OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, this.handleRequestChangeConfiguration.bind(this)],
+      [OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, this.handleRequestSetChargingProfile.bind(this)],
+      [OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, this.handleRequestClearChargingProfile.bind(this)],
+      [OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, this.handleRequestChangeAvailability.bind(this)],
+      [OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, this.handleRequestRemoteStartTransaction.bind(this)],
+      [OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, this.handleRequestRemoteStopTransaction.bind(this)],
+      [OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)]
+    ]);
+  }
+
   public async handleRequest(messageId: string, commandName: OCPP16IncomingRequestCommand, commandPayload: Record<string, unknown>): Promise<void> {
     let response: Record<string, unknown>;
-    const methodName = `handleRequest${commandName}`;
-    // Call
-    if (typeof this[methodName] === 'function') {
+    if (this.incomingRequestHandlers.has(commandName)) {
       try {
         // Call the method to build the response
-        response = await this[methodName](commandPayload);
+        response = await this.incomingRequestHandlers.get(commandName)(commandPayload);
       } catch (error) {
         // Log
         logger.error(this.chargingStation.logPrefix() + ' Handle request error: %j', error);
@@ -130,10 +148,10 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
   private handleRequestChangeConfiguration(commandPayload: ChangeConfigurationRequest): ChangeConfigurationResponse {
     // JSON request fields type sanity check
     if (!Utils.isString(commandPayload.key)) {
-      logger.error(`${this.chargingStation.logPrefix()} ${RequestCommand.CHANGE_CONFIGURATION} request key field is not a string:`, commandPayload);
+      logger.error(`${this.chargingStation.logPrefix()} ${OCPP16RequestCommand.CHANGE_CONFIGURATION} request key field is not a string:`, commandPayload);
     }
     if (!Utils.isString(commandPayload.value)) {
-      logger.error(`${this.chargingStation.logPrefix()} ${RequestCommand.CHANGE_CONFIGURATION} request value field is not a string:`, commandPayload);
+      logger.error(`${this.chargingStation.logPrefix()} ${OCPP16RequestCommand.CHANGE_CONFIGURATION} request value field is not a string:`, commandPayload);
     }
     const keyToChange = this.chargingStation.getConfigurationKey(commandPayload.key, true);
     if (!keyToChange) {
@@ -348,7 +366,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
   }
 
   private async handleRequestGetDiagnostics(commandPayload: GetDiagnosticsRequest): Promise<GetDiagnosticsResponse> {
-    logger.debug(this.chargingStation.logPrefix() + ' ' + IncomingRequestCommand.GET_DIAGNOSTICS + ' request received: %j', commandPayload);
+    logger.debug(this.chargingStation.logPrefix() + ' ' + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ' request received: %j', commandPayload);
     const uri = new URL(commandPayload.location);
     if (uri.protocol.startsWith('ftp:')) {
       let ftpClient: Client;
@@ -378,15 +396,15 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
             }
             return { fileName: diagnosticsArchive };
           }
-          throw new OCPPError(ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`, IncomingRequestCommand.GET_DIAGNOSTICS);
+          throw new OCPPError(ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`, OCPP16IncomingRequestCommand.GET_DIAGNOSTICS);
         }
-        throw new OCPPError(ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`, IncomingRequestCommand.GET_DIAGNOSTICS);
+        throw new OCPPError(ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`, OCPP16IncomingRequestCommand.GET_DIAGNOSTICS);
       } catch (error) {
         await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.UploadFailed);
         if (ftpClient) {
           ftpClient.close();
         }
-        return this.handleIncomingRequestError(IncomingRequestCommand.GET_DIAGNOSTICS, error, Constants.OCPP_RESPONSE_EMPTY);
+        return this.handleIncomingRequestError(OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, error, Constants.OCPP_RESPONSE_EMPTY);
       }
     } else {
       logger.error(`${this.chargingStation.logPrefix()} Unsupported protocol ${uri.protocol} to transfer the diagnostic logs archive`);
@@ -414,7 +432,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
           return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
       }
     } catch (error) {
-      return this.handleIncomingRequestError(IncomingRequestCommand.TRIGGER_MESSAGE, error, Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED);
+      return this.handleIncomingRequestError(OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, error, Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED);
     }
   }
 }
index a8fb489093e8464a191dcb82316e9fbe231a3bdd..d08404c9d8b691ca15b5d7fd1a16fa176031a683 100644 (file)
@@ -18,7 +18,6 @@ import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStat
 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
 import OCPPError from '../OCPPError';
 import OCPPRequestService from '../OCPPRequestService';
-import { RequestCommand } from '../../../types/ocpp/Requests';
 import Utils from '../../../utils/Utils';
 import logger from '../../../utils/Logger';
 
@@ -222,7 +221,7 @@ export default class OCPP16RequestService extends OCPPRequestService {
             break;
           default:
             logger.error(errMsg);
-            throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES);
+            throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
         }
         meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(powerSampledValueTemplate, powerMeasurandValues.allPhases));
         const sampledValuesIndex = meterValue.sampledValue.length - 1;
@@ -288,7 +287,7 @@ export default class OCPP16RequestService extends OCPPRequestService {
             break;
           default:
             logger.error(errMsg);
-            throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES);
+            throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES);
         }
         meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(currentSampledValueTemplate, currentMeasurandValues.allPhases));
         const sampledValuesIndex = meterValue.sampledValue.length - 1;
index 5d1b04e92a747b733d8dafd9bbc1eb414d8619dc..7dac13e3a117e82c7e46f0663b0018250a241175 100644 (file)
@@ -5,20 +5,36 @@ import { HeartbeatRequest, OCPP16RequestCommand, StatusNotificationRequest } fro
 import { HeartbeatResponse, OCPP16BootNotificationResponse, OCPP16RegistrationStatus, StatusNotificationResponse } from '../../../types/ocpp/1.6/Responses';
 import { MeterValuesRequest, MeterValuesResponse } from '../../../types/ocpp/1.6/MeterValues';
 
+import ChargingStation from '../../ChargingStation';
 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
 import { OCPP16StandardParametersKey } from '../../../types/ocpp/1.6/Configuration';
 import OCPPResponseService from '../OCPPResponseService';
+import { ResponseHandler } from '../../../types/ocpp/Responses';
 import Utils from '../../../utils/Utils';
 import logger from '../../../utils/Logger';
 
 export default class OCPP16ResponseService extends OCPPResponseService {
+  private responseHandlers: Map<OCPP16RequestCommand, ResponseHandler>;
+
+  constructor(chargingStation: ChargingStation) {
+    super(chargingStation);
+    this.responseHandlers = new Map<OCPP16RequestCommand, ResponseHandler>([
+      [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)],
+      [OCPP16RequestCommand.HEARTBEAT, this.handleResponseHeartbeat.bind(this)],
+      [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this)],
+      [OCPP16RequestCommand.START_TRANSACTION, this.handleResponseStartTransaction.bind(this)],
+      [OCPP16RequestCommand.STOP_TRANSACTION, this.handleResponseStopTransaction.bind(this)],
+      [OCPP16RequestCommand.STATUS_NOTIFICATION, this.handleResponseStatusNotification.bind(this)],
+      [OCPP16RequestCommand.METER_VALUES, this.handleResponseMeterValues.bind(this)],
+    ]);
+  }
+
   public async handleResponse(commandName: OCPP16RequestCommand, payload: Record<string, unknown> | string, requestPayload: Record<string, unknown>): Promise<void> {
-    const responseCallbackMethodName = `handleResponse${commandName}`;
-    if (typeof this[responseCallbackMethodName] === 'function') {
-      await this[responseCallbackMethodName](payload, requestPayload);
+    if (this.responseHandlers.has(commandName)) {
+      await this.responseHandlers.get(commandName)(payload, requestPayload);
     } else {
-      logger.error(this.chargingStation.logPrefix() + ' Trying to call an undefined response callback method: ' + responseCallbackMethodName);
+      logger.error(this.chargingStation.logPrefix() + ' Trying to call an undefined method for command ' + commandName + ' response');
     }
   }
 
@@ -34,6 +50,28 @@ export default class OCPP16ResponseService extends OCPPResponseService {
     }
   }
 
+  private handleResponseHeartbeat(payload: HeartbeatResponse, requestPayload: HeartbeatRequest): void {
+    logger.debug(this.chargingStation.logPrefix() + ' Heartbeat response received: %j to Heartbeat request: %j', payload, requestPayload);
+  }
+
+  private handleResponseAuthorize(payload: OCPP16AuthorizeResponse, requestPayload: AuthorizeRequest): void {
+    let authorizeConnectorId: number;
+    for (const connector in this.chargingStation.connectors) {
+      if (Utils.convertToInt(connector) > 0 && this.chargingStation.getConnector(Utils.convertToInt(connector))?.authorizeIdTag === requestPayload.idTag) {
+        authorizeConnectorId = Utils.convertToInt(connector);
+        break;
+      }
+    }
+    if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
+      this.chargingStation.getConnector(authorizeConnectorId).authorized = true;
+      logger.debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} authorized on connector ${authorizeConnectorId}`);
+    } else {
+      this.chargingStation.getConnector(authorizeConnectorId).authorized = false;
+      delete this.chargingStation.getConnector(authorizeConnectorId).authorizeIdTag;
+      logger.debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status ${payload.idTagInfo.status} on connector ${authorizeConnectorId}`);
+    }
+  }
+
   private async handleResponseStartTransaction(payload: OCPP16StartTransactionResponse, requestPayload: StartTransactionRequest): Promise<void> {
     const connectorId = requestPayload.connectorId;
 
@@ -57,7 +95,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
       return;
     }
     if (this.chargingStation.getConnector(connectorId)?.status !== OCPP16ChargePointStatus.AVAILABLE
-        && this.chargingStation.getConnector(connectorId)?.status !== OCPP16ChargePointStatus.PREPARING) {
+      && this.chargingStation.getConnector(connectorId)?.status !== OCPP16ChargePointStatus.PREPARING) {
       logger.error(`${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${this.chargingStation.getConnector(connectorId)?.status}`);
       return;
     }
@@ -133,26 +171,4 @@ export default class OCPP16ResponseService extends OCPPResponseService {
   private handleResponseMeterValues(payload: MeterValuesRequest, requestPayload: MeterValuesResponse): void {
     logger.debug(this.chargingStation.logPrefix() + ' MeterValues response received: %j to MeterValues request: %j', payload, requestPayload);
   }
-
-  private handleResponseHeartbeat(payload: HeartbeatResponse, requestPayload: HeartbeatRequest): void {
-    logger.debug(this.chargingStation.logPrefix() + ' Heartbeat response received: %j to Heartbeat request: %j', payload, requestPayload);
-  }
-
-  private handleResponseAuthorize(payload: OCPP16AuthorizeResponse, requestPayload: AuthorizeRequest): void {
-    let authorizeConnectorId: number;
-    for (const connector in this.chargingStation.connectors) {
-      if (Utils.convertToInt(connector) > 0 && this.chargingStation.getConnector(Utils.convertToInt(connector))?.authorizeIdTag === requestPayload.idTag) {
-        authorizeConnectorId = Utils.convertToInt(connector);
-        break;
-      }
-    }
-    if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
-      this.chargingStation.getConnector(authorizeConnectorId).authorized = true;
-      logger.debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} authorized on connector ${authorizeConnectorId}`);
-    } else {
-      this.chargingStation.getConnector(authorizeConnectorId).authorized = false;
-      delete this.chargingStation.getConnector(authorizeConnectorId).authorizeIdTag;
-      logger.debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status ${payload.idTagInfo.status} on connector ${authorizeConnectorId}`);
-    }
-  }
 }
index b2adad378ab71b8fe4105790cf253b54486ecec9..f32db002c98da786fb9066a4a5f0f442b131c3f6 100644 (file)
@@ -4,6 +4,8 @@ import { MessageType } from './MessageType';
 import { OCPP16DiagnosticsStatus } from './1.6/DiagnosticsStatus';
 import OCPPError from '../../charging-station/ocpp/OCPPError';
 
+export type IncomingRequestHandler = (commandPayload: Record<string, unknown>) => Record<string, unknown> | Promise<Record<string, unknown>>;
+
 export type BootNotificationRequest = OCPP16BootNotificationRequest;
 
 export type AvailabilityType = OCPP16AvailabilityType;
index 77497074221dd2a496e9e2b93ad26bc7f0d5dc5c..7a851170bea7a274c645263cbae0e1b4a0c3024b 100644 (file)
@@ -1,5 +1,7 @@
 import { OCPP16AvailabilityStatus, OCPP16BootNotificationResponse, OCPP16ChargingProfileStatus, OCPP16ClearChargingProfileStatus, OCPP16ConfigurationStatus, OCPP16RegistrationStatus, OCPP16TriggerMessageStatus, OCPP16UnlockStatus } from './1.6/Responses';
 
+export type ResponseHandler = (payload: Record<string, unknown> | string, requestPayload?: Record<string, unknown>) => void | Promise<void>;
+
 export type BootNotificationResponse = OCPP16BootNotificationResponse;
 
 export enum DefaultStatus {