Add support for performance statistics for all requests sent to the OCPP
authorJérôme Benoit <jerome.benoit@sap.com>
Fri, 13 Aug 2021 18:20:51 +0000 (20:20 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Fri, 13 Aug 2021 18:20:51 +0000 (20:20 +0200)
server.

The distinction between request and response is not done.

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/AutomaticTransactionGenerator.ts
src/charging-station/Bootstrap.ts
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/1.6/OCPP16RequestService.ts
src/charging-station/ocpp/OCPPRequestService.ts
src/types/Statistics.ts [moved from src/types/CommandStatistics.ts with 59% similarity]
src/utils/PerformanceStatistics.ts

index f8183b8b41470a9135609b5cc08f23d63486be1d..73412c161e00eb3117b5a14ad1a49d464b6be1fc 100644 (file)
@@ -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<StartTransactionResponse | AuthorizeResponse>;
-        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<StopTransactionResponse>;
-            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<StartTransactionResponse | AuthorizeResponse> {
-    if (self.chargingStation.hasAuthorizedTags()) {
-      const tagId = self.chargingStation.getRandomTagId();
-      if (self.chargingStation.getAutomaticTransactionGeneratorRequireAuthorize()) {
+  private async startTransaction(connectorId: number): Promise<StartTransactionResponse | AuthorizeResponse> {
+    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<StopTransactionResponse> {
-    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<StopTransactionResponse> {
+    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 {
index a9e6d1ef70d1491ee4c1d68cc33092a95c4db117..f5c0de9f6b722a50aca6ab0a8c85315e6d3455ce 100644 (file)
@@ -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;
index 2626a23353c0815cea003ea59787c7cc81b5b662..79c9e65afc219c2672f6fc49a964f0333b618a0e 100644 (file)
@@ -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<void> => {
-        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);
index 2c17b334ff1759296e09e539ab39b563ceb635fd..dcd38a817a1bbb9465a87fe62868e393186b1579 100644 (file)
@@ -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<void> {
+  public async sendMeterValues(connectorId: number, transactionId: number, interval: number, debug = false): Promise<void> {
     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);
     }
   }
 
index 0d8f8cf66134b205b3f7c6a90c27958c46016e5d..6c24665ac1042b5591184f69627e83b3b265f1f1 100644 (file)
@@ -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, unknown> | string, requestPayload: Record<string, unknown>): Promise<void> {
         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<AuthorizeResponse>;
   public abstract sendStartTransaction(connectorId: number, idTag?: string): Promise<StartTransactionResponse>;
   public abstract sendStopTransaction(transactionId: number, meterStop: number, idTag?: string, reason?: StopTransactionReason): Promise<StopTransactionResponse>;
-  public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number, self: OCPPRequestService): Promise<void>;
+  public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number): Promise<void>;
   public abstract sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: MeterValue): Promise<void>;
   public abstract sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: MeterValue): Promise<void>;
   public abstract sendDiagnosticsStatusNotification(diagnosticsStatus: DiagnosticsStatus): Promise<void>;
similarity index 59%
rename from src/types/CommandStatistics.ts
rename to src/types/Statistics.ts
index 2b84a5e4abca4e368deaf332ce31e18a85db059c..7c099bc96eaf29389f66650baa0219360195df19 100644 (file)
@@ -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<string, CommandStatisticsData>;
+  statisticsData: Record<string, StatisticsData>;
 }
index c2d1527a1a7adcd1803c03e1e2fa18d64e1be860..e7cd58e8b9e17a39d1546c37c2a9d39d447d4c22 100644 (file)
@@ -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<number>(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<number>(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 {