X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16IncomingRequestService.ts;h=49ed8c92ae99ffd93fb4495d4fb916c8c73da925;hb=0ce9babe5c83eced0e773109d4393c1ab487039f;hp=b735967728bb5ffc5bc4de1f24444639549391a3;hpb=60a743910478b70e39dcefa5e1b752ec8a93880e;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index b7359677..49ed8c92 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -19,11 +19,11 @@ import { type ChangeAvailabilityResponse, type ChangeConfigurationRequest, type ChangeConfigurationResponse, - ChargingProfilePurposeType, type ClearChargingProfileRequest, type ClearChargingProfileResponse, ErrorType, type GenericResponse, + GenericStatus, type GetConfigurationRequest, type GetConfigurationResponse, type GetDiagnosticsRequest, @@ -40,6 +40,8 @@ import { OCPP16ChargePointErrorCode, OCPP16ChargePointStatus, type OCPP16ChargingProfile, + OCPP16ChargingProfilePurposeType, + type OCPP16ChargingSchedule, type OCPP16ClearCacheRequest, type OCPP16DataTransferRequest, type OCPP16DataTransferResponse, @@ -51,6 +53,8 @@ import { OCPP16FirmwareStatus, type OCPP16FirmwareStatusNotificationRequest, type OCPP16FirmwareStatusNotificationResponse, + type OCPP16GetCompositeScheduleRequest, + type OCPP16GetCompositeScheduleResponse, type OCPP16HeartbeatRequest, type OCPP16HeartbeatResponse, OCPP16IncomingRequestCommand, @@ -87,9 +91,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { private incomingRequestHandlers: Map; public constructor() { - if (new.target?.name === moduleName) { - throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); - } + // if (new.target?.name === moduleName) { + // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); + // } super(OCPPVersion.VERSION_16); this.incomingRequestHandlers = new Map([ [OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)], @@ -103,6 +107,10 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, this.handleRequestChangeConfiguration.bind(this), ], + [ + OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, + this.handleRequestGetCompositeSchedule.bind(this), + ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, this.handleRequestSetChargingProfile.bind(this), @@ -177,6 +185,14 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { 'constructor' ), ], + [ + OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, + OCPP16ServiceUtils.parseJsonSchemaFile( + '../../../assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json', + moduleName, + 'constructor' + ), + ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, OCPP16ServiceUtils.parseJsonSchemaFile( @@ -242,7 +258,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ), ], ]); - this.validatePayload.bind(this); + this.validatePayload = this.validatePayload.bind(this) as ( + chargingStation: ChargingStation, + commandName: OCPP16IncomingRequestCommand, + commandPayload: JsonType + ) => boolean; } public async incomingRequestHandler( @@ -358,9 +378,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) => Promise, chargingStation, `${commandPayload.type}Reset` as OCPP16StopTransactionReason - ).catch(() => { - /* This is intentional */ - }); + ).catch(Constants.EMPTY_FUNCTION); logger.info( `${chargingStation.logPrefix()} ${ commandPayload.type @@ -398,15 +416,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } return OCPPConstants.OCPP_RESPONSE_UNLOCK_FAILED; } - await chargingStation.ocppRequestService.requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { + await OCPP16ServiceUtils.sendAndSetConnectorStatus( + chargingStation, connectorId, - status: OCPP16ChargePointStatus.AVAILABLE, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - }); - chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE; + OCPP16ChargePointStatus.Available + ); return OCPPConstants.OCPP_RESPONSE_UNLOCKED; } @@ -537,14 +551,14 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } if ( commandPayload.csChargingProfiles.chargingProfilePurpose === - ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE && + OCPP16ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE && commandPayload.connectorId !== 0 ) { return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } if ( commandPayload.csChargingProfiles.chargingProfilePurpose === - ChargingProfilePurposeType.TX_PROFILE && + OCPP16ChargingProfilePurposeType.TX_PROFILE && (commandPayload.connectorId === 0 || chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted === false) @@ -570,6 +584,56 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED; } + private handleRequestGetCompositeSchedule( + chargingStation: ChargingStation, + commandPayload: OCPP16GetCompositeScheduleRequest + ): OCPP16GetCompositeScheduleResponse { + if ( + OCPP16ServiceUtils.checkFeatureProfile( + chargingStation, + OCPP16SupportedFeatureProfiles.SmartCharging, + OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE + ) === false + ) { + return OCPPConstants.OCPP_RESPONSE_REJECTED; + } + if (chargingStation.connectors.has(commandPayload.connectorId) === false) { + logger.error( + `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector Id ${ + commandPayload.connectorId + }` + ); + return OCPPConstants.OCPP_RESPONSE_REJECTED; + } + if ( + Utils.isEmptyArray( + chargingStation.getConnectorStatus(commandPayload.connectorId)?.chargingProfiles + ) + ) { + return OCPPConstants.OCPP_RESPONSE_REJECTED; + } + const startDate = new Date(); + const endDate = new Date(startDate.getTime() + commandPayload.duration * 1000); + let compositeSchedule: OCPP16ChargingSchedule; + for (const chargingProfile of chargingStation.getConnectorStatus(commandPayload.connectorId) + .chargingProfiles) { + // FIXME: build the composite schedule including the local power limit, the stack level, the charging rate unit, etc. + if ( + chargingProfile.chargingSchedule?.startSchedule >= startDate && + chargingProfile.chargingSchedule?.startSchedule <= endDate + ) { + compositeSchedule = chargingProfile.chargingSchedule; + break; + } + } + return { + status: GenericStatus.Accepted, + scheduleStart: compositeSchedule?.startSchedule, + connectorId: commandPayload.connectorId, + chargingSchedule: compositeSchedule, + }; + } + private handleRequestClearChargingProfile( chargingStation: ChargingStation, commandPayload: ClearChargingProfileRequest @@ -666,8 +730,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } const chargePointStatus: OCPP16ChargePointStatus = commandPayload.type === OCPP16AvailabilityType.OPERATIVE - ? OCPP16ChargePointStatus.AVAILABLE - : OCPP16ChargePointStatus.UNAVAILABLE; + ? OCPP16ChargePointStatus.Available + : OCPP16ChargePointStatus.Unavailable; if (connectorId === 0) { let response: ChangeAvailabilityResponse = OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; for (const id of chargingStation.connectors.keys()) { @@ -676,15 +740,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { } chargingStation.getConnectorStatus(id).availability = commandPayload.type; if (response === OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) { - await chargingStation.ocppRequestService.requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { - connectorId: id, - status: chargePointStatus, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - }); - chargingStation.getConnectorStatus(id).status = chargePointStatus; + await OCPP16ServiceUtils.sendAndSetConnectorStatus( + chargingStation, + id, + chargePointStatus + ); } } return response; @@ -699,15 +759,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; } chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type; - await chargingStation.ocppRequestService.requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { + await OCPP16ServiceUtils.sendAndSetConnectorStatus( + chargingStation, connectorId, - status: chargePointStatus, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - }); - chargingStation.getConnectorStatus(connectorId).status = chargePointStatus; + chargePointStatus + ); return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; } return OCPPConstants.OCPP_AVAILABILITY_RESPONSE_REJECTED; @@ -722,28 +778,25 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { const remoteStartTransactionLogMsg = `${chargingStation.logPrefix()} Transaction remotely STARTED on ${ chargingStation.stationInfo.chargingStationId }#${transactionConnectorId.toString()} for idTag '${commandPayload.idTag}'`; - await chargingStation.ocppRequestService.requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { - connectorId: transactionConnectorId, - status: OCPP16ChargePointStatus.PREPARING, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - }); + await OCPP16ServiceUtils.sendAndSetConnectorStatus( + chargingStation, + transactionConnectorId, + OCPP16ChargePointStatus.Preparing + ); const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId); - connectorStatus.status = OCPP16ChargePointStatus.PREPARING; - if (chargingStation.isChargingStationAvailable() === true) { + if ( + chargingStation.isChargingStationAvailable() === true && + chargingStation.isConnectorAvailable(transactionConnectorId) === true + ) { // Check if authorized if (chargingStation.getAuthorizeRemoteTxRequests() === true) { let authorized = false; if ( chargingStation.getLocalAuthListEnabled() === true && - chargingStation.hasAuthorizedTags() === true && + chargingStation.hasIdTags() === true && Utils.isNotEmptyString( - chargingStation.authorizedTagsCache - .getAuthorizedTags( - ChargingStationUtils.getAuthorizationFile(chargingStation.stationInfo) - ) + chargingStation.idTagsCache + .getIdTags(ChargingStationUtils.getIdTagsFile(chargingStation.stationInfo)) ?.find((idTag) => idTag === commandPayload.idTag) ) ) { @@ -863,17 +916,13 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { idTag: string ): Promise { if ( - chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.AVAILABLE + chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available ) { - await chargingStation.ocppRequestService.requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { + await OCPP16ServiceUtils.sendAndSetConnectorStatus( + chargingStation, connectorId, - status: OCPP16ChargePointStatus.AVAILABLE, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - }); - chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE; + OCPP16ChargePointStatus.Available + ); } logger.warn( `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector Id ${connectorId.toString()}, idTag '${idTag}', availability '${ @@ -888,14 +937,14 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { connectorId: number, cp: OCPP16ChargingProfile ): boolean { - if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) { + if (cp && cp.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE) { OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, cp); logger.debug( `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`, cp ); return true; - } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) { + } else if (cp && cp.chargingProfilePurpose !== OCPP16ChargingProfilePurposeType.TX_PROFILE) { logger.warn( `${chargingStation.logPrefix()} Not allowed to set ${ cp.chargingProfilePurpose @@ -917,15 +966,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { connectorId > 0 && chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId ) { - await chargingStation.ocppRequestService.requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { + await OCPP16ServiceUtils.sendAndSetConnectorStatus( + chargingStation, connectorId, - status: OCPP16ChargePointStatus.FINISHING, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - }); - chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING; + OCPP16ChargePointStatus.Finishing + ); const stopResponse = await chargingStation.stopTransactionOnConnector( connectorId, OCPP16StopTransactionReason.REMOTE @@ -971,67 +1016,66 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { const now = Date.now(); if (retrieveDate?.getTime() <= now) { this.runInAsyncScope( - this.updateFirmware.bind(this) as ( + this.updateFirmwareSimulation.bind(this) as ( this: OCPP16IncomingRequestService, ...args: any[] ) => Promise, this, chargingStation - ).catch(() => { - /* This is intentional */ - }); + ).catch(Constants.EMPTY_FUNCTION); } else { setTimeout(() => { - this.updateFirmware(chargingStation).catch(() => { - /* Intentional */ - }); + this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION); }, retrieveDate?.getTime() - now); } return OCPPConstants.OCPP_RESPONSE_EMPTY; } - private async updateFirmware( + private async updateFirmwareSimulation( chargingStation: ChargingStation, maxDelay = 30, minDelay = 15 ): Promise { - chargingStation.stopAutomaticTransactionGenerator(); + if ( + ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) === + false + ) { + return; + } for (const connectorId of chargingStation.connectors.keys()) { if ( connectorId > 0 && chargingStation.getConnectorStatus(connectorId)?.transactionStarted === false ) { - await chargingStation.ocppRequestService.requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { + await OCPP16ServiceUtils.sendAndSetConnectorStatus( + chargingStation, connectorId, - status: OCPP16ChargePointStatus.UNAVAILABLE, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - }); - chargingStation.getConnectorStatus(connectorId).status = - OCPP16ChargePointStatus.UNAVAILABLE; + OCPP16ChargePointStatus.Unavailable + ); } } + await chargingStation.ocppRequestService.requestHandler< + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse + >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { + status: OCPP16FirmwareStatus.Downloading, + }); + chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading; if ( - chargingStation.stationInfo?.firmwareUpgrade?.failureStatus && - Utils.isNotEmptyString(chargingStation.stationInfo?.firmwareUpgrade?.failureStatus) + chargingStation.stationInfo?.firmwareUpgrade?.failureStatus === + OCPP16FirmwareStatus.DownloadFailed ) { + await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000); await chargingStation.ocppRequestService.requestHandler< OCPP16FirmwareStatusNotificationRequest, OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus, }); + chargingStation.stationInfo.firmwareStatus = + chargingStation.stationInfo?.firmwareUpgrade?.failureStatus; return; } - await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: OCPP16FirmwareStatus.Downloading, - }); - chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading; await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000); await chargingStation.ocppRequestService.requestHandler< OCPP16FirmwareStatusNotificationRequest, @@ -1040,7 +1084,53 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { status: OCPP16FirmwareStatus.Downloaded, }); chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded; - await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000); + let wasTransactionsStarted = false; + let transactionsStarted: boolean; + do { + let trxCount = 0; + for (const connectorId of chargingStation.connectors.keys()) { + if ( + connectorId > 0 && + chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true + ) { + trxCount++; + } + } + if (trxCount > 0) { + const waitTime = 15 * 1000; + logger.debug( + `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${trxCount} transaction(s) in progress, waiting ${ + waitTime / 1000 + } seconds before continuing firmware update simulation` + ); + await Utils.sleep(waitTime); + transactionsStarted = true; + wasTransactionsStarted = true; + } else { + for (const connectorId of chargingStation.connectors.keys()) { + if ( + connectorId > 0 && + chargingStation.getConnectorStatus(connectorId)?.status !== + OCPP16ChargePointStatus.Unavailable + ) { + await OCPP16ServiceUtils.sendAndSetConnectorStatus( + chargingStation, + connectorId, + OCPP16ChargePointStatus.Unavailable + ); + } + } + transactionsStarted = false; + } + } while (transactionsStarted); + !wasTransactionsStarted && + (await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000)); + if ( + ChargingStationUtils.checkChargingStation(chargingStation, chargingStation.logPrefix()) === + false + ) { + return; + } await chargingStation.ocppRequestService.requestHandler< OCPP16FirmwareStatusNotificationRequest, OCPP16FirmwareStatusNotificationResponse @@ -1048,6 +1138,21 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { status: OCPP16FirmwareStatus.Installing, }); chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing; + if ( + chargingStation.stationInfo?.firmwareUpgrade?.failureStatus === + OCPP16FirmwareStatus.InstallationFailed + ) { + await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000); + await chargingStation.ocppRequestService.requestHandler< + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse + >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { + status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus, + }); + chargingStation.stationInfo.firmwareStatus = + chargingStation.stationInfo?.firmwareUpgrade?.failureStatus; + return; + } if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) { await Utils.sleep(Utils.getRandomInteger(maxDelay, minDelay) * 1000); await chargingStation.reset(OCPP16StopTransactionReason.REBOOT); @@ -1218,9 +1323,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { .then((response) => { chargingStation.bootNotificationResponse = response; }) - .catch(() => { - /* This is intentional */ - }); + .catch(Constants.EMPTY_FUNCTION); }, Constants.OCPP_TRIGGER_MESSAGE_DELAY); return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; case OCPP16MessageTrigger.Heartbeat: @@ -1234,9 +1337,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { triggerMessage: true, } ) - .catch(() => { - /* This is intentional */ - }); + .catch(Constants.EMPTY_FUNCTION); }, Constants.OCPP_TRIGGER_MESSAGE_DELAY); return OCPPConstants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; case OCPP16MessageTrigger.StatusNotification: @@ -1255,9 +1356,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { triggerMessage: true, } ) - .catch(() => { - /* This is intentional */ - }); + .catch(Constants.EMPTY_FUNCTION); } else { for (const connectorId of chargingStation.connectors.keys()) { chargingStation.ocppRequestService @@ -1276,9 +1375,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { triggerMessage: true, } ) - .catch(() => { - /* This is intentional */ - }); + .catch(Constants.EMPTY_FUNCTION); } } }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);