// 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';
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);
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) {
}
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;
}
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`);
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);
}
}
}
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';
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;
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;
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');
}
}
}
}
+ 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;
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;
}
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}`);
- }
- }
}
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;
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 {