import { Client, type FTPResponse } from 'basic-ftp';
import tar from 'tar';
+import { OCPP16Constants } from './OCPP16Constants';
+import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
import {
type ChargingStation,
ChargingStationConfigurationUtils,
type ClearChargingProfileRequest,
type ClearChargingProfileResponse,
type ConnectorStatus,
+ ConnectorStatusEnum,
ErrorType,
type GenericResponse,
GenericStatus,
type JsonObject,
type JsonType,
OCPP16AuthorizationStatus,
- type OCPP16AuthorizeRequest,
- type OCPP16AuthorizeResponse,
OCPP16AvailabilityType,
type OCPP16BootNotificationRequest,
type OCPP16BootNotificationResponse,
type UnlockConnectorRequest,
type UnlockConnectorResponse,
} from '../../../types';
+import type {
+ OCPP16CancelReservationRequest,
+ OCPP16ReserveNowRequest,
+} from '../../../types/ocpp/1.6/Requests';
+import { ReservationTerminationReason } from '../../../types/ocpp/1.6/Reservation';
+import type {
+ OCPP16CancelReservationResponse,
+ OCPP16ReserveNowResponse,
+} from '../../../types/ocpp/1.6/Responses';
import { Constants, Utils, logger } from '../../../utils';
-import { OCPP16Constants, OCPP16ServiceUtils, OCPPIncomingRequestService } from '../internal';
+import { OCPPConstants } from '../OCPPConstants';
+import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService';
const moduleName = 'OCPP16IncomingRequestService';
[OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)],
[OCPP16IncomingRequestCommand.DATA_TRANSFER, this.handleRequestDataTransfer.bind(this)],
[OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, this.handleRequestUpdateFirmware.bind(this)],
+ [OCPP16IncomingRequestCommand.RESERVE_NOW, this.handleRequestReserveNow.bind(this)],
+ [
+ OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
+ this.handleRequestCancelReservation.bind(this),
+ ],
]);
this.jsonSchemas = new Map<OCPP16IncomingRequestCommand, JSONSchemaType<JsonObject>>([
[
OCPP16IncomingRequestCommand.RESET,
OCPP16ServiceUtils.parseJsonSchemaFile<ResetRequest>(
- '../../../assets/json-schemas/ocpp/1.6/Reset.json',
+ 'assets/json-schemas/ocpp/1.6/Reset.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.CLEAR_CACHE,
OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ClearCacheRequest>(
- '../../../assets/json-schemas/ocpp/1.6/ClearCache.json',
+ 'assets/json-schemas/ocpp/1.6/ClearCache.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR,
OCPP16ServiceUtils.parseJsonSchemaFile<UnlockConnectorRequest>(
- '../../../assets/json-schemas/ocpp/1.6/UnlockConnector.json',
+ 'assets/json-schemas/ocpp/1.6/UnlockConnector.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.GET_CONFIGURATION,
OCPP16ServiceUtils.parseJsonSchemaFile<GetConfigurationRequest>(
- '../../../assets/json-schemas/ocpp/1.6/GetConfiguration.json',
+ 'assets/json-schemas/ocpp/1.6/GetConfiguration.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
OCPP16ServiceUtils.parseJsonSchemaFile<ChangeConfigurationRequest>(
- '../../../assets/json-schemas/ocpp/1.6/ChangeConfiguration.json',
+ 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.GET_DIAGNOSTICS,
OCPP16ServiceUtils.parseJsonSchemaFile<GetDiagnosticsRequest>(
- '../../../assets/json-schemas/ocpp/1.6/GetDiagnostics.json',
+ 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleRequest>(
- '../../../assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
+ 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileRequest>(
- '../../../assets/json-schemas/ocpp/1.6/SetChargingProfile.json',
+ 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE,
OCPP16ServiceUtils.parseJsonSchemaFile<ClearChargingProfileRequest>(
- '../../../assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
+ 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityRequest>(
- '../../../assets/json-schemas/ocpp/1.6/ChangeAvailability.json',
+ 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION,
OCPP16ServiceUtils.parseJsonSchemaFile<RemoteStartTransactionRequest>(
- '../../../assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json',
+ 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION,
OCPP16ServiceUtils.parseJsonSchemaFile<RemoteStopTransactionRequest>(
- '../../../assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json',
+ 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16TriggerMessageRequest>(
- '../../../assets/json-schemas/ocpp/1.6/TriggerMessage.json',
+ 'assets/json-schemas/ocpp/1.6/TriggerMessage.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.DATA_TRANSFER,
OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16DataTransferRequest>(
- '../../../assets/json-schemas/ocpp/1.6/DataTransfer.json',
+ 'assets/json-schemas/ocpp/1.6/DataTransfer.json',
moduleName,
'constructor'
),
[
OCPP16IncomingRequestCommand.UPDATE_FIRMWARE,
OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16UpdateFirmwareRequest>(
- '../../../assets/json-schemas/ocpp/1.6/UpdateFirmware.json',
+ 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json',
+ moduleName,
+ 'constructor'
+ ),
+ ],
+ [
+ OCPP16IncomingRequestCommand.RESERVE_NOW,
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ReserveNowRequest>(
+ 'assets/json-schemas/ocpp/1.6/ReserveNow.json',
+ moduleName,
+ 'constructor'
+ ),
+ ],
+ [
+ OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16CancelReservationRequest>(
+ 'assets/json-schemas/ocpp/1.6/CancelReservation.json',
moduleName,
'constructor'
),
let response: JsonType;
if (
chargingStation.getOcppStrictCompliance() === true &&
- chargingStation.isInPendingState() === true &&
+ chargingStation.inPendingState() === true &&
(commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION ||
commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION)
) {
if (
chargingStation.isRegistered() === true ||
(chargingStation.getOcppStrictCompliance() === false &&
- chargingStation.isInUnknownState() === true)
+ chargingStation.inUnknownState() === true)
) {
if (
this.incomingRequestHandlers.has(commandName) === true &&
commandPayload: RemoteStartTransactionRequest
): Promise<GenericResponse> {
const transactionConnectorId = commandPayload.connectorId;
+ const reserved =
+ chargingStation.getConnectorStatus(transactionConnectorId).status ===
+ OCPP16ChargePointStatus.Reserved;
+ if (
+ reserved &&
+ chargingStation.validateIncomingRequestWithReservation(
+ transactionConnectorId,
+ commandPayload.idTag
+ )
+ ) {
+ return OCPP16Constants.OCPP_RESPONSE_REJECTED;
+ }
if (chargingStation.hasConnector(transactionConnectorId) === false) {
return this.notifyRemoteStartTransactionRejected(
chargingStation,
);
}
if (
- chargingStation.isChargingStationAvailable() === false ||
- chargingStation.isConnectorAvailable(transactionConnectorId) === false
+ !chargingStation.isChargingStationAvailable() ||
+ !chargingStation.isConnectorAvailable(transactionConnectorId)
) {
return this.notifyRemoteStartTransactionRejected(
chargingStation,
const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId);
// Check if authorized
if (chargingStation.getAuthorizeRemoteTxRequests() === true) {
- let authorized = false;
- if (
- chargingStation.getLocalAuthListEnabled() === true &&
- chargingStation.hasIdTags() === true &&
- Utils.isNotEmptyString(
- chargingStation.idTagsCache
- .getIdTags(ChargingStationUtils.getIdTagsFile(chargingStation.stationInfo))
- ?.find((idTag) => idTag === commandPayload.idTag)
- )
- ) {
- connectorStatus.localAuthorizeIdTag = commandPayload.idTag;
- connectorStatus.idTagLocalAuthorized = true;
- authorized = true;
- } else if (chargingStation.getMustAuthorizeAtRemoteStart() === true) {
- connectorStatus.authorizeIdTag = commandPayload.idTag;
- const authorizeResponse: OCPP16AuthorizeResponse =
- await chargingStation.ocppRequestService.requestHandler<
- OCPP16AuthorizeRequest,
- OCPP16AuthorizeResponse
- >(chargingStation, OCPP16RequestCommand.AUTHORIZE, {
- idTag: commandPayload.idTag,
- });
- if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
- authorized = true;
- }
- } else {
- logger.warn(
- `${chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`
- );
- }
+ const authorized = await chargingStation.isAuthorized(
+ transactionConnectorId,
+ commandPayload.idTag
+ );
if (authorized === true) {
// Authorization successful, start transaction
if (
) === true
) {
connectorStatus.transactionRemoteStarted = true;
+ const startTransactionPayload: JsonType = {
+ connectorId: transactionConnectorId,
+ idTag: commandPayload.idTag,
+ };
+ if (reserved) {
+ const reservation = chargingStation.getReservationByConnectorId(transactionConnectorId);
+ startTransactionData.reservationId = reservation.id;
+ await chargingStation.removeReservation(
+ reservation,
+ ReservationTerminationReason.TRANSACTION_STARTED
+ );
+ }
if (
(
await chargingStation.ocppRequestService.requestHandler<
OCPP16StartTransactionRequest,
OCPP16StartTransactionResponse
- >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, {
- connectorId: transactionConnectorId,
- idTag: commandPayload.idTag,
- })
+ >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, startTransactionData)
).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED
) {
logger.debug(remoteStartTransactionLogMsg);
let ftpClient: Client;
try {
const logFiles = fs
- .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'))
+ .readdirSync(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'))
.filter((file) => file.endsWith('.log'))
.map((file) => path.join('./', file));
const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`;
});
uploadResponse = await ftpClient.uploadFrom(
path.join(
- path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../'),
+ path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../'),
diagnosticsArchive
),
`${uri.pathname}${diagnosticsArchive}`
);
}
}
+
+ private async handleRequestReserveNow(
+ chargingStation: ChargingStation,
+ commandPayload: OCPP16ReserveNowRequest
+ ): Promise<OCPP16ReserveNowResponse> {
+ const { reservationId, idTag, connectorId } = commandPayload;
+ let response: OCPP16ReserveNowResponse;
+ try {
+ if (
+ !chargingStation.supportsReservations() &&
+ chargingStation.isConnectorAvailable(connectorId)
+ ) {
+ return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED;
+ }
+ if (connectorId === 0 && !chargingStation.supportsReservationsOnConnectorId0()) {
+ return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED;
+ }
+ if (!(await chargingStation.isAuthorized(connectorId, idTag))) {
+ return OCPPConstants.OCPP_RESERVATION_RESPONSE_REJECTED;
+ }
+ switch (chargingStation.getConnectorStatus(connectorId).status) {
+ case ConnectorStatusEnum.Faulted:
+ response = OCPPConstants.OCPP_RESERVATION_RESPONSE_FAULTED;
+ break;
+ case ConnectorStatusEnum.Occupied:
+ response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
+ break;
+ case ConnectorStatusEnum.Unavailable:
+ response = OCPPConstants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE;
+ break;
+ case ConnectorStatusEnum.Reserved:
+ if (!chargingStation.isConnectorReservable(reservationId, connectorId, idTag)) {
+ response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
+ break;
+ }
+ // eslint-disable-next-line no-fallthrough
+ default:
+ if (!chargingStation.isConnectorReservable(reservationId)) {
+ response = OCPPConstants.OCPP_RESERVATION_RESPONSE_OCCUPIED;
+ break;
+ }
+ await chargingStation.addReservation({
+ id: commandPayload.reservationId,
+ ...commandPayload,
+ });
+ response = OCPPConstants.OCPP_RESERVATION_RESPONSE_ACCEPTED;
+ break;
+ }
+ return response;
+ } catch (error) {
+ chargingStation.getConnectorStatus(connectorId).status = ConnectorStatusEnum.Available;
+ return this.handleIncomingRequestError(
+ chargingStation,
+ OCPP16IncomingRequestCommand.RESERVE_NOW,
+ error as Error,
+ { errorResponse: OCPPConstants.OCPP_RESERVATION_RESPONSE_FAULTED }
+ );
+ }
+ }
+
+ private async handleRequestCancelReservation(
+ chargingStation: ChargingStation,
+ commandPayload: OCPP16CancelReservationRequest
+ ): Promise<OCPP16CancelReservationResponse> {
+ try {
+ const { reservationId } = commandPayload;
+ const [exists, reservation] = chargingStation.doesReservationExists({ id: reservationId });
+ if (!exists) {
+ logger.error(
+ `${chargingStation.logPrefix()} Reservation with ID ${reservationId} does not exist on charging station`
+ );
+ return OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED;
+ }
+ await chargingStation.removeReservation(reservation);
+ return OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED;
+ } catch (error) {
+ return this.handleIncomingRequestError(
+ chargingStation,
+ OCPP16IncomingRequestCommand.CANCEL_RESERVATION,
+ error as Error,
+ { errorResponse: OCPPConstants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED }
+ );
+ }
+ }
}