Refine autoRegister CS template tunable handling
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStation.ts
index 92e4a912b652d2a508c5ebb796e3aa1257b46040..00cfce9f96f00014a251284a02494abe9be7d5bc 100644 (file)
@@ -4,7 +4,7 @@ import { AvailabilityType, BootNotificationRequest, CachedRequest, IncomingReque
 import { BootNotificationResponse, RegistrationStatus } from '../types/ocpp/Responses';
 import ChargingStationConfiguration, { ConfigurationKey } from '../types/ChargingStationConfiguration';
 import ChargingStationTemplate, { CurrentType, PowerUnits, Voltage } from '../types/ChargingStationTemplate';
-import { ConnectorPhaseRotation, StandardParametersKey, SupportedFeatureProfiles } from '../types/ocpp/Configuration';
+import { ConnectorPhaseRotation, StandardParametersKey, SupportedFeatureProfiles, VendorDefaultParametersKey } from '../types/ocpp/Configuration';
 import { ConnectorStatus, SampledValueTemplate } from '../types/Connectors';
 import { MeterValueMeasurand, MeterValuePhase } from '../types/ocpp/MeterValues';
 import { WSError, WebSocketCloseEventStatusCode } from '../types/WebSocket';
@@ -14,6 +14,7 @@ import AutomaticTransactionGenerator from './AutomaticTransactionGenerator';
 import { ChargePointStatus } from '../types/ocpp/ChargePointStatus';
 import { ChargingProfile } from '../types/ocpp/ChargingProfile';
 import ChargingStationInfo from '../types/ChargingStationInfo';
+import { ChargingStationWorkerMessageEvents } from '../types/ChargingStationWorker';
 import { ClientRequestArgs } from 'http';
 import Configuration from '../utils/Configuration';
 import Constants from '../utils/Constants';
@@ -23,7 +24,7 @@ import { MessageType } from '../types/ocpp/MessageType';
 import OCPP16IncomingRequestService from './ocpp/1.6/OCPP16IncomingRequestService';
 import OCPP16RequestService from './ocpp/1.6/OCPP16RequestService';
 import OCPP16ResponseService from './ocpp/1.6/OCPP16ResponseService';
-import OCPPError from './ocpp/OCPPError';
+import OCPPError from '../exception/OCPPError';
 import OCPPIncomingRequestService from './ocpp/OCPPIncomingRequestService';
 import OCPPRequestService from './ocpp/OCPPRequestService';
 import { OCPPVersion } from '../types/ocpp/OCPPVersion';
@@ -34,6 +35,7 @@ import Utils from '../utils/Utils';
 import crypto from 'crypto';
 import fs from 'fs';
 import logger from '../utils/Logger';
+import { parentPort } from 'worker_threads';
 import path from 'path';
 
 export default class ChargingStation {
@@ -53,7 +55,7 @@ export default class ChargingStation {
   private connectorsConfigurationHash!: string;
   private ocppIncomingRequestService!: OCPPIncomingRequestService;
   private readonly messageBuffer: Set<string>;
-  private wsConnectionUrl!: URL;
+  private wsConfiguredConnectionUrl!: URL;
   private wsConnectionRestarted: boolean;
   private stopped: boolean;
   private autoReconnectRetryCount: number;
@@ -76,6 +78,10 @@ export default class ChargingStation {
     this.authorizedTags = this.getAuthorizedTags();
   }
 
+  get wsConnectionUrl(): URL {
+    return this.getSupervisionUrlOcppConfiguration() ? new URL(this.getConfigurationKey(this.stationInfo.supervisionUrlOcppKey ?? VendorDefaultParametersKey.ConnectionUrl).value + '/' + this.stationInfo.chargingStationId) : this.wsConfiguredConnectionUrl;
+  }
+
   public logPrefix(): string {
     return Utils.logPrefix(` ${this.stationInfo.chargingStationId} |`);
   }
@@ -111,11 +117,11 @@ export default class ChargingStation {
   }
 
   public isWebSocketConnectionOpened(): boolean {
-    return this.wsConnection?.readyState === OPEN;
+    return this?.wsConnection?.readyState === OPEN;
   }
 
   public isRegistered(): boolean {
-    return this.bootNotificationResponse?.status === RegistrationStatus.ACCEPTED;
+    return this?.bootNotificationResponse?.status === RegistrationStatus.ACCEPTED;
   }
 
   public isChargingStationAvailable(): boolean {
@@ -331,6 +337,7 @@ export default class ChargingStation {
     this.wsConnection.on('ping', this.onPing.bind(this));
     // Handle WebSocket pong
     this.wsConnection.on('pong', this.onPong.bind(this));
+    parentPort.postMessage({ id: ChargingStationWorkerMessageEvents.STARTED, data: { id: this.stationInfo.chargingStationId } });
   }
 
   public async stop(reason: StopTransactionReason = StopTransactionReason.NONE): Promise<void> {
@@ -349,6 +356,7 @@ export default class ChargingStation {
       this.performanceStatistics.stop();
     }
     this.bootNotificationResponse = null;
+    parentPort.postMessage({ id: ChargingStationWorkerMessageEvents.STOPPED, data: { id: this.stationInfo.chargingStationId } });
     this.stopped = true;
   }
 
@@ -361,8 +369,11 @@ export default class ChargingStation {
     });
   }
 
-  public addConfigurationKey(key: string | StandardParametersKey, value: string, readonly = false, visible = true, reboot = false): void {
+  public addConfigurationKey(key: string | StandardParametersKey, value: string, options: { readonly?: boolean, visible?: boolean, reboot?: boolean } = { readonly: false, visible: true, reboot: false }): void {
     const keyFound = this.getConfigurationKey(key);
+    const readonly = options.readonly;
+    const visible = options.visible;
+    const reboot = options.reboot;
     if (!keyFound) {
       this.configuration.configurationKey.push({
         key,
@@ -428,6 +439,10 @@ export default class ChargingStation {
     }
   }
 
+  private getSupervisionUrlOcppConfiguration(): boolean {
+    return this.stationInfo.supervisionUrlOcppConfiguration ?? false;
+  }
+
   private getChargingStationId(stationTemplate: ChargingStationTemplate): string {
     // In case of multiple instances: add instance index to charging station id
     const instanceIndex = process.env.CF_INSTANCE_INDEX ?? 0;
@@ -466,7 +481,7 @@ export default class ChargingStation {
     return stationInfo;
   }
 
-  private getOCPPVersion(): OCPPVersion {
+  private getOcppVersion(): OCPPVersion {
     return this.stationInfo.ocppVersion ? this.stationInfo.ocppVersion : OCPPVersion.VERSION_16;
   }
 
@@ -486,7 +501,6 @@ export default class ChargingStation {
       ...!Utils.isUndefined(this.stationInfo.chargeBoxSerialNumberPrefix) && { chargeBoxSerialNumber: this.stationInfo.chargeBoxSerialNumberPrefix },
       ...!Utils.isUndefined(this.stationInfo.firmwareVersion) && { firmwareVersion: this.stationInfo.firmwareVersion },
     };
-    this.wsConnectionUrl = new URL(this.getSupervisionURL().href + '/' + this.stationInfo.chargingStationId);
     // Build connectors if needed
     const maxConnectors = this.getMaxNumberOfConnectors();
     if (maxConnectors <= 0) {
@@ -541,17 +555,18 @@ export default class ChargingStation {
         this.initializeConnectorStatus(connectorId);
       }
     }
-    switch (this.getOCPPVersion()) {
+    this.wsConfiguredConnectionUrl = new URL(this.getConfiguredSupervisionUrl().href + '/' + this.stationInfo.chargingStationId);
+    switch (this.getOcppVersion()) {
       case OCPPVersion.VERSION_16:
         this.ocppIncomingRequestService = new OCPP16IncomingRequestService(this);
         this.ocppRequestService = new OCPP16RequestService(this, new OCPP16ResponseService(this));
         break;
       default:
-        this.handleUnsupportedVersion(this.getOCPPVersion());
+        this.handleUnsupportedVersion(this.getOcppVersion());
         break;
     }
     // OCPP parameters
-    this.initOCPPParameters();
+    this.initOcppParameters();
     if (this.stationInfo.autoRegister) {
       this.bootNotificationResponse = {
         currentTime: new Date().toISOString(),
@@ -565,11 +580,14 @@ export default class ChargingStation {
     }
   }
 
-  private initOCPPParameters(): void {
+  private initOcppParameters(): void {
+    if (this.getSupervisionUrlOcppConfiguration() && !this.getConfigurationKey(this.stationInfo.supervisionUrlOcppKey ?? VendorDefaultParametersKey.ConnectionUrl)) {
+      this.addConfigurationKey(VendorDefaultParametersKey.ConnectionUrl, this.getConfiguredSupervisionUrl().href, { reboot: true });
+    }
     if (!this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles)) {
       this.addConfigurationKey(StandardParametersKey.SupportedFeatureProfiles, `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.Local_Auth_List_Management},${SupportedFeatureProfiles.Smart_Charging}`);
     }
-    this.addConfigurationKey(StandardParametersKey.NumberOfConnectors, this.getNumberOfConnectors().toString(), true);
+    this.addConfigurationKey(StandardParametersKey.NumberOfConnectors, this.getNumberOfConnectors().toString(), { readonly: true });
     if (!this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData)) {
       this.addConfigurationKey(StandardParametersKey.MeterValuesSampledData, MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER);
     }
@@ -616,6 +634,10 @@ export default class ChargingStation {
         }
       } while (!this.isRegistered() && (registrationRetryCount <= this.getRegistrationMaxRetries() || this.getRegistrationMaxRetries() === -1));
     }
+    if (this.isRegistered() && this.stationInfo.autoRegister) {
+      await this.ocppRequestService.sendBootNotification(this.bootNotificationRequest.chargePointModel,
+        this.bootNotificationRequest.chargePointVendor, this.bootNotificationRequest.chargeBoxSerialNumber, this.bootNotificationRequest.firmwareVersion);
+    }
     if (this.isRegistered()) {
       await this.startMessageSequence();
       this.stopped && (this.stopped = false);
@@ -811,7 +833,7 @@ export default class ChargingStation {
   }
 
   private getMaxNumberOfConnectors(): number {
-    let maxConnectors = 0;
+    let maxConnectors: number;
     if (!Utils.isEmptyArray(this.stationInfo.numberOfConnectors)) {
       const numberOfConnectors = this.stationInfo.numberOfConnectors as number[];
       // Distribute evenly the number of connectors
@@ -910,8 +932,8 @@ export default class ChargingStation {
     }
   }
 
-  private getSupervisionURL(): URL {
-    const supervisionUrls = Utils.cloneObject<string | string[]>(this.stationInfo.supervisionURL ? this.stationInfo.supervisionURL : Configuration.getSupervisionURLs());
+  private getConfiguredSupervisionUrl(): URL {
+    const supervisionUrls = Utils.cloneObject<string | string[]>(this.stationInfo.supervisionUrl ?? Configuration.getSupervisionUrls());
     let indexUrl = 0;
     if (!Utils.isEmptyArray(supervisionUrls)) {
       if (Configuration.getDistributeStationsToTenantsEqually()) {
@@ -953,12 +975,12 @@ export default class ChargingStation {
       this.wsConnection.close();
     }
     let protocol: string;
-    switch (this.getOCPPVersion()) {
+    switch (this.getOcppVersion()) {
       case OCPPVersion.VERSION_16:
         protocol = 'ocpp' + OCPPVersion.VERSION_16;
         break;
       default:
-        this.handleUnsupportedVersion(this.getOCPPVersion());
+        this.handleUnsupportedVersion(this.getOcppVersion());
         break;
     }
     this.wsConnection = new WebSocket(this.wsConnectionUrl, protocol, options);