-// 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';
import { URL, fileURLToPath } from 'url';
import type { JSONSchemaType } from 'ajv';
-import { Client, FTPResponse } from 'basic-ftp';
+import { Client, type FTPResponse } from 'basic-ftp';
import tar from 'tar';
import OCPPError from '../../../exception/OCPPError';
import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
import {
ChargingProfilePurposeType,
- OCPP16ChargingProfile,
+ type OCPP16ChargingProfile,
} from '../../../types/ocpp/1.6/ChargingProfile';
import {
OCPP16StandardParametersKey,
} from '../../../types/ocpp/1.6/Configuration';
import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus';
import {
- ChangeAvailabilityRequest,
- ChangeConfigurationRequest,
- ClearChargingProfileRequest,
- DiagnosticsStatusNotificationRequest,
- GetConfigurationRequest,
- GetDiagnosticsRequest,
- MessageTrigger,
+ type ChangeAvailabilityRequest,
+ type ChangeConfigurationRequest,
+ type ClearChargingProfileRequest,
+ type DiagnosticsStatusNotificationRequest,
+ type GetConfigurationRequest,
+ type GetDiagnosticsRequest,
OCPP16AvailabilityType,
- OCPP16BootNotificationRequest,
- OCPP16ClearCacheRequest,
- OCPP16HeartbeatRequest,
+ type OCPP16BootNotificationRequest,
+ type OCPP16ClearCacheRequest,
+ type OCPP16DataTransferRequest,
+ OCPP16DataTransferVendorId,
+ type OCPP16HeartbeatRequest,
OCPP16IncomingRequestCommand,
+ OCPP16MessageTrigger,
OCPP16RequestCommand,
- OCPP16StatusNotificationRequest,
- OCPP16TriggerMessageRequest,
- RemoteStartTransactionRequest,
- RemoteStopTransactionRequest,
- ResetRequest,
- SetChargingProfileRequest,
- UnlockConnectorRequest,
+ type OCPP16StatusNotificationRequest,
+ type OCPP16TriggerMessageRequest,
+ type OCPP16UpdateFirmwareRequest,
+ type RemoteStartTransactionRequest,
+ type RemoteStopTransactionRequest,
+ type ResetRequest,
+ type SetChargingProfileRequest,
+ type UnlockConnectorRequest,
} from '../../../types/ocpp/1.6/Requests';
-import type {
- ChangeAvailabilityResponse,
- ChangeConfigurationResponse,
- ClearChargingProfileResponse,
- DiagnosticsStatusNotificationResponse,
- GetConfigurationResponse,
- GetDiagnosticsResponse,
- OCPP16BootNotificationResponse,
- OCPP16HeartbeatResponse,
- OCPP16StatusNotificationResponse,
- OCPP16TriggerMessageResponse,
- SetChargingProfileResponse,
- UnlockConnectorResponse,
+import {
+ type ChangeAvailabilityResponse,
+ type ChangeConfigurationResponse,
+ type ClearChargingProfileResponse,
+ type DiagnosticsStatusNotificationResponse,
+ type GetConfigurationResponse,
+ type GetDiagnosticsResponse,
+ type OCPP16BootNotificationResponse,
+ type OCPP16DataTransferResponse,
+ OCPP16DataTransferStatus,
+ type OCPP16HeartbeatResponse,
+ type OCPP16StatusNotificationResponse,
+ type OCPP16TriggerMessageResponse,
+ type OCPP16UpdateFirmwareResponse,
+ type SetChargingProfileResponse,
+ type UnlockConnectorResponse,
} from '../../../types/ocpp/1.6/Responses';
import {
OCPP16AuthorizationStatus,
- OCPP16AuthorizeRequest,
- OCPP16AuthorizeResponse,
- OCPP16StartTransactionRequest,
- OCPP16StartTransactionResponse,
+ type OCPP16AuthorizeRequest,
+ type OCPP16AuthorizeResponse,
+ type OCPP16StartTransactionRequest,
+ type OCPP16StartTransactionResponse,
OCPP16StopTransactionReason,
} from '../../../types/ocpp/1.6/Transaction';
import type { OCPPConfigurationKey } from '../../../types/ocpp/Configuration';
import { ErrorType } from '../../../types/ocpp/ErrorType';
+import { OCPPVersion } from '../../../types/ocpp/OCPPVersion';
import type { IncomingRequestHandler } from '../../../types/ocpp/Requests';
import type { DefaultResponse } from '../../../types/ocpp/Responses';
import Constants from '../../../utils/Constants';
if (new.target?.name === moduleName) {
throw new TypeError(`Cannot construct ${new.target?.name} instances directly`);
}
- super();
+ super(OCPPVersion.VERSION_16);
this.incomingRequestHandlers = new Map<OCPP16IncomingRequestCommand, IncomingRequestHandler>([
[OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)],
[OCPP16IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)],
],
[OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, this.handleRequestGetDiagnostics.bind(this)],
[OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)],
+ [OCPP16IncomingRequestCommand.DATA_TRANSFER, this.handleRequestDataTransfer.bind(this)],
+ // [OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, this.handleRequestUpdateFirmware.bind(this)],
]);
this.jsonSchemas = new Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>([
[
)
) as JSONSchemaType<OCPP16TriggerMessageRequest>,
],
+ [
+ OCPP16IncomingRequestCommand.DATA_TRANSFER,
+ JSON.parse(
+ fs.readFileSync(
+ path.resolve(
+ path.dirname(fileURLToPath(import.meta.url)),
+ '../../../assets/json-schemas/ocpp/1.6/DataTransfer.json'
+ ),
+ 'utf8'
+ )
+ ) as JSONSchemaType<OCPP16DataTransferRequest>,
+ ],
]);
this.validatePayload.bind(this);
}
commandPayload: UnlockConnectorRequest
): Promise<UnlockConnectorResponse> {
const connectorId = commandPayload.connectorId;
+ if (chargingStation.connectors.has(connectorId) === false) {
+ logger.error(
+ `${chargingStation.logPrefix()} Trying to unlock a non existing connector Id ${connectorId.toString()}`
+ );
+ return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
+ }
if (connectorId === 0) {
logger.error(
- chargingStation.logPrefix() + ' Trying to unlock connector ' + connectorId.toString()
+ chargingStation.logPrefix() + ' Trying to unlock connector Id ' + connectorId.toString()
);
return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
}
commandPayload: ChangeAvailabilityRequest
): Promise<ChangeAvailabilityResponse> {
const connectorId: number = commandPayload.connectorId;
- if (!chargingStation.getConnectorStatus(connectorId)) {
+ if (chargingStation.connectors.has(connectorId) === false) {
logger.error(
`${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
);
return Constants.OCPP_RESPONSE_REJECTED;
}
+ private handleRequestUpdateFirmware(
+ chargingStation: ChargingStation,
+ commandPayload: OCPP16UpdateFirmwareRequest
+ ): OCPP16UpdateFirmwareResponse {
+ if (
+ OCPP16ServiceUtils.checkFeatureProfile(
+ chargingStation,
+ OCPP16SupportedFeatureProfiles.FirmwareManagement,
+ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE
+ ) === false
+ ) {
+ return Constants.OCPP_RESPONSE_EMPTY;
+ }
+ logger.debug(
+ chargingStation.logPrefix() +
+ ' ' +
+ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE +
+ ' request received: %j',
+ commandPayload
+ );
+ }
+
private async handleRequestGetDiagnostics(
chargingStation: ChargingStation,
commandPayload: GetDiagnosticsRequest
chargingStation,
OCPP16SupportedFeatureProfiles.RemoteTrigger,
OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
+ ) ||
+ !OCPP16ServiceUtils.isMessageTriggerSupported(
+ chargingStation,
+ commandPayload.requestedMessage
)
) {
return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
}
- // TODO: factor out the check on connector id
- if (commandPayload?.connectorId < 0) {
- logger.warn(
- `${chargingStation.logPrefix()} ${
- OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
- } incoming request received with invalid connectorId ${commandPayload.connectorId}`
- );
+ if (
+ !OCPP16ServiceUtils.isConnectorIdValid(
+ chargingStation,
+ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
+ commandPayload.connectorId
+ )
+ ) {
return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
}
try {
switch (commandPayload.requestedMessage) {
- case MessageTrigger.BootNotification:
+ case OCPP16MessageTrigger.BootNotification:
setTimeout(() => {
chargingStation.ocppRequestService
.requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
});
}, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
- case MessageTrigger.Heartbeat:
+ case OCPP16MessageTrigger.Heartbeat:
setTimeout(() => {
chargingStation.ocppRequestService
.requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
});
}, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
- case MessageTrigger.StatusNotification:
+ case OCPP16MessageTrigger.StatusNotification:
setTimeout(() => {
if (commandPayload?.connectorId) {
chargingStation.ocppRequestService
);
}
}
+
+ private handleRequestDataTransfer(
+ chargingStation: ChargingStation,
+ commandPayload: OCPP16DataTransferRequest
+ ): OCPP16DataTransferResponse {
+ try {
+ if (Object.values(OCPP16DataTransferVendorId).includes(commandPayload.vendorId)) {
+ return {
+ status: OCPP16DataTransferStatus.ACCEPTED,
+ };
+ }
+ return {
+ status: OCPP16DataTransferStatus.UNKNOWN_VENDOR_ID,
+ };
+ } catch (error) {
+ return this.handleIncomingRequestError(
+ chargingStation,
+ OCPP16IncomingRequestCommand.DATA_TRANSFER,
+ error as Error,
+ { errorResponse: Constants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }
+ );
+ }
+ }
}