From 94ec7e9600ffc86ea375d2bfd3f4973074e1db69 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 27 Apr 2022 17:48:30 +0200 Subject: [PATCH] Fix truncated boot notification payload MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Reference #215 Signed-off-by: Jérôme Benoit --- src/charging-station/ChargingStation.ts | 60 +++++++++++++------ .../ocpp/1.6/OCPP16IncomingRequestService.ts | 4 +- src/utils/Utils.ts | 2 +- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index acaaa7df..690859e0 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -53,6 +53,7 @@ import { WSError, WebSocketCloseEventStatusCode } from '../types/WebSocket'; import WebSocket, { Data, OPEN, RawData } from 'ws'; import AutomaticTransactionGenerator from './AutomaticTransactionGenerator'; +import BaseError from '../exception/BaseError'; import { ChargePointErrorCode } from '../types/ocpp/ChargePointErrorCode'; import { ChargePointStatus } from '../types/ocpp/ChargePointStatus'; import ChargingStationInfo from '../types/ChargingStationInfo'; @@ -646,6 +647,14 @@ export default class ChargingStation { this.stopped = true; } + public async reset(reason?: StopTransactionReason): Promise { + await this.stop(reason); + await Utils.sleep(this.stationInfo.resetTime); + this.stationInfo = this.getStationInfo(); + this.stationInfo?.Connectors && delete this.stationInfo.Connectors; + this.start(); + } + public getConfigurationKey( key: string | StandardParametersKey, caseInsensitive = false @@ -925,7 +934,7 @@ export default class ChargingStation { params = params ?? {}; params.randomSerialNumberUpperCase = params?.randomSerialNumberUpperCase ?? true; params.randomSerialNumber = params?.randomSerialNumber ?? true; - if (existingStationInfo) { + if (!Utils.isEmptyObject(existingStationInfo)) { existingStationInfo?.chargePointSerialNumber && (stationInfo.chargePointSerialNumber = existingStationInfo.chargePointSerialNumber); existingStationInfo?.chargeBoxSerialNumber && @@ -950,6 +959,14 @@ export default class ChargingStation { private getStationInfoFromTemplate(): ChargingStationInfo { const stationInfo: ChargingStationInfo = this.getTemplateFromFile(); + if (Utils.isNullOrUndefined(stationInfo)) { + const logMsg = 'Fail to read charging station template file'; + logger.error(`${this.logPrefix()} ${logMsg}`); + throw new BaseError(logMsg); + } + if (Utils.isEmptyObject(stationInfo)) { + logger.warn(this.logPrefix(), 'Empty charging station template file'); + } const chargingStationId = this.getChargingStationId(stationInfo); // Deprecation template keys section this.warnDeprecatedTemplateKey( @@ -984,18 +1001,18 @@ export default class ChargingStation { } private createStationInfoHash(stationInfo: ChargingStationInfo): ChargingStationInfo { - const previousInfoHash = stationInfo.infoHash ?? ''; + const previousInfoHash = stationInfo?.infoHash ?? ''; delete stationInfo.infoHash; const currentInfoHash = crypto .createHash(Constants.DEFAULT_HASH_ALGORITHM) .update(JSON.stringify(stationInfo)) .digest('hex'); if ( - Utils.isEmptyString(previousInfoHash) || + (!Utils.isEmptyObject(stationInfo) && Utils.isEmptyString(previousInfoHash)) || (!Utils.isEmptyString(previousInfoHash) && currentInfoHash !== previousInfoHash) ) { stationInfo.infoHash = currentInfoHash; - } else { + } else if (!Utils.isEmptyObject(stationInfo)) { stationInfo.infoHash = previousInfoHash; } return stationInfo; @@ -1119,14 +1136,20 @@ export default class ChargingStation { ); } const templateMaxConnectors = this.getTemplateMaxNumberOfConnectors(); - if (templateMaxConnectors <= 0) { + if (templateMaxConnectors === 0) { logger.warn( `${this.logPrefix()} Charging station template ${ this.templateFile - } with no connector configuration` + } with empty connectors configuration` + ); + } else if (templateMaxConnectors < 0) { + logger.error( + `${this.logPrefix()} Charging station template ${ + this.templateFile + } with no connectors configuration defined` ); } - if (!this.stationInfo.Connectors[0]) { + if (!this.stationInfo?.Connectors[0]) { logger.warn( `${this.logPrefix()} Charging station template ${ this.templateFile @@ -1136,7 +1159,7 @@ export default class ChargingStation { // Sanity check if ( maxConnectors > - (this.stationInfo.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) && + (this.stationInfo?.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) && !this.stationInfo.randomConnectors ) { logger.warn( @@ -1148,7 +1171,7 @@ export default class ChargingStation { } const connectorsConfigHash = crypto .createHash(Constants.DEFAULT_HASH_ALGORITHM) - .update(JSON.stringify(this.stationInfo.Connectors) + maxConnectors.toString()) + .update(JSON.stringify(this.stationInfo?.Connectors) + maxConnectors.toString()) .digest('hex'); const connectorsConfigChanged = this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash; @@ -1157,16 +1180,16 @@ export default class ChargingStation { this.connectorsConfigurationHash = connectorsConfigHash; // Add connector Id 0 let lastConnector = '0'; - for (lastConnector in this.stationInfo.Connectors) { + for (lastConnector in this.stationInfo?.Connectors) { const lastConnectorId = Utils.convertToInt(lastConnector); if ( lastConnectorId === 0 && this.getUseConnectorId0() && - this.stationInfo.Connectors[lastConnector] + this.stationInfo?.Connectors[lastConnector] ) { this.connectors.set( lastConnectorId, - Utils.cloneObject(this.stationInfo.Connectors[lastConnector]) + Utils.cloneObject(this.stationInfo?.Connectors[lastConnector]) ); this.getConnectorStatus(lastConnectorId).availability = AvailabilityType.OPERATIVE; if (Utils.isUndefined(this.getConnectorStatus(lastConnectorId)?.chargingProfiles)) { @@ -1176,7 +1199,7 @@ export default class ChargingStation { } // Generate all connectors if ( - (this.stationInfo.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) > 0 + (this.stationInfo?.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) > 0 ) { for (let index = 1; index <= maxConnectors; index++) { const randConnectorId = this.stationInfo.randomConnectors @@ -1184,7 +1207,7 @@ export default class ChargingStation { : index; this.connectors.set( index, - Utils.cloneObject(this.stationInfo.Connectors[randConnectorId]) + Utils.cloneObject(this.stationInfo?.Connectors[randConnectorId]) ); this.getConnectorStatus(index).availability = AvailabilityType.OPERATIVE; if (Utils.isUndefined(this.getConnectorStatus(index)?.chargingProfiles)) { @@ -1197,7 +1220,7 @@ export default class ChargingStation { this.stationInfo = this.createStationInfoHash(this.stationInfo); this.saveStationInfo(); // Avoid duplication of connectors related information in RAM - delete this.stationInfo.Connectors; + this.stationInfo?.Connectors && delete this.stationInfo.Connectors; // Initialize transaction attributes on connectors for (const connectorId of this.connectors.keys()) { if (connectorId > 0 && !this.getConnectorStatus(connectorId)?.transactionStarted) { @@ -1744,7 +1767,10 @@ export default class ChargingStation { } private getTemplateMaxNumberOfConnectors(): number { - return Object.keys(this.stationInfo.Connectors).length; + if (!this.stationInfo?.Connectors) { + return -1; + } + return Object.keys(this.stationInfo?.Connectors).length; } private getMaxNumberOfConnectors(): number { @@ -1756,7 +1782,7 @@ export default class ChargingStation { } else if (!Utils.isUndefined(this.stationInfo.numberOfConnectors)) { maxConnectors = this.stationInfo.numberOfConnectors as number; } else { - maxConnectors = this.stationInfo.Connectors[0] + maxConnectors = this.stationInfo?.Connectors[0] ? this.getTemplateMaxNumberOfConnectors() - 1 : this.getTemplateMaxNumberOfConnectors(); } diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index 6df6f75f..4ef74b04 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -192,11 +192,9 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer private handleRequestReset(commandPayload: ResetRequest): DefaultResponse { // eslint-disable-next-line @typescript-eslint/no-misused-promises setImmediate(async (): Promise => { - await this.chargingStation.stop( + await this.chargingStation.reset( (commandPayload.type + 'Reset') as OCPP16StopTransactionReason ); - await Utils.sleep(this.chargingStation.stationInfo.resetTime); - this.chargingStation.start(); }); logger.info( `${this.chargingStation.logPrefix()} ${ diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 8eebc2ab..074b1073 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -197,7 +197,7 @@ export default class Utils { return true; } - public static isEmptyObject(obj: Record): boolean { + public static isEmptyObject(obj: object): boolean { return !Object.keys(obj).length; } -- 2.34.1