X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16ResponseService.ts;h=a1fa0e43aa535ecbae64596431dbe058ef93966a;hb=86f51b961d470ea555b25bf08664d40705111454;hp=2550dde9c4e29594131326b4cd6894ebd51f181c;hpb=5edd8ba0f8978cfb3ca9d80f299d9748c6c5970e;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 2550dde9..a1fa0e43 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -3,16 +3,18 @@ import { parentPort } from 'node:worker_threads'; import type { JSONSchemaType } from 'ajv'; +import { secondsToMilliseconds } from 'date-fns'; import { OCPP16ServiceUtils } from './OCPP16ServiceUtils'; import { type ChargingStation, - ChargingStationConfigurationUtils, + addConfigurationKey, + getConfigurationKey, + hasReservationExpired, resetConnectorStatus, } from '../../../charging-station'; import { OCPPError } from '../../../exception'; import { - type ChangeAvailabilityResponse, type ChangeConfigurationResponse, type ClearChargingProfileResponse, ErrorType, @@ -25,6 +27,7 @@ import { type OCPP16AuthorizeRequest, type OCPP16AuthorizeResponse, type OCPP16BootNotificationResponse, + type OCPP16ChangeAvailabilityResponse, OCPP16ChargePointStatus, type OCPP16DataTransferResponse, type OCPP16DiagnosticsStatusNotificationResponse, @@ -46,6 +49,7 @@ import { type OCPP16UpdateFirmwareResponse, OCPPVersion, RegistrationStatusEnumType, + ReservationTerminationReason, type ResponseHandler, type SetChargingProfileResponse, type UnlockConnectorResponse, @@ -76,16 +80,34 @@ export class OCPP16ResponseService extends OCPPResponseService { // } super(OCPPVersion.VERSION_16); this.responseHandlers = new Map([ - [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)], - [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.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.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)], + [ + OCPP16RequestCommand.BOOT_NOTIFICATION, + this.handleResponseBootNotification.bind(this) as ResponseHandler, + ], + [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this) as ResponseHandler], + [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this) as ResponseHandler], + [ + OCPP16RequestCommand.START_TRANSACTION, + this.handleResponseStartTransaction.bind(this) as ResponseHandler, + ], + [ + OCPP16RequestCommand.STOP_TRANSACTION, + this.handleResponseStopTransaction.bind(this) as ResponseHandler, + ], + [ + OCPP16RequestCommand.STATUS_NOTIFICATION, + this.emptyResponseHandler.bind(this) as ResponseHandler, + ], + [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this) as ResponseHandler], + [ + OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, + this.emptyResponseHandler.bind(this) as ResponseHandler, + ], + [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this) as ResponseHandler], + [ + OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, + this.emptyResponseHandler.bind(this) as ResponseHandler, + ], ]); this.jsonSchemas = new Map>([ [ @@ -188,7 +210,7 @@ export class OCPP16ResponseService extends OCPPResponseService { ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, - OCPP16ServiceUtils.parseJsonSchemaFile( + OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json', moduleName, 'constructor', @@ -314,11 +336,11 @@ export class OCPP16ResponseService extends OCPPResponseService { ) => boolean; } - public async responseHandler( + public async responseHandler( chargingStation: ChargingStation, commandName: OCPP16RequestCommand, - payload: JsonType, - requestPayload: JsonType, + payload: ResType, + requestPayload: ReqType, ): Promise { if ( chargingStation.isRegistered() === true || @@ -330,7 +352,7 @@ export class OCPP16ResponseService extends OCPPResponseService { ) { try { this.validatePayload(chargingStation, commandName, payload); - await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload); + await this.responseHandlers.get(commandName)!(chargingStation, payload, requestPayload); } catch (error) { logger.error( `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`, @@ -374,7 +396,7 @@ export class OCPP16ResponseService extends OCPPResponseService { return this.validateResponsePayload( chargingStation, commandName, - this.jsonSchemas.get(commandName), + this.jsonSchemas.get(commandName)!, payload, ); } @@ -389,14 +411,14 @@ export class OCPP16ResponseService extends OCPPResponseService { payload: OCPP16BootNotificationResponse, ): void { if (payload.status === RegistrationStatusEnumType.ACCEPTED) { - ChargingStationConfigurationUtils.addConfigurationKey( + addConfigurationKey( chargingStation, OCPP16StandardParametersKey.HeartbeatInterval, payload.interval.toString(), {}, { overwrite: true, save: true }, ); - ChargingStationConfigurationUtils.addConfigurationKey( + addConfigurationKey( chargingStation, OCPP16StandardParametersKey.HeartBeatInterval, payload.interval.toString(), @@ -425,7 +447,7 @@ export class OCPP16ResponseService extends OCPPResponseService { payload: OCPP16AuthorizeResponse, requestPayload: OCPP16AuthorizeRequest, ): void { - let authorizeConnectorId: number; + let authorizeConnectorId: number | undefined; if (chargingStation.hasEvses) { for (const [evseId, evseStatus] of chargingStation.evses) { if (evseId > 0) { @@ -450,8 +472,11 @@ export class OCPP16ResponseService extends OCPPResponseService { } const authorizeConnectorIdDefined = !isNullOrUndefined(authorizeConnectorId); if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) { - authorizeConnectorIdDefined && - (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true); + if (authorizeConnectorIdDefined) { + // chargingStation.getConnectorStatus(authorizeConnectorId!)!.authorizeIdTag = + // requestPayload.idTag; + chargingStation.getConnectorStatus(authorizeConnectorId!)!.idTagAuthorized = true; + } logger.debug( `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${ authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : '' @@ -459,8 +484,8 @@ export class OCPP16ResponseService extends OCPPResponseService { ); } else { if (authorizeConnectorIdDefined) { - chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false; - delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag; + chargingStation.getConnectorStatus(authorizeConnectorId!)!.idTagAuthorized = false; + delete chargingStation.getConnectorStatus(authorizeConnectorId!)?.authorizeIdTag; } logger.debug( `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${ @@ -490,7 +515,7 @@ export class OCPP16ResponseService extends OCPPResponseService { true && chargingStation.getAuthorizeRemoteTxRequests() === true && chargingStation.getLocalAuthListEnabled() === true && - chargingStation.hasIdTags() && + chargingStation.hasIdTags() === true && chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false ) { logger.error( @@ -505,7 +530,7 @@ export class OCPP16ResponseService extends OCPPResponseService { chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted === true && chargingStation.getAuthorizeRemoteTxRequests() === true && - chargingStation.getMustAuthorizeAtRemoteStart() === true && + chargingStation.getRemoteAuthorization() === true && chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false && chargingStation.getConnectorStatus(transactionConnectorId)?.idTagAuthorized === false ) { @@ -589,7 +614,7 @@ export class OCPP16ResponseService extends OCPPResponseService { ); return; } - if (!Number.isInteger(payload.transactionId)) { + if (!Number.isSafeInteger(payload.transactionId)) { logger.warn( `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${transactionConnectorId.toString()} with a non integer transaction id ${ payload.transactionId @@ -599,20 +624,50 @@ export class OCPP16ResponseService extends OCPPResponseService { } if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { - chargingStation.getConnectorStatus(transactionConnectorId).transactionStarted = true; - chargingStation.getConnectorStatus(transactionConnectorId).transactionId = + chargingStation.getConnectorStatus(transactionConnectorId)!.transactionStarted = true; + chargingStation.getConnectorStatus(transactionConnectorId)!.transactionStart = + requestPayload.timestamp; + chargingStation.getConnectorStatus(transactionConnectorId)!.transactionId = payload.transactionId; - chargingStation.getConnectorStatus(transactionConnectorId).transactionIdTag = + chargingStation.getConnectorStatus(transactionConnectorId)!.transactionIdTag = requestPayload.idTag; chargingStation.getConnectorStatus( transactionConnectorId, - ).transactionEnergyActiveImportRegisterValue = 0; - chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue = + )!.transactionEnergyActiveImportRegisterValue = 0; + chargingStation.getConnectorStatus(transactionConnectorId)!.transactionBeginMeterValue = OCPP16ServiceUtils.buildTransactionBeginMeterValue( chargingStation, transactionConnectorId, requestPayload.meterStart, ); + if (requestPayload.reservationId) { + const reservation = chargingStation.getReservationBy( + 'reservationId', + requestPayload.reservationId, + )!; + if (reservation.idTag !== requestPayload.idTag) { + logger.warn( + `${chargingStation.logPrefix()} Reserved transaction ${ + payload.transactionId + } started with a different idTag ${requestPayload.idTag} than the reservation one ${ + reservation.idTag + }`, + ); + } + if (hasReservationExpired(reservation)) { + logger.warn( + `${chargingStation.logPrefix()} Reserved transaction ${ + payload.transactionId + } started with expired reservation ${ + requestPayload.reservationId + } (expiry date: ${reservation.expiryDate.toISOString()}))`, + ); + } + await chargingStation.removeReservation( + reservation, + ReservationTerminationReason.TRANSACTION_STARTED, + ); + } chargingStation.getBeginEndMeterValues() && (await chargingStation.ocppRequestService.requestHandler< OCPP16MeterValuesRequest, @@ -621,9 +676,9 @@ export class OCPP16ResponseService extends OCPPResponseService { connectorId: transactionConnectorId, transactionId: payload.transactionId, meterValue: [ - chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue, + chargingStation.getConnectorStatus(transactionConnectorId)!.transactionBeginMeterValue, ], - })); + } as OCPP16MeterValuesRequest)); await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, transactionConnectorId, @@ -637,21 +692,31 @@ export class OCPP16ResponseService extends OCPPResponseService { if (chargingStation.stationInfo.powerSharedByConnectors) { ++chargingStation.powerDivider; } - const configuredMeterValueSampleInterval = - ChargingStationConfigurationUtils.getConfigurationKey( - chargingStation, - OCPP16StandardParametersKey.MeterValueSampleInterval, - ); + const configuredMeterValueSampleInterval = getConfigurationKey( + chargingStation, + OCPP16StandardParametersKey.MeterValueSampleInterval, + ); chargingStation.startMeterValues( transactionConnectorId, configuredMeterValueSampleInterval - ? convertToInt(configuredMeterValueSampleInterval.value) * 1000 + ? secondsToMilliseconds(convertToInt(configuredMeterValueSampleInterval.value)) : Constants.DEFAULT_METER_VALUES_INTERVAL, ); } else { logger.warn( - `${chargingStation.logPrefix()} Starting transaction with id ${payload.transactionId.toString()} REJECTED with status '${payload - .idTagInfo?.status}', idTag '${requestPayload.idTag}'`, + `${chargingStation.logPrefix()} Starting transaction with id ${payload.transactionId.toString()} REJECTED on ${ + chargingStation.stationInfo.chargingStationId + }#${transactionConnectorId.toString()} with status '${payload.idTagInfo?.status}', idTag '${ + requestPayload.idTag + }'${ + OCPP16ServiceUtils.hasReservation( + chargingStation, + transactionConnectorId, + requestPayload.idTag, + ) + ? `, reservationId '${requestPayload.reservationId}'` + : '' + }`, ); await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId); } @@ -661,9 +726,8 @@ export class OCPP16ResponseService extends OCPPResponseService { chargingStation: ChargingStation, connectorId: number, ): Promise { - resetConnectorStatus(chargingStation.getConnectorStatus(connectorId)); + resetConnectorStatus(chargingStation.getConnectorStatus(connectorId)!); chargingStation.stopMeterValues(connectorId); - parentPort?.postMessage(buildUpdatedMessage(chargingStation)); if ( chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available ) { @@ -673,6 +737,7 @@ export class OCPP16ResponseService extends OCPPResponseService { OCPP16ChargePointStatus.Available, ); } + parentPort?.postMessage(buildUpdatedMessage(chargingStation)); } private async handleResponseStopTransaction( @@ -701,32 +766,32 @@ export class OCPP16ResponseService extends OCPPResponseService { meterValue: [ OCPP16ServiceUtils.buildTransactionEndMeterValue( chargingStation, - transactionConnectorId, + transactionConnectorId!, requestPayload.meterStop, ), ], })); if ( chargingStation.isChargingStationAvailable() === false || - chargingStation.isConnectorAvailable(transactionConnectorId) === false + chargingStation.isConnectorAvailable(transactionConnectorId!) === false ) { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, - transactionConnectorId, + transactionConnectorId!, OCPP16ChargePointStatus.Unavailable, ); } else { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, - transactionConnectorId, + transactionConnectorId!, OCPP16ChargePointStatus.Available, ); } if (chargingStation.stationInfo.powerSharedByConnectors) { chargingStation.powerDivider--; } - resetConnectorStatus(chargingStation.getConnectorStatus(transactionConnectorId)); - chargingStation.stopMeterValues(transactionConnectorId); + resetConnectorStatus(chargingStation.getConnectorStatus(transactionConnectorId!)!); + chargingStation.stopMeterValues(transactionConnectorId!); parentPort?.postMessage(buildUpdatedMessage(chargingStation)); const logMsg = `${chargingStation.logPrefix()} Transaction with id ${requestPayload.transactionId.toString()} STOPPED on ${ chargingStation.stationInfo.chargingStationId