From aef1b33aa631deaf5c939dc6a232ca8cc8147fc6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 13 Aug 2021 20:20:51 +0200 Subject: [PATCH] Add support for performance statistics for all requests sent to the OCPP server. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The distinction between request and response is not done. Signed-off-by: Jérôme Benoit --- .../AutomaticTransactionGenerator.ts | 60 +++++---- src/charging-station/Bootstrap.ts | 2 +- src/charging-station/ChargingStation.ts | 5 +- .../ocpp/1.6/OCPP16RequestService.ts | 102 +++++++------- .../ocpp/OCPPRequestService.ts | 11 +- .../{CommandStatistics.ts => Statistics.ts} | 14 +- src/utils/PerformanceStatistics.ts | 126 +++++++++--------- 7 files changed, 162 insertions(+), 158 deletions(-) rename src/types/{CommandStatistics.ts => Statistics.ts} (59%) diff --git a/src/charging-station/AutomaticTransactionGenerator.ts b/src/charging-station/AutomaticTransactionGenerator.ts index f8183b8b..73412c16 100644 --- a/src/charging-station/AutomaticTransactionGenerator.ts +++ b/src/charging-station/AutomaticTransactionGenerator.ts @@ -78,13 +78,7 @@ export default class AutomaticTransactionGenerator { let skip = 0; if (start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) { // Start transaction - let startATGTransaction: (connectorId: number, self: AutomaticTransactionGenerator) => Promise; - if (this.chargingStation.getEnableStatistics()) { - startATGTransaction = PerformanceStatistics.timedFunction(this.startATGTransaction.bind(this)); - } else { - startATGTransaction = this.startATGTransaction.bind(this); - } - const startResponse = await startATGTransaction(connectorId, this); + const startResponse = await this.startTransaction(connectorId); if (startResponse?.idTagInfo?.status !== AuthorizationStatus.ACCEPTED) { logger.warn(this.logPrefix(connectorId) + ' transaction rejected'); await Utils.sleep(Constants.CHARGING_STATION_ATG_WAIT_TIME); @@ -97,13 +91,7 @@ export default class AutomaticTransactionGenerator { // Stop transaction if (this.chargingStation.getConnector(connectorId)?.transactionStarted) { logger.info(this.logPrefix(connectorId) + ' stop transaction ' + this.chargingStation.getConnector(connectorId).transactionId.toString()); - let stopATGTransaction: (connectorId: number, self: AutomaticTransactionGenerator) => Promise; - if (this.chargingStation.getEnableStatistics()) { - stopATGTransaction = PerformanceStatistics.timedFunction(this.stopATGTransaction.bind(this)); - } else { - stopATGTransaction = this.stopATGTransaction.bind(this); - } - await stopATGTransaction(connectorId, this); + await this.stopTransaction(connectorId); } } } else { @@ -115,32 +103,46 @@ export default class AutomaticTransactionGenerator { } // eslint-disable-next-line consistent-this - private async startATGTransaction(connectorId: number, self: AutomaticTransactionGenerator): Promise { - if (self.chargingStation.hasAuthorizedTags()) { - const tagId = self.chargingStation.getRandomTagId(); - if (self.chargingStation.getAutomaticTransactionGeneratorRequireAuthorize()) { + private async startTransaction(connectorId: number): Promise { + const measureId = 'StartTransaction with ATG'; + const beginId = PerformanceStatistics.beginMeasure(measureId); + let startResponse: StartTransactionResponse; + if (this.chargingStation.hasAuthorizedTags()) { + const tagId = this.chargingStation.getRandomTagId(); + if (this.chargingStation.getAutomaticTransactionGeneratorRequireAuthorize()) { // Authorize tagId - const authorizeResponse = await self.chargingStation.ocppRequestService.sendAuthorize(connectorId, tagId); + const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(connectorId, tagId); if (authorizeResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { - logger.info(self.logPrefix(connectorId) + ' start transaction for tagID ' + tagId); + logger.info(this.logPrefix(connectorId) + ' start transaction for tagID ' + tagId); // Start transaction - return self.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId); + startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId); + PerformanceStatistics.endMeasure(measureId, beginId); + return startResponse; } + PerformanceStatistics.endMeasure(measureId, beginId); return authorizeResponse; } - logger.info(self.logPrefix(connectorId) + ' start transaction for tagID ' + tagId); + logger.info(this.logPrefix(connectorId) + ' start transaction for tagID ' + tagId); // Start transaction - return self.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId); + startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, tagId); + PerformanceStatistics.endMeasure(measureId, beginId); + return startResponse; } - logger.info(self.logPrefix(connectorId) + ' start transaction without a tagID'); - return self.chargingStation.ocppRequestService.sendStartTransaction(connectorId); + logger.info(this.logPrefix(connectorId) + ' start transaction without a tagID'); + startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId); + PerformanceStatistics.endMeasure(measureId, beginId); + return startResponse; } // eslint-disable-next-line consistent-this - private async stopATGTransaction(connectorId: number, self: AutomaticTransactionGenerator): Promise { - const transactionId = self.chargingStation.getConnector(connectorId).transactionId; - return self.chargingStation.ocppRequestService.sendStopTransaction(transactionId, self.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId), - self.chargingStation.getTransactionIdTag(transactionId)); + private async stopTransaction(connectorId: number): Promise { + const measureId = 'StopTransaction with ATG'; + const beginId = PerformanceStatistics.beginMeasure(measureId); + const transactionId = this.chargingStation.getConnector(connectorId).transactionId; + const stopResponse = this.chargingStation.ocppRequestService.sendStopTransaction(transactionId, + this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId), this.chargingStation.getTransactionIdTag(transactionId)); + PerformanceStatistics.endMeasure(measureId, beginId); + return stopResponse; } private logPrefix(connectorId?: number): string { diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index a9e6d1ef..f5c0de9f 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -9,7 +9,7 @@ import { version } from '../../package.json'; export default class Bootstrap { private static instance: Bootstrap; - private version: string = version ; + private version: string = version; private started: boolean; private workerScript: string; private workerImplementationInstance: WorkerAbstract | null = null; diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 2626a233..79c9e65a 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -290,7 +290,7 @@ export default class ChargingStation { if (interval > 0) { // eslint-disable-next-line @typescript-eslint/no-misused-promises this.getConnector(connectorId).transactionSetInterval = setInterval(async (): Promise => { - await this.ocppRequestService.sendMeterValues(connectorId, this.getConnector(connectorId).transactionId, interval, this.ocppRequestService); + await this.ocppRequestService.sendMeterValues(connectorId, this.getConnector(connectorId).transactionId, interval); }, interval); } else { logger.error(`${this.logPrefix()} Charging station ${StandardParametersKey.MeterValueSampleInterval} configuration set to ${interval ? Utils.milliSecondsToHHMMSS(interval) : interval}, not sending MeterValues`); @@ -413,6 +413,7 @@ export default class ChargingStation { if (!Utils.isEmptyArray(this.messageQueue)) { this.messageQueue.forEach((message, index) => { this.messageQueue.splice(index, 1); + // TODO: evaluate the need to track performance this.wsConnection.send(message); }); } @@ -649,7 +650,7 @@ export default class ChargingStation { // Incoming Message case MessageType.CALL_MESSAGE: if (this.getEnableStatistics()) { - this.performanceStatistics.addMessage(commandName, messageType); + this.performanceStatistics.addRequestStatistic(commandName, messageType); } // Process the call await this.ocppIncomingRequestService.handleRequest(messageId, commandName, commandPayload); diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index 2c17b334..dcd38a81 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -117,15 +117,15 @@ export default class OCPP16RequestService extends OCPPRequestService { } // eslint-disable-next-line consistent-this - public async sendMeterValues(connectorId: number, transactionId: number, interval: number, self: OCPPRequestService, debug = false): Promise { + public async sendMeterValues(connectorId: number, transactionId: number, interval: number, debug = false): Promise { try { const meterValue: OCPP16MeterValue = { timestamp: new Date().toISOString(), sampledValue: [], }; - const connector = self.chargingStation.getConnector(connectorId); + const connector = this.chargingStation.getConnector(connectorId); // SoC measurand - const socSampledValueTemplate = self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.STATE_OF_CHARGE); + const socSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.STATE_OF_CHARGE); if (socSampledValueTemplate) { const socSampledValueTemplateValue = socSampledValueTemplate.value ? Utils.getRandomFloatFluctuatedRounded(parseInt(socSampledValueTemplate.value), socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) @@ -133,33 +133,33 @@ export default class OCPP16RequestService extends OCPPRequestService { meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue)); const sampledValuesIndex = meterValue.sampledValue.length - 1; if (Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > 100 || debug) { - logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/100`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/100`); } } // Voltage measurand - const voltageSampledValueTemplate = self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.VOLTAGE); + const voltageSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.VOLTAGE); if (voltageSampledValueTemplate) { - const voltageSampledValueTemplateValue = voltageSampledValueTemplate.value ? parseInt(voltageSampledValueTemplate.value) : self.chargingStation.getVoltageOut(); + const voltageSampledValueTemplateValue = voltageSampledValueTemplate.value ? parseInt(voltageSampledValueTemplate.value) : this.chargingStation.getVoltageOut(); const fluctuationPercent = voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT; const voltageMeasurandValue = Utils.getRandomFloatFluctuatedRounded(voltageSampledValueTemplateValue, fluctuationPercent); - if (self.chargingStation.getNumberOfPhases() !== 3 || (self.chargingStation.getNumberOfPhases() === 3 && self.chargingStation.getMainVoltageMeterValues())) { + if (this.chargingStation.getNumberOfPhases() !== 3 || (this.chargingStation.getNumberOfPhases() === 3 && this.chargingStation.getMainVoltageMeterValues())) { meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue)); } - for (let phase = 1; self.chargingStation.getNumberOfPhases() === 3 && phase <= self.chargingStation.getNumberOfPhases(); phase++) { + for (let phase = 1; this.chargingStation.getNumberOfPhases() === 3 && phase <= this.chargingStation.getNumberOfPhases(); phase++) { const phaseLineToNeutralValue = `L${phase}-N`; - const voltagePhaseLineToNeutralSampledValueTemplate = self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.VOLTAGE, + const voltagePhaseLineToNeutralSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.VOLTAGE, phaseLineToNeutralValue as OCPP16MeterValuePhase); let voltagePhaseLineToNeutralMeasurandValue: number; if (voltagePhaseLineToNeutralSampledValueTemplate) { - const voltagePhaseLineToNeutralSampledValueTemplateValue = voltagePhaseLineToNeutralSampledValueTemplate.value ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value) : self.chargingStation.getVoltageOut(); + const voltagePhaseLineToNeutralSampledValueTemplateValue = voltagePhaseLineToNeutralSampledValueTemplate.value ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value) : this.chargingStation.getVoltageOut(); const fluctuationPhaseToNeutralPercent = voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT; voltagePhaseLineToNeutralMeasurandValue = Utils.getRandomFloatFluctuatedRounded(voltagePhaseLineToNeutralSampledValueTemplateValue, fluctuationPhaseToNeutralPercent); } meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate, voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue, null, phaseLineToNeutralValue as OCPP16MeterValuePhase)); - if (self.chargingStation.getPhaseLineToLineVoltageMeterValues()) { - const phaseLineToLineValue = `L${phase}-L${(phase + 1) % self.chargingStation.getNumberOfPhases() !== 0 ? (phase + 1) % self.chargingStation.getNumberOfPhases() : self.chargingStation.getNumberOfPhases()}`; - const voltagePhaseLineToLineSampledValueTemplate = self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.VOLTAGE, phaseLineToLineValue as OCPP16MeterValuePhase); + if (this.chargingStation.getPhaseLineToLineVoltageMeterValues()) { + const phaseLineToLineValue = `L${phase}-L${(phase + 1) % this.chargingStation.getNumberOfPhases() !== 0 ? (phase + 1) % this.chargingStation.getNumberOfPhases() : this.chargingStation.getNumberOfPhases()}`; + const voltagePhaseLineToLineSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.VOLTAGE, phaseLineToLineValue as OCPP16MeterValuePhase); let voltagePhaseLineToLineMeasurandValue: number; if (voltagePhaseLineToLineSampledValueTemplate) { const voltagePhaseLineToLineSampledValueTemplateValue = voltagePhaseLineToLineSampledValueTemplate.value ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value) : Voltage.VOLTAGE_400; @@ -173,27 +173,27 @@ export default class OCPP16RequestService extends OCPPRequestService { } } // Power.Active.Import measurand - const powerSampledValueTemplate = self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT); + const powerSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT); let powerPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}; - if (self.chargingStation.getNumberOfPhases() === 3) { + if (this.chargingStation.getNumberOfPhases() === 3) { powerPerPhaseSampledValueTemplates = { - L1: self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L1_N), - L2: self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L2_N), - L3: self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L3_N), + L1: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L1_N), + L2: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L2_N), + L3: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L3_N), }; } if (powerSampledValueTemplate) { - OCPP16ServiceUtils.checkMeasurandPowerDivider(self.chargingStation, powerSampledValueTemplate.measurand); - const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${powerSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self.chargingStation.getCurrentOutType()} currentOutType in template file ${self.chargingStation.stationTemplateFile}, cannot calculate ${powerSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`; + OCPP16ServiceUtils.checkMeasurandPowerDivider(this.chargingStation, powerSampledValueTemplate.measurand); + const errMsg = `${this.chargingStation.logPrefix()} MeterValues measurand ${powerSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${this.chargingStation.stationTemplateFile}, cannot calculate ${powerSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`; const powerMeasurandValues = {} as MeasurandValues; const unitDivider = powerSampledValueTemplate?.unit === MeterValueUnit.KILO_WATT ? 1000 : 1; - const maxPower = Math.round(self.chargingStation.stationInfo.maxPower / self.chargingStation.stationInfo.powerDivider); - const maxPowerPerPhase = Math.round((self.chargingStation.stationInfo.maxPower / self.chargingStation.stationInfo.powerDivider) / self.chargingStation.getNumberOfPhases()); - switch (self.chargingStation.getCurrentOutType()) { + const maxPower = Math.round(this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider); + const maxPowerPerPhase = Math.round((this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider) / this.chargingStation.getNumberOfPhases()); + switch (this.chargingStation.getCurrentOutType()) { case CurrentType.AC: - if (self.chargingStation.getNumberOfPhases() === 3) { + if (this.chargingStation.getNumberOfPhases() === 3) { const defaultFluctuatedPowerPerPhase = powerSampledValueTemplate.value - && Utils.getRandomFloatFluctuatedRounded(parseInt(powerSampledValueTemplate.value) / self.chargingStation.getNumberOfPhases(), powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); + && Utils.getRandomFloatFluctuatedRounded(parseInt(powerSampledValueTemplate.value) / this.chargingStation.getNumberOfPhases(), powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); const phase1FluctuatedValue = powerPerPhaseSampledValueTemplates?.L1?.value && Utils.getRandomFloatFluctuatedRounded(parseInt(powerPerPhaseSampledValueTemplates.L1.value), powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); const phase2FluctuatedValue = powerPerPhaseSampledValueTemplates?.L2?.value @@ -225,38 +225,38 @@ export default class OCPP16RequestService extends OCPPRequestService { const sampledValuesIndex = meterValue.sampledValue.length - 1; const maxPowerRounded = Utils.roundTo(maxPower / unitDivider, 2); if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxPowerRounded || debug) { - logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxPowerRounded}`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxPowerRounded}`); } - for (let phase = 1; self.chargingStation.getNumberOfPhases() === 3 && phase <= self.chargingStation.getNumberOfPhases(); phase++) { + for (let phase = 1; this.chargingStation.getNumberOfPhases() === 3 && phase <= this.chargingStation.getNumberOfPhases(); phase++) { const phaseValue = `L${phase}-N`; meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(powerPerPhaseSampledValueTemplates[`L${phase}`] ?? powerSampledValueTemplate, powerMeasurandValues[`L${phase}`], null, phaseValue as OCPP16MeterValuePhase)); const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1; const maxPowerPerPhaseRounded = Utils.roundTo(maxPowerPerPhase / unitDivider, 2); if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > maxPowerPerPhaseRounded || debug) { - logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxPowerPerPhaseRounded}`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxPowerPerPhaseRounded}`); } } } // Current.Import measurand - const currentSampledValueTemplate = self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT); + const currentSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT); let currentPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}; - if (self.chargingStation.getNumberOfPhases() === 3) { + if (this.chargingStation.getNumberOfPhases() === 3) { currentPerPhaseSampledValueTemplates = { - L1: self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L1), - L2: self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L2), - L3: self.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L3), + L1: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L1), + L2: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L2), + L3: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L3), }; } if (currentSampledValueTemplate) { - OCPP16ServiceUtils.checkMeasurandPowerDivider(self.chargingStation, currentSampledValueTemplate.measurand); - const errMsg = `${self.chargingStation.logPrefix()} MeterValues measurand ${currentSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self.chargingStation.getCurrentOutType()} currentOutType in template file ${self.chargingStation.stationTemplateFile}, cannot calculate ${currentSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`; + OCPP16ServiceUtils.checkMeasurandPowerDivider(this.chargingStation, currentSampledValueTemplate.measurand); + const errMsg = `${this.chargingStation.logPrefix()} MeterValues measurand ${currentSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${this.chargingStation.stationTemplateFile}, cannot calculate ${currentSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`; const currentMeasurandValues: MeasurandValues = {} as MeasurandValues; let maxAmperage: number; - switch (self.chargingStation.getCurrentOutType()) { + switch (this.chargingStation.getCurrentOutType()) { case CurrentType.AC: - maxAmperage = ACElectricUtils.amperagePerPhaseFromPower(self.chargingStation.getNumberOfPhases(), self.chargingStation.stationInfo.maxPower / self.chargingStation.stationInfo.powerDivider, self.chargingStation.getVoltageOut()); - if (self.chargingStation.getNumberOfPhases() === 3) { + maxAmperage = ACElectricUtils.amperagePerPhaseFromPower(this.chargingStation.getNumberOfPhases(), this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider, this.chargingStation.getVoltageOut()); + if (this.chargingStation.getNumberOfPhases() === 3) { const defaultFluctuatedAmperagePerPhase = currentSampledValueTemplate.value && Utils.getRandomFloatFluctuatedRounded(parseInt(currentSampledValueTemplate.value), currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); const phase1FluctuatedValue = currentPerPhaseSampledValueTemplates?.L1?.value @@ -275,10 +275,10 @@ export default class OCPP16RequestService extends OCPPRequestService { currentMeasurandValues.L2 = 0; currentMeasurandValues.L3 = 0; } - currentMeasurandValues.allPhases = Utils.roundTo((currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / self.chargingStation.getNumberOfPhases(), 2); + currentMeasurandValues.allPhases = Utils.roundTo((currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / this.chargingStation.getNumberOfPhases(), 2); break; case CurrentType.DC: - maxAmperage = DCElectricUtils.amperage(self.chargingStation.stationInfo.maxPower / self.chargingStation.stationInfo.powerDivider, self.chargingStation.getVoltageOut()); + maxAmperage = DCElectricUtils.amperage(this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider, this.chargingStation.getVoltageOut()); currentMeasurandValues.allPhases = currentSampledValueTemplate.value ? Utils.getRandomFloatFluctuatedRounded(parseInt(currentSampledValueTemplate.value), currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) : Utils.getRandomFloatRounded(maxAmperage); @@ -290,27 +290,27 @@ export default class OCPP16RequestService extends OCPPRequestService { meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(currentSampledValueTemplate, currentMeasurandValues.allPhases)); const sampledValuesIndex = meterValue.sampledValue.length - 1; if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxAmperage || debug) { - logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxAmperage}`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxAmperage}`); } - for (let phase = 1; self.chargingStation.getNumberOfPhases() === 3 && phase <= self.chargingStation.getNumberOfPhases(); phase++) { + for (let phase = 1; this.chargingStation.getNumberOfPhases() === 3 && phase <= this.chargingStation.getNumberOfPhases(); phase++) { const phaseValue = `L${phase}`; meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(currentPerPhaseSampledValueTemplates[phaseValue] ?? currentSampledValueTemplate, currentMeasurandValues[phaseValue], null, phaseValue as OCPP16MeterValuePhase)); const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1; if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > maxAmperage || debug) { - logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxAmperage}`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxAmperage}`); } } } // Energy.Active.Import.Register measurand (default) - const energySampledValueTemplate = self.chargingStation.getSampledValueTemplate(connectorId); + const energySampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId); if (energySampledValueTemplate) { - OCPP16ServiceUtils.checkMeasurandPowerDivider(self.chargingStation, energySampledValueTemplate.measurand); + OCPP16ServiceUtils.checkMeasurandPowerDivider(this.chargingStation, energySampledValueTemplate.measurand); const unitDivider = energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; const energyMeasurandValue = energySampledValueTemplate.value // Cumulate the fluctuated value around the static one ? Utils.getRandomFloatFluctuatedRounded(parseInt(energySampledValueTemplate.value), energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) - : Utils.getRandomInt(self.chargingStation.stationInfo.maxPower / (self.chargingStation.stationInfo.powerDivider * 3600000) * interval); + : Utils.getRandomInt(this.chargingStation.stationInfo.maxPower / (this.chargingStation.stationInfo.powerDivider * 3600000) * interval); // Persist previous value on connector if (connector && !Utils.isNullOrUndefined(connector.energyActiveImportRegisterValue) && connector.energyActiveImportRegisterValue >= 0 && !Utils.isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) && connector.transactionEnergyActiveImportRegisterValue >= 0) { @@ -321,12 +321,12 @@ export default class OCPP16RequestService extends OCPPRequestService { connector.transactionEnergyActiveImportRegisterValue = 0; } meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(energySampledValueTemplate, - Utils.roundTo(self.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) / unitDivider, 4))); + Utils.roundTo(this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) / unitDivider, 4))); const sampledValuesIndex = meterValue.sampledValue.length - 1; - const maxEnergy = Math.round(self.chargingStation.stationInfo.maxPower * 3600 / (self.chargingStation.stationInfo.powerDivider * interval)); + const maxEnergy = Math.round(this.chargingStation.stationInfo.maxPower * 3600 / (this.chargingStation.stationInfo.powerDivider * interval)); const maxEnergyRounded = Utils.roundTo(maxEnergy / unitDivider, 4); if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxEnergyRounded || debug) { - logger.error(`${self.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxEnergyRounded}`); + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxEnergyRounded}`); } } const payload: MeterValuesRequest = { @@ -334,9 +334,9 @@ export default class OCPP16RequestService extends OCPPRequestService { transactionId, meterValue, }; - await self.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES); + await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES); } catch (error) { - self.handleRequestError(OCPP16RequestCommand.METER_VALUES, error); + this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error); } } diff --git a/src/charging-station/ocpp/OCPPRequestService.ts b/src/charging-station/ocpp/OCPPRequestService.ts index 0d8f8cf6..6c24665a 100644 --- a/src/charging-station/ocpp/OCPPRequestService.ts +++ b/src/charging-station/ocpp/OCPPRequestService.ts @@ -11,6 +11,7 @@ import { MessageType } from '../../types/ocpp/MessageType'; import { MeterValue } from '../../types/ocpp/MeterValues'; import OCPPError from '../OCPPError'; import OCPPResponseService from './OCPPResponseService'; +import PerformanceStatistics from '../../utils/PerformanceStatistics'; import logger from '../../utils/Logger'; export default abstract class OCPPRequestService { @@ -50,10 +51,12 @@ export default abstract class OCPPRequestService { // Check if wsConnection opened and charging station registered if (this.chargingStation.isWebSocketOpen() && (this.chargingStation.isRegistered() || commandName === RequestCommand.BOOT_NOTIFICATION)) { if (this.chargingStation.getEnableStatistics()) { - this.chargingStation.performanceStatistics.addMessage(commandName, messageType); + this.chargingStation.performanceStatistics.addRequestStatistic(commandName, messageType); } // Yes: Send Message + const beginId = PerformanceStatistics.beginMeasure(commandName); this.chargingStation.wsConnection.send(messageToSend); + PerformanceStatistics.endMeasure(commandName, beginId); } else if (commandName !== RequestCommand.BOOT_NOTIFICATION) { // Buffer it this.chargingStation.addToMessageQueue(messageToSend); @@ -77,7 +80,7 @@ export default abstract class OCPPRequestService { */ async function responseCallback(payload: Record | string, requestPayload: Record): Promise { if (self.chargingStation.getEnableStatistics()) { - self.chargingStation.performanceStatistics.addMessage(commandName, MessageType.CALL_RESULT_MESSAGE); + self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_RESULT_MESSAGE); } // Send the response await self.ocppResponseService.handleResponse(commandName as RequestCommand, payload, requestPayload); @@ -91,7 +94,7 @@ export default abstract class OCPPRequestService { */ function rejectCallback(error: OCPPError): void { if (self.chargingStation.getEnableStatistics()) { - self.chargingStation.performanceStatistics.addMessage(commandName, MessageType.CALL_ERROR_MESSAGE); + self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_ERROR_MESSAGE); } logger.debug(`${self.chargingStation.logPrefix()} Error: %j occurred when calling command %s with parameters: %j`, error, commandName, commandParams); // Build Exception @@ -114,7 +117,7 @@ export default abstract class OCPPRequestService { public abstract sendAuthorize(connectorId: number, idTag?: string): Promise; public abstract sendStartTransaction(connectorId: number, idTag?: string): Promise; public abstract sendStopTransaction(transactionId: number, meterStop: number, idTag?: string, reason?: StopTransactionReason): Promise; - public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number, self: OCPPRequestService): Promise; + public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number): Promise; public abstract sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: MeterValue): Promise; public abstract sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: MeterValue): Promise; public abstract sendDiagnosticsStatusNotification(diagnosticsStatus: DiagnosticsStatus): Promise; diff --git a/src/types/CommandStatistics.ts b/src/types/Statistics.ts similarity index 59% rename from src/types/CommandStatistics.ts rename to src/types/Statistics.ts index 2b84a5e4..7c099bc9 100644 --- a/src/types/CommandStatistics.ts +++ b/src/types/Statistics.ts @@ -1,14 +1,6 @@ import { CircularArray } from '../utils/CircularArray'; -import { EntryType } from 'perf_hooks'; -export interface PerfEntry { - name: string; - entryType: EntryType; - startTime: number; - duration: number; -} - -export interface CommandStatisticsData { +export interface StatisticsData { countRequest: number; countResponse: number; countError: number; @@ -23,7 +15,7 @@ export interface CommandStatisticsData { stdDevTimeMeasurement: number; } -export default interface CommandStatistics { +export default interface Statistics { id: string; - commandsStatisticsData: Record; + statisticsData: Record; } diff --git a/src/utils/PerformanceStatistics.ts b/src/utils/PerformanceStatistics.ts index c2d1527a..e7cd58e8 100644 --- a/src/utils/PerformanceStatistics.ts +++ b/src/utils/PerformanceStatistics.ts @@ -1,7 +1,7 @@ import { CircularArray, DEFAULT_CIRCULAR_ARRAY_SIZE } from './CircularArray'; -import CommandStatistics, { CommandStatisticsData, PerfEntry } from '../types/CommandStatistics'; import { IncomingRequestCommand, RequestCommand } from '../types/ocpp/Requests'; import { PerformanceEntry, PerformanceObserver, performance } from 'perf_hooks'; +import Statistics, { StatisticsData } from '../types/Statistics'; import Configuration from './Configuration'; import { MessageType } from '../types/ocpp/MessageType'; @@ -10,50 +10,58 @@ import logger from './Logger'; export default class PerformanceStatistics { private objId: string; - private commandsStatistics: CommandStatistics; + private performanceObserver: PerformanceObserver; + private statistics: Statistics; + private displayInterval: NodeJS.Timeout; public constructor(objId: string) { - this.initFunctionPerformanceObserver(); this.objId = objId; - this.commandsStatistics = { id: this.objId ? this.objId : 'Object id not specified', commandsStatisticsData: {} }; + this.initializePerformanceObserver(); + this.statistics = { id: this.objId ?? 'Object id not specified', statisticsData: {} }; } - public static timedFunction(method: (...optionalParams: any[]) => any): (...optionalParams: any[]) => any { - return performance.timerify(method); + public static beginMeasure(id: string): string { + const beginId = 'begin' + id.charAt(0).toUpperCase() + id.slice(1); + performance.mark(beginId); + return beginId; } - public addMessage(command: RequestCommand | IncomingRequestCommand, messageType: MessageType): void { + public static endMeasure(name: string, beginId: string): void { + performance.measure(name, beginId); + } + + public addRequestStatistic(command: RequestCommand | IncomingRequestCommand, messageType: MessageType): void { switch (messageType) { case MessageType.CALL_MESSAGE: - if (this.commandsStatistics.commandsStatisticsData[command] && this.commandsStatistics.commandsStatisticsData[command].countRequest) { - this.commandsStatistics.commandsStatisticsData[command].countRequest++; + if (this.statistics.statisticsData[command] && this.statistics.statisticsData[command].countRequest) { + this.statistics.statisticsData[command].countRequest++; } else { - this.commandsStatistics.commandsStatisticsData[command] = {} as CommandStatisticsData; - this.commandsStatistics.commandsStatisticsData[command].countRequest = 1; + this.statistics.statisticsData[command] = {} as StatisticsData; + this.statistics.statisticsData[command].countRequest = 1; } break; case MessageType.CALL_RESULT_MESSAGE: - if (this.commandsStatistics.commandsStatisticsData[command]) { - if (this.commandsStatistics.commandsStatisticsData[command].countResponse) { - this.commandsStatistics.commandsStatisticsData[command].countResponse++; + if (this.statistics.statisticsData[command]) { + if (this.statistics.statisticsData[command].countResponse) { + this.statistics.statisticsData[command].countResponse++; } else { - this.commandsStatistics.commandsStatisticsData[command].countResponse = 1; + this.statistics.statisticsData[command].countResponse = 1; } } else { - this.commandsStatistics.commandsStatisticsData[command] = {} as CommandStatisticsData; - this.commandsStatistics.commandsStatisticsData[command].countResponse = 1; + this.statistics.statisticsData[command] = {} as StatisticsData; + this.statistics.statisticsData[command].countResponse = 1; } break; case MessageType.CALL_ERROR_MESSAGE: - if (this.commandsStatistics.commandsStatisticsData[command]) { - if (this.commandsStatistics.commandsStatisticsData[command].countError) { - this.commandsStatistics.commandsStatisticsData[command].countError++; + if (this.statistics.statisticsData[command]) { + if (this.statistics.statisticsData[command].countError) { + this.statistics.statisticsData[command].countError++; } else { - this.commandsStatistics.commandsStatisticsData[command].countError = 1; + this.statistics.statisticsData[command].countError = 1; } } else { - this.commandsStatistics.commandsStatisticsData[command] = {} as CommandStatisticsData; - this.commandsStatistics.commandsStatisticsData[command].countError = 1; + this.statistics.statisticsData[command] = {} as StatisticsData; + this.statistics.statisticsData[command].countError = 1; } break; default: @@ -62,39 +70,40 @@ export default class PerformanceStatistics { } } - public logPerformance(entry: PerformanceEntry): void { - this.addPerformanceTimer(entry.name, entry.duration); - const perfEntry: PerfEntry = { - name: entry.name, - entryType: entry.entryType, - startTime: entry.startTime, - duration: entry.duration - }; - logger.debug(`${this.logPrefix()} method or function '${entry.name}' performance entry: %j`, perfEntry); + public start(): void { + this.startDisplayInterval(); } - public start(): void { - this.displayInterval(); + public stop(): void { + clearInterval(this.displayInterval); + performance.clearMarks(); + this.performanceObserver.disconnect(); } - private initFunctionPerformanceObserver(): void { - const performanceObserver = new PerformanceObserver((list, observer) => { - this.logPerformance(list.getEntries()[0]); - observer.disconnect(); + private initializePerformanceObserver(): void { + this.performanceObserver = new PerformanceObserver((list) => { + this.logPerformanceEntry(list.getEntries()[0]); }); - performanceObserver.observe({ entryTypes: ['function'] }); + this.performanceObserver.observe({ entryTypes: ['measure'] }); + } + + private logPerformanceEntry(entry: PerformanceEntry): void { + this.addPerformanceStatistic(entry.name, entry.duration); + logger.debug(`${this.logPrefix()} '${entry.name}' performance entry: %j`, entry); } - private display(): void { - logger.info(this.logPrefix() + ' %j', this.commandsStatistics); + private logStatistics(): void { + logger.info(this.logPrefix() + ' %j', this.statistics); } - private displayInterval(): void { + private startDisplayInterval(): void { if (Configuration.getStatisticsDisplayInterval() > 0) { - setInterval(() => { - this.display(); + this.displayInterval = setInterval(() => { + this.logStatistics(); }, Configuration.getStatisticsDisplayInterval() * 1000); logger.info(this.logPrefix() + ' displayed every ' + Utils.secondsToHHMMSS(Configuration.getStatisticsDisplayInterval())); + } else { + logger.info(this.logPrefix() + ' display interval is set to ' + Configuration.getStatisticsDisplayInterval().toString() + '. Not displaying statistics'); } } @@ -124,29 +133,26 @@ export default class PerformanceStatistics { return Math.sqrt(totalGeometricDeviation / dataSet.length); } - private addPerformanceTimer(name: string, duration: number): void { + private addPerformanceStatistic(name: string, duration: number): void { // Rename entry name - const MAP_NAME = { - startATGTransaction: 'StartATGTransaction', - stopATGTransaction: 'StartATGTransaction' - }; + const MAP_NAME = {}; if (MAP_NAME[name]) { name = MAP_NAME[name] as string; } // Initialize command statistics - if (!this.commandsStatistics.commandsStatisticsData[name]) { - this.commandsStatistics.commandsStatisticsData[name] = {} as CommandStatisticsData; + if (!this.statistics.statisticsData[name]) { + this.statistics.statisticsData[name] = {} as StatisticsData; } // Update current statistics timers - this.commandsStatistics.commandsStatisticsData[name].countTimeMeasurement = this.commandsStatistics.commandsStatisticsData[name].countTimeMeasurement ? this.commandsStatistics.commandsStatisticsData[name].countTimeMeasurement + 1 : 1; - this.commandsStatistics.commandsStatisticsData[name].currentTimeMeasurement = duration; - this.commandsStatistics.commandsStatisticsData[name].minTimeMeasurement = this.commandsStatistics.commandsStatisticsData[name].minTimeMeasurement ? (this.commandsStatistics.commandsStatisticsData[name].minTimeMeasurement > duration ? duration : this.commandsStatistics.commandsStatisticsData[name].minTimeMeasurement) : duration; - this.commandsStatistics.commandsStatisticsData[name].maxTimeMeasurement = this.commandsStatistics.commandsStatisticsData[name].maxTimeMeasurement ? (this.commandsStatistics.commandsStatisticsData[name].maxTimeMeasurement < duration ? duration : this.commandsStatistics.commandsStatisticsData[name].maxTimeMeasurement) : duration; - this.commandsStatistics.commandsStatisticsData[name].totalTimeMeasurement = this.commandsStatistics.commandsStatisticsData[name].totalTimeMeasurement ? this.commandsStatistics.commandsStatisticsData[name].totalTimeMeasurement + duration : duration; - this.commandsStatistics.commandsStatisticsData[name].avgTimeMeasurement = this.commandsStatistics.commandsStatisticsData[name].totalTimeMeasurement / this.commandsStatistics.commandsStatisticsData[name].countTimeMeasurement; - Array.isArray(this.commandsStatistics.commandsStatisticsData[name].timeMeasurementSeries) ? this.commandsStatistics.commandsStatisticsData[name].timeMeasurementSeries.push(duration) : this.commandsStatistics.commandsStatisticsData[name].timeMeasurementSeries = new CircularArray(DEFAULT_CIRCULAR_ARRAY_SIZE, duration); - this.commandsStatistics.commandsStatisticsData[name].medTimeMeasurement = this.median(this.commandsStatistics.commandsStatisticsData[name].timeMeasurementSeries); - this.commandsStatistics.commandsStatisticsData[name].stdDevTimeMeasurement = this.stdDeviation(this.commandsStatistics.commandsStatisticsData[name].timeMeasurementSeries); + this.statistics.statisticsData[name].countTimeMeasurement = this.statistics.statisticsData[name].countTimeMeasurement ? this.statistics.statisticsData[name].countTimeMeasurement + 1 : 1; + this.statistics.statisticsData[name].currentTimeMeasurement = duration; + this.statistics.statisticsData[name].minTimeMeasurement = this.statistics.statisticsData[name].minTimeMeasurement ? (this.statistics.statisticsData[name].minTimeMeasurement > duration ? duration : this.statistics.statisticsData[name].minTimeMeasurement) : duration; + this.statistics.statisticsData[name].maxTimeMeasurement = this.statistics.statisticsData[name].maxTimeMeasurement ? (this.statistics.statisticsData[name].maxTimeMeasurement < duration ? duration : this.statistics.statisticsData[name].maxTimeMeasurement) : duration; + this.statistics.statisticsData[name].totalTimeMeasurement = this.statistics.statisticsData[name].totalTimeMeasurement ? this.statistics.statisticsData[name].totalTimeMeasurement + duration : duration; + this.statistics.statisticsData[name].avgTimeMeasurement = this.statistics.statisticsData[name].totalTimeMeasurement / this.statistics.statisticsData[name].countTimeMeasurement; + Array.isArray(this.statistics.statisticsData[name].timeMeasurementSeries) ? this.statistics.statisticsData[name].timeMeasurementSeries.push(duration) : this.statistics.statisticsData[name].timeMeasurementSeries = new CircularArray(DEFAULT_CIRCULAR_ARRAY_SIZE, duration); + this.statistics.statisticsData[name].medTimeMeasurement = this.median(this.statistics.statisticsData[name].timeMeasurementSeries); + this.statistics.statisticsData[name].stdDevTimeMeasurement = this.stdDeviation(this.statistics.statisticsData[name].timeMeasurementSeries); } private logPrefix(): string { -- 2.34.1