Add supervision connection URL support setup through OCPP parameter
authorJérôme Benoit <jerome.benoit@sap.com>
Wed, 24 Nov 2021 11:07:04 +0000 (12:07 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Wed, 24 Nov 2021 11:07:04 +0000 (12:07 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
README.md
src/charging-station/ChargingStation.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/types/ChargingStationTemplate.ts
src/types/ocpp/1.6/Configuration.ts
src/types/ocpp/Configuration.ts

index 695690ad9173644d0d111de90f278a4371d8013a..47ae68cd587afe1c7ef66185d24ab0fce6bb4d31 100644 (file)
--- a/README.md
+++ b/README.md
@@ -86,6 +86,8 @@ Key | Value(s) | Default Value | Value type | Description
 supervisionURL | | '' | string | connection URI to OCPP-J server
 supervisionUser | | '' | string | basic HTTP authentication user to OCPP-J server
 supervisionPassword | | '' | string | basic HTTP authentication password to OCPP-J server
+supervisionURLOCPPConfiguration | | '' | boolean | Allow supervision URL configuration via a vendor OCPP parameter key
+supervisionURLOCPPKey | | '' | string | The vendor string that will be used as a vendor OCPP parameter key to set the supervision URL
 ocppVersion | 1.6 | 1.6 | string | OCPP version 
 ocppProtocol | json | json | string | OCPP protocol
 wsOptions | | {} | ClientOptions & ClientRequestArgs | [ws](https://github.com/websockets/ws) and node.js [http](https://nodejs.org/api/http.html) clients options intersection
index 92e4a912b652d2a508c5ebb796e3aa1257b46040..db385612bf85615090fd7493fee30838f60068f5 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';
@@ -53,7 +53,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 +76,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} |`);
   }
@@ -361,8 +365,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 +435,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;
@@ -486,7 +497,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,6 +551,7 @@ export default class ChargingStation {
         this.initializeConnectorStatus(connectorId);
       }
     }
+    this.wsConfiguredConnectionUrl = new URL(this.getConfiguredSupervisionURL().href + '/' + this.stationInfo.chargingStationId);
     switch (this.getOCPPVersion()) {
       case OCPPVersion.VERSION_16:
         this.ocppIncomingRequestService = new OCPP16IncomingRequestService(this);
@@ -566,10 +577,13 @@ export default class ChargingStation {
   }
 
   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);
     }
@@ -910,8 +924,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()) {
index 3e63c84ac90e6c128f614a20225adaaf148b30d0..d429e04569a5100bcf92095ff018c8f596ee434a 100644 (file)
@@ -49,7 +49,7 @@ export default class OCPP16ResponseService extends OCPPResponseService {
   private handleResponseBootNotification(payload: OCPP16BootNotificationResponse): void {
     if (payload.status === OCPP16RegistrationStatus.ACCEPTED) {
       this.chargingStation.addConfigurationKey(OCPP16StandardParametersKey.HeartBeatInterval, payload.interval.toString());
-      this.chargingStation.addConfigurationKey(OCPP16StandardParametersKey.HeartbeatInterval, payload.interval.toString(), false, false);
+      this.chargingStation.addConfigurationKey(OCPP16StandardParametersKey.HeartbeatInterval, payload.interval.toString(), { visible: false });
       this.chargingStation.heartbeatSetInterval ? this.chargingStation.restartHeartbeat() : this.chargingStation.startHeartbeat();
     } else if (payload.status === OCPP16RegistrationStatus.PENDING) {
       logger.info(this.chargingStation.logPrefix() + ' Charging station in pending state on the central server');
index 74848226683c9bf0d4377c1bbf7f1b2aa7addd21..0286663bc486954cd99ee4f5a002ccf7486f548d 100644 (file)
@@ -36,6 +36,8 @@ export interface AutomaticTransactionGenerator {
 
 export default interface ChargingStationTemplate {
   supervisionURL?: string;
+  supervisionURLOCPPConfiguration?: boolean;
+  supervisionURLOCPPKey?: string;
   supervisionUser?: string;
   supervisionPassword?: string;
   ocppVersion?: OCPPVersion;
index 315c9646df94e82a7fc63c8d709af4634f2426b7..474c691ffee14c787681b9b8e8fefde48aff7dbb 100644 (file)
@@ -53,3 +53,7 @@ export enum OCPP16StandardParametersKey {
   ConnectorSwitch3to1PhaseSupported = 'ConnectorSwitch3to1PhaseSupported',
   MaxChargingProfilesInstalled = 'MaxChargingProfilesInstalled'
 }
+
+export enum OCPP16VendorDefaultParametersKey {
+  ConnectionUrl = 'ConnectionUrl'
+}
index 6559f815a364048cb2b2c4f4c1ebf2adcd82c3b5..b55c212196a53fe09f71db17c69fbd55007dca4c 100644 (file)
@@ -1,4 +1,4 @@
-import { OCPP16StandardParametersKey, OCPP16SupportedFeatureProfiles } from './1.6/Configuration';
+import { OCPP16StandardParametersKey, OCPP16SupportedFeatureProfiles, OCPP16VendorDefaultParametersKey } from './1.6/Configuration';
 
 export type StandardParametersKey = OCPP16StandardParametersKey;
 
@@ -6,6 +6,12 @@ export const StandardParametersKey = {
   ...OCPP16StandardParametersKey
 };
 
+export type VendorDefaultParametersKey = OCPP16VendorDefaultParametersKey;
+
+export const VendorDefaultParametersKey = {
+  ...OCPP16VendorDefaultParametersKey
+};
+
 export type SupportedFeatureProfiles = OCPP16SupportedFeatureProfiles;
 
 export const SupportedFeatureProfiles = {