From: Jérôme Benoit Date: Thu, 2 Apr 2026 11:58:12 +0000 (+0200) Subject: refactor(ocpp): consolidate variable access in auth adapter X-Git-Tag: ocpp-server@v4.2.0~4 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=52c94b9a6381f03bd48440323a30c770693ed6b7;p=e-mobility-charging-stations-simulator.git refactor(ocpp): consolidate variable access in auth adapter Remove getVariableValue/getDefaultVariableValue wrappers from OCPP20AuthAdapter in favor of OCPP20ServiceUtils.readVariableAs* helpers. Fixes LocalAuthListCtrlr.Enabled default discrepancy between adapter (true) and registry (false). Add readVariableAsString helper for consistent string variable access. --- diff --git a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts index 6951bc61..d5c30e8b 100644 --- a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts @@ -2242,12 +2242,11 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { } } - const priorityValue = - OCPP20ServiceUtils.readVariableValue( - chargingStation, - OCPP20ComponentName.OCPPCommCtrlr, - OCPP20RequiredVariableName.NetworkConfigurationPriority - ) ?? '' + const priorityValue = OCPP20ServiceUtils.readVariableAsString( + chargingStation, + OCPP20ComponentName.OCPPCommCtrlr, + OCPP20RequiredVariableName.NetworkConfigurationPriority + ) if (priorityValue.length > 0) { const priorities = priorityValue.split(',').map(Number) if (!priorities.includes(commandPayload.configurationSlot)) { diff --git a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts index 6a913910..9922cb63 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts @@ -506,6 +506,18 @@ export class OCPP20ServiceUtils { return defaultValue } + public static readVariableAsString ( + chargingStation: ChargingStation, + componentName: string, + variableName: string, + defaultValue = '' + ): string { + return ( + OCPP20ServiceUtils.readVariableValue(chargingStation, componentName, variableName) ?? + defaultValue + ) + } + public static readVariableValue ( chargingStation: ChargingStation, componentName: string, diff --git a/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts b/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts index 0938fcb9..937d3fcd 100644 --- a/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts +++ b/src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts @@ -23,7 +23,7 @@ import { OCPP20RequiredVariableName, OCPPVersion, } from '../../../../types/index.js' -import { convertToBoolean, logger, truncateId } from '../../../../utils/index.js' +import { logger, truncateId } from '../../../../utils/index.js' import { AuthContext, AuthenticationMethod, @@ -366,20 +366,13 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter { */ isRemoteAvailable (): boolean { try { - // Check if station supports remote authorization via variables - // OCPP 2.0 uses variables instead of configuration keys - - // Check if station is online and can communicate const isOnline = this.chargingStation.inAcceptedState() - - // Check AuthorizeRemoteStart variable - const remoteStartValue = this.getVariableValue( + const remoteStartEnabled = OCPP20ServiceUtils.readVariableAsBoolean( + this.chargingStation, OCPP20ComponentName.AuthCtrlr, - OCPP20RequiredVariableName.AuthorizeRemoteStart + OCPP20RequiredVariableName.AuthorizeRemoteStart, + true ) - const remoteStartEnabled = - remoteStartValue != null ? convertToBoolean(remoteStartValue) : true - return isOnline && remoteStartEnabled } catch (error) { logger.warn( @@ -459,61 +452,18 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter { } } - /** - * Get default variable value based on OCPP 2.0.1 specification - * @param component - OCPP component name (e.g., OCPP20ComponentName.AuthCtrlr) - * @param variable - OCPP variable name (e.g., OCPP20RequiredVariableName.AuthorizeRemoteStart) - * @param useFallback - Whether to return fallback values when variable is not configured - * @returns Default value according to OCPP 2.0.1 spec, or undefined if no default exists - */ - private getDefaultVariableValue ( - component: string, - variable: string, - useFallback: boolean - ): string | undefined { - if (!useFallback) { - return undefined - } - - // Default values from OCPP 2.0.1 specification and variable registry - if (component === (OCPP20ComponentName.AuthCtrlr as string)) { - switch (variable) { - case OCPP20RequiredVariableName.AuthorizeRemoteStart as string: - return 'true' - case OCPP20RequiredVariableName.Enabled as string: - return 'true' - case OCPP20RequiredVariableName.LocalAuthorizationOffline as string: - return 'true' - case OCPP20RequiredVariableName.LocalPreAuthorization as string: - return 'false' - default: - return undefined - } - } - - if (component === (OCPP20ComponentName.LocalAuthListCtrlr as string)) { - switch (variable) { - case OCPP20RequiredVariableName.Enabled as string: - return 'true' - default: - return undefined - } - } - - return undefined - } - /** * Check if offline authorization is allowed * @returns True if offline authorization is enabled */ private getOfflineAuthorizationConfig (): boolean { try { - const value = this.getVariableValue( + return OCPP20ServiceUtils.readVariableAsBoolean( + this.chargingStation, OCPP20ComponentName.AuthCtrlr, - OCPP20RequiredVariableName.LocalAuthorizationOffline + OCPP20RequiredVariableName.LocalAuthorizationOffline, + true ) - return value != null ? convertToBoolean(value) : true } catch (error) { logger.warn( `${this.chargingStation.logPrefix()} ${moduleName}.getOfflineAuthorizationConfig: Error getting offline authorization config`, @@ -522,35 +472,4 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter { return false } } - - /** - * Get variable value from OCPP 2.0 variable system - * @param component - OCPP component name (e.g., OCPP20ComponentName.AuthCtrlr) - * @param variable - OCPP variable name (e.g., OCPP20RequiredVariableName.AuthorizeRemoteStart) - * @param useDefaultFallback - If true, use OCPP 2.0.1 spec default values when variable is not found - * @returns Promise resolving to variable value as string, or undefined if not available - */ - private getVariableValue ( - component: string, - variable: string, - useDefaultFallback = true - ): string | undefined { - try { - const value = OCPP20ServiceUtils.readVariableValue(this.chargingStation, component, variable) - if (value != null) { - return value - } - - logger.debug( - `${this.chargingStation.logPrefix()} ${moduleName}.getVariableValue: Variable ${component}.${variable} not available` - ) - return this.getDefaultVariableValue(component, variable, useDefaultFallback) - } catch (error) { - logger.warn( - `${this.chargingStation.logPrefix()} ${moduleName}.getVariableValue: Error getting variable ${component}.${variable}`, - error - ) - return this.getDefaultVariableValue(component, variable, useDefaultFallback) - } - } } diff --git a/tests/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.test.ts b/tests/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.test.ts index 6d4de7d3..985b0315 100644 --- a/tests/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.test.ts +++ b/tests/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.test.ts @@ -7,6 +7,7 @@ import { afterEach, beforeEach, describe, it, mock } from 'node:test' import type { ChargingStation } from '../../../../../src/charging-station/index.js' +import { OCPP20ServiceUtils } from '../../../../../src/charging-station/ocpp/2.0/OCPP20ServiceUtils.js' import { OCPP20AuthAdapter } from '../../../../../src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.js' import { type AuthConfiguration, @@ -239,11 +240,7 @@ await describe('OCPP20AuthAdapter', async () => { await describe('isRemoteAvailable', async () => { await it('should return true when station is online and remote start enabled', t => { - t.mock.method( - adapter as unknown as { getVariableValue: () => string | undefined }, - 'getVariableValue', - () => 'true' - ) + t.mock.method(OCPP20ServiceUtils, 'readVariableAsBoolean', () => true) const isAvailable = adapter.isRemoteAvailable() assert.strictEqual(isAvailable, true) @@ -251,11 +248,7 @@ await describe('OCPP20AuthAdapter', async () => { await it('should return false when station is offline', t => { mockStation.inAcceptedState = () => false - t.mock.method( - adapter as unknown as { getVariableValue: () => string | undefined }, - 'getVariableValue', - () => 'true' - ) + t.mock.method(OCPP20ServiceUtils, 'readVariableAsBoolean', () => true) const isAvailable = adapter.isRemoteAvailable() assert.strictEqual(isAvailable, false) @@ -388,25 +381,21 @@ await describe('OCPP20AuthAdapter', async () => { }) await describe('C15 - Offline detection', async () => { - await it('should detect station is offline when not in accepted state', () => { - // Given: Station is offline (not in accepted state) + await it('should detect station is offline when not in accepted state', t => { offlineMockChargingStation.inAcceptedState = () => false + t.mock.method(OCPP20ServiceUtils, 'readVariableAsBoolean', () => true) - // When: Check if remote authorization is available const isAvailable = offlineAdapter.isRemoteAvailable() - // Then: Remote should not be available assert.strictEqual(isAvailable, false) }) - await it('should detect station is online when in accepted state', () => { - // Given: Station is online (in accepted state) + await it('should detect station is online when in accepted state', t => { offlineMockChargingStation.inAcceptedState = () => true + t.mock.method(OCPP20ServiceUtils, 'readVariableAsBoolean', () => true) - // When: Check if remote authorization is available const isAvailable = offlineAdapter.isRemoteAvailable() - // Then: Remote should be available (assuming AuthorizeRemoteStart is enabled by default) assert.strictEqual(isAvailable, true) }) @@ -417,14 +406,12 @@ await describe('OCPP20AuthAdapter', async () => { }) await describe('C15 - Remote availability check', async () => { - await it('should return false when offline even with valid configuration', () => { - // Given: Station is offline + await it('should return false when offline even with valid configuration', t => { offlineMockChargingStation.inAcceptedState = () => false + t.mock.method(OCPP20ServiceUtils, 'readVariableAsBoolean', () => true) - // When: Check remote availability const isAvailable = offlineAdapter.isRemoteAvailable() - // Then: Should not be available assert.strictEqual(isAvailable, false) })