From a7fc8211ff35f821074120edea9ddb6ce024d58d Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 27 Jun 2021 22:03:40 +0200 Subject: [PATCH] Various fixes at charging profiles handling: MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit + ensure set at remote start transaction + fix set with matching txProfile, etc. Signed-off-by: Jérôme Benoit --- README.md | 3 +- src/charging-station/ChargingStation.ts | 12 +- .../ocpp/1.6/OCCP16IncomingRequestService.ts | 103 +++++++++--------- src/types/ChargingStationTemplate.ts | 1 + 4 files changed, 65 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index 6748a91a..45fee8ad 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,10 @@ autoReconnectMaxRetries | | -1 (unlimited) | integer | connection retries to the reconnectExponentialDelay | true/false | false | boolean | connection delay retry to the OCPP-J server registrationMaxRetries | | -1 (unlimited) | integer | charging stations boot notification retries enableStatistics | true/false | true | boolean | enable charging stations statistics +mayAuthorizeAtRemoteStart | true/false | true | boolean | always send authorize at remote start transaction when AuthorizeRemoteTxRequests is enabled beginEndMeterValues | true/false | false | boolean | enable Transaction.{Begin,End} MeterValues outOfOrderEndMeterValues | true/false | false | boolean | send Transaction.End MeterValues out of order -meteringPerTransaction | true/false | true | boolean | disable metering on a per transaction basis +meteringPerTransaction | true/false | true | boolean | enable metering history on a per transaction basis transactionDataMeterValues | true/false | false | boolean | enable transaction data MeterValues at stop transaction mainVoltageMeterValues | true/false | true | boolean | include charging station main voltage MeterValues on three phased charging stations phaseLineToLineVoltageMeterValues | true/false | true | boolean | include charging station line to line voltage MeterValues on three phased charging stations diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 30b21bdc..71de426c 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -91,6 +91,10 @@ export default class ChargingStation { return !Utils.isUndefined(this.stationInfo.enableStatistics) ? this.stationInfo.enableStatistics : true; } + public getMayAuthorizeAtRemoteStart(): boolean | undefined { + return this.stationInfo.mayAuthorizeAtRemoteStart ?? true; + } + public getNumberOfPhases(): number | undefined { switch (this.getCurrentOutType()) { case CurrentType.AC: @@ -371,18 +375,18 @@ export default class ChargingStation { } } - public setChargingProfile(connectorId: number, cp: ChargingProfile): boolean { + public setChargingProfile(connectorId: number, cp: ChargingProfile): void { + let cpReplaced = false; if (!Utils.isEmptyArray(this.getConnector(connectorId).chargingProfiles)) { this.getConnector(connectorId).chargingProfiles?.forEach((chargingProfile: ChargingProfile, index: number) => { if (chargingProfile.chargingProfileId === cp.chargingProfileId || (chargingProfile.stackLevel === cp.stackLevel && chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose)) { this.getConnector(connectorId).chargingProfiles[index] = cp; - return true; + cpReplaced = true; } }); } - this.getConnector(connectorId).chargingProfiles?.push(cp); - return true; + !cpReplaced && this.getConnector(connectorId).chargingProfiles?.push(cp); } public resetTransactionOnConnector(connectorId: number): void { diff --git a/src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts index 03aae442..f27e2f02 100644 --- a/src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts @@ -256,63 +256,68 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer } private async handleRequestRemoteStartTransaction(commandPayload: RemoteStartTransactionRequest): Promise { - const transactionConnectorId: number = commandPayload.connectorId ?? 1; - await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.PREPARING); - this.chargingStation.getConnector(transactionConnectorId).status = OCPP16ChargePointStatus.PREPARING; - if (this.chargingStation.isChargingStationAvailable() && this.chargingStation.isConnectorAvailable(transactionConnectorId)) { - if (this.chargingStation.getAuthorizeRemoteTxRequests()) { - let authorized = false; - // Check if authorized - if (this.chargingStation.getLocalAuthListEnabled() && this.chargingStation.hasAuthorizedTags() - && this.chargingStation.authorizedTags.find((value) => value === commandPayload.idTag)) { - authorized = true; - if (commandPayload.chargingProfile && commandPayload.chargingProfile.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) { - this.chargingStation.setChargingProfile(transactionConnectorId, commandPayload.chargingProfile); - logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnector(transactionConnectorId).chargingProfiles); - } else if (commandPayload.chargingProfile && commandPayload.chargingProfile.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) { - await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.AVAILABLE); - this.chargingStation.getConnector(transactionConnectorId).status = OCPP16ChargePointStatus.AVAILABLE; - logger.warn(`${this.chargingStation.logPrefix()} Not allowed to set ${commandPayload.chargingProfile.chargingProfilePurpose} charging profile(s) at remote start transaction`); - return Constants.OCPP_RESPONSE_REJECTED; - } - } - if (!authorized) { - const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(transactionConnectorId, commandPayload.idTag); - if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { + const transactionConnectorId: number = commandPayload.connectorId; + if (transactionConnectorId) { + await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.PREPARING); + this.chargingStation.getConnector(transactionConnectorId).status = OCPP16ChargePointStatus.PREPARING; + if (this.chargingStation.isChargingStationAvailable() && this.chargingStation.isConnectorAvailable(transactionConnectorId)) { + if (this.chargingStation.getAuthorizeRemoteTxRequests()) { + let authorized = false; + // Check if authorized + if (this.chargingStation.getLocalAuthListEnabled() && this.chargingStation.hasAuthorizedTags() + && this.chargingStation.authorizedTags.find((value) => value === commandPayload.idTag)) { authorized = true; } + if (!authorized || (authorized && this.chargingStation.getMayAuthorizeAtRemoteStart())) { + const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(transactionConnectorId, commandPayload.idTag); + if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { + authorized = true; + } else { + authorized = false; + } + } + if (authorized) { + // Authorization successful, start transaction + await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag); + logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); + return await this.setRemoteStartChargingProfile(transactionConnectorId, commandPayload.chargingProfile) + ? Constants.OCPP_RESPONSE_ACCEPTED + : await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); + } + return await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); } - if (authorized) { - // Authorization successful, start transaction - await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag); - logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); - return Constants.OCPP_RESPONSE_ACCEPTED; - } - await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.AVAILABLE); - this.chargingStation.getConnector(transactionConnectorId).status = OCPP16ChargePointStatus.AVAILABLE; - logger.warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on connector Id ' + transactionConnectorId.toString() + ', idTag ' + commandPayload.idTag); - return Constants.OCPP_RESPONSE_REJECTED; - } - if (commandPayload.chargingProfile && commandPayload.chargingProfile.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) { - this.chargingStation.setChargingProfile(transactionConnectorId, commandPayload.chargingProfile); - logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnector(commandPayload.connectorId).chargingProfiles); - } else if (commandPayload.chargingProfile && commandPayload.chargingProfile.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) { - await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.AVAILABLE); - this.chargingStation.getConnector(transactionConnectorId).status = OCPP16ChargePointStatus.AVAILABLE; - logger.warn(`${this.chargingStation.logPrefix()} Not allowed to set ${commandPayload.chargingProfile.chargingProfilePurpose} charging profile(s) at remote start transaction`); - return Constants.OCPP_RESPONSE_REJECTED; + // No authorization check required, start transaction + await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag); + logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); + return await this.setRemoteStartChargingProfile(transactionConnectorId, commandPayload.chargingProfile) + ? Constants.OCPP_RESPONSE_ACCEPTED + : await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); } - // No authorization check required, start transaction - await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag); - logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); - return Constants.OCPP_RESPONSE_ACCEPTED; + return await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); } - await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.AVAILABLE); - this.chargingStation.getConnector(transactionConnectorId).status = OCPP16ChargePointStatus.AVAILABLE; - logger.warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on unavailable connector Id ' + transactionConnectorId.toString() + ', idTag ' + commandPayload.idTag); + return await this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); + } + + private async notifyRemoteStartTransactionRejected(connectorId: number, idTag: string): Promise { + await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE); + this.chargingStation.getConnector(connectorId).status = OCPP16ChargePointStatus.AVAILABLE; + logger.warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on connector Id ' + connectorId.toString() + ', availability: ' + this.chargingStation.getConnector(connectorId).availability + ', idTag ' + idTag); return Constants.OCPP_RESPONSE_REJECTED; } + private async setRemoteStartChargingProfile(connectorId: number, cp: OCPP16ChargingProfile): Promise { + if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) { + this.chargingStation.setChargingProfile(connectorId, cp); + logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnector(connectorId).chargingProfiles); + return true; + } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) { + await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE); + this.chargingStation.getConnector(connectorId).status = OCPP16ChargePointStatus.AVAILABLE; + logger.warn(`${this.chargingStation.logPrefix()} Not allowed to set ${cp.chargingProfilePurpose} charging profile(s) at remote start transaction`); + return false; + } + } + private async handleRequestRemoteStopTransaction(commandPayload: RemoteStopTransactionRequest): Promise { const transactionId = commandPayload.transactionId; for (const connector in this.chargingStation.connectors) { diff --git a/src/types/ChargingStationTemplate.ts b/src/types/ChargingStationTemplate.ts index fd1d988f..c65e94c6 100644 --- a/src/types/ChargingStationTemplate.ts +++ b/src/types/ChargingStationTemplate.ts @@ -58,6 +58,7 @@ export default interface ChargingStationTemplate { reconnectExponentialDelay?: boolean; registrationMaxRetries?: number; enableStatistics?: boolean; + mayAuthorizeAtRemoteStart: boolean; beginEndMeterValues?: boolean; outOfOrderEndMeterValues?: boolean; meteringPerTransaction?: boolean; -- 2.34.1