]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
refactor: audit-driven cleanup — OCPP conformance fixes, physics, harmonization ...
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Mon, 6 Jul 2026 16:09:30 +0000 (18:09 +0200)
committerGitHub <noreply@github.com>
Mon, 6 Jul 2026 16:09:30 +0000 (18:09 +0200)
* fix(ocpp/1.6): correct GetDiagnostics log directory resolution

`handleRequestGetDiagnostics` called `resolve()` with the comma operator
by accident:

  resolve((fileURLToPath(import.meta.url), '../', dirname(logFile)))

The extra parentheses wrap a comma-operator expression that evaluates to
`dirname(logFile)` only, so `readdirSync` was invoked with a
directory resolved from CWD instead of relative to the module URL. When
the process CWD is not the project root (e.g. a systemd service, a docker
container that mounts logs elsewhere, a background worker with a chdir'd
parent), the log-file discovery scanned the wrong directory and produced
an incomplete diagnostics archive.

Align with the sibling call at line 1183 which already uses the intended
pattern:

  resolve(dirname(fileURLToPath(import.meta.url)), '../', diagnosticsArchive)

Both call sites now resolve paths relative to the module's parent
directory. Gates pass (typecheck / lint).

* fix(physics): use sqrt(3) for line-to-line voltage derivation

Two sites derived the line-to-line nominal voltage from Math.sqrt(numberOfPhases) * V_LN:

- src/charging-station/ocpp/OCPPServiceUtils.ts:1255
- src/charging-station/meter-values/CoherentMeterValueBuilder.ts:282

The ratio V_LL / V_LN in a balanced 3-phase Y system is a fixed sqrt(3),
which comes from the 30-degree phase separation between line and neutral
voltages (V_LL = 2 * V_LN * sin(60 deg) = sqrt(3) * V_LN). It is NOT a
function of the phase count.

Both call sites are currently reachable only when numberOfPhases === 3
(one guarded explicitly at CoherentMeterValueBuilder.ts:281; the other
implicitly via the phaseLineToLineVoltageMeterValues + phase-rotation
plumbing that only supports 3-phase). Numerically the emitted value is
unchanged, but the formula was a trap: any future relaxation of the
3-phase guard would silently emit sqrt(2) * V_LN at N=2, which is
physically meaningless.

Replace both call sites with Math.sqrt(3) and add a code comment stating
the physics derivation so the constant cannot be re-parameterized on the
phase count.

Gates pass (typecheck / test, 2955 pass / 0 fail).

* refactor(auth): standardize on OCPP 2.0.1 terminology in auth subsystem

The recently-added auth subsystem used bare 'OCPP 2.0' throughout its
comments and JSDoc while the rest of the codebase (README, AGENTS.md,
all other source files) uses 'OCPP 2.0.1' as the canonical spec version
and 'OCPP 2.0.x' as the family. The auth subsystem also used non-existent
forms 'OCPP 2.0+' and 'OCPP 2.0/2.1' that misrepresented the supported
spec range: OCPP 2.1 is not implemented.

Bulk-replace across src/charging-station/ocpp/auth/:
- 'OCPP 2.0+'   -> 'OCPP 2.0.1'
- 'OCPP 2.0/2.1' -> 'OCPP 2.0.1'
- bare 'OCPP 2.0' -> 'OCPP 2.0.1'  (negative lookahead on '.digit' so
  existing 'OCPP 2.0.1' and 'OCPP 2.0.x' occurrences are preserved)

65 occurrences updated across 5 files. Files touched:
- adapters/OCPP20AuthAdapter.ts   (31)
- types/AuthTypes.ts              (21)
- strategies/CertificateAuthStrategy.ts (7)
- utils/AuthValidators.ts         (5)
- strategies/RemoteAuthStrategy.ts (1)

Zero code behavior change; comment/JSDoc content only.
Gates pass (typecheck / lint).

* refactor(ocpp/1.6): harmonize 'Central System' capitalization for logs and docs

Two mismatches surfaced against OCPP 2.0 counterparts:

1. `csmsName` field values in OCPP16IncomingRequestService.ts:176 and
   OCPP16ResponseService.ts:129 held the lowercase two-word form
   'central system'. The abstract base classes interpolate this field
   into runtime error messages, producing 'not registered on the central
   system' for OCPP 1.6 versus 'not registered on the CSMS' for OCPP
   2.0. Two spec-correct forms are involved (OCPP 1.6 uses 'Central
   System'; OCPP 2.0.1 uses 'CSMS' for 'Charging Station Management
   System'), so keep the spec-correct term per version but capitalize
   the 1.6 value to match OCPP 1.6 spec convention.

2. JSDoc prose across OCPP 1.6 files ('sends X to the central system',
   'response from the central system', etc.) mixed the lowercase form
   with the capitalized 'Central System (CS)' used in the module-level
   docstring of OCPP16IncomingRequestService.ts. Normalize to the
   capitalized form throughout src/charging-station/ocpp/1.6/.

Zero code behavior change; only logged strings and comment prose shift
casing. Gates pass (typecheck / lint).

* refactor: apply numeric-literal underscore separators to 5-digit-plus constants

Numeric-literal style was inconsistent: OCPP20Constants.ts uses
underscore separators for readability (e.g. `30_000`, `5_000`),
while src/utils/Constants.ts and other constants files used bare
literals (`60000`, `30000`, `1048576`).

Apply underscore separators uniformly to 5+ digit numeric literals
across the constants files, matching the OCPP20Constants.ts precedent:

- src/utils/Constants.ts: `60000` -> `60_000` (6 sites),
  `30000` -> `30_000`, `281474976710655` -> `281_474_976_710_655`,
  `2147483647` -> `2_147_483_647`.
- src/charging-station/ocpp/OCPPConstants.ts:
  `OCPP_WEBSOCKET_TIMEOUT_MS = 60000` -> `60_000`.
- src/charging-station/ui-server/UIServerSecurity.ts:
  `DEFAULT_MAX_PAYLOAD_SIZE_BYTES = 1048576` -> `1_048_576`,
  `DEFAULT_RATE_WINDOW_MS = 60000` -> `60_000`,
  `DEFAULT_MAX_TRACKED_IPS = 10000` -> `10_000`.

4-digit values (1000, 3600, 5000, 8080) are intentionally left unchanged;
they include port literals (`DEFAULT_UI_SERVER_PORT = 8080`) where the
underscore form is unusual. AGENTS.md numeric-literal style is a
readability convention rather than an enforced rule; applying it only
above the 5-digit threshold matches the existing OCPP20Constants.ts
pattern.

Zero code behavior change. Gates pass (typecheck / lint / test, 2955
pass / 0 fail).

* chore: expose WILDCARD_HOSTS + document Authorize in OCPP 2.0.x README table

Two low-risk harmonization findings from the from-zero audit.

1. README OCPP 2.0.x commands table missing 'Authorize' (Lane 5 M2)
   The 'Authorize' command is fully implemented for OCPP 2.0.x:
   - OCPP20RequestCommand.AUTHORIZE registered
   - OCPP20RequestService.ts / OCPP20ResponseService.ts handlers wired
   - OCPP20ServiceUtils.ts sending helper
   but the README '#### C. Authorization' section listed only 'ClearCache'.
   Add ':white_check_mark: Authorize' alongside 'ClearCache' so the docs
   match the actual implementation surface.

2. Extract WILDCARD_HOSTS set (Lane 4 M20)
   AbstractUIServer.ts:1325 inlined the three literals '', '0.0.0.0', '::'
   as a triple '===' chain to detect wildcard host configuration, while
   UIServerAccessPolicy.ts:23 already held the same three values in a
   module-scoped 'WILDCARD_HOSTS' set. Two independent definitions of
   the same domain concept.
   Export WILDCARD_HOSTS (typed 'ReadonlySet<string>') from
   UIServerAccessPolicy.ts, import it in AbstractUIServer.ts, and
   replace the inline OR chain with 'WILDCARD_HOSTS.has(configuredHost)'.

Zero behavior change. Gates pass (typecheck / lint).

* fix(ocpp/1.6): return Accepted for DataTransfer with matching vendor and messageId

Per OCPP 1.6 §4.3, the DataTransfer response statuses are:
- `UnknownVendorId` when the vendorId is not recognized
- `UnknownMessageId` when the vendorId is recognized but the messageId
  is not
- `Accepted` when both vendor and message are recognized

`handleRequestDataTransfer` previously returned `UnknownMessageId` for
*any* non-null messageId, even when the vendorId matched the station's
configured `chargePointVendor`:

    if (vendorId !== chargingStation.stationInfo?.chargePointVendor) {
      return ... UNKNOWN_VENDOR_ID
    }
    if (messageId != null) {
      return ... UNKNOWN_MESSAGE_ID     // wrong: any non-null messageId
    }

That rejected every legitimate DataTransfer with a messageId, regardless
of whether the messageId would have been supported. The simulator does
not maintain a per-vendor messageId registry (it has no way to know
which custom messageIds a real charge point would accept), so the
spec-correct behaviour is to accept any messageId once the vendor
matches.

Drop the erroneous `messageId != null` branch and return `Accepted`
whenever the vendor matches; the `UnknownVendorId` path is unchanged.
Add a source comment citing OCPP 1.6 §4.3 to prevent regression of the
buggy check.

Test update: the existing 'should return UnknownMessageId for matching
vendor with messageId' test asserted the old buggy behaviour. Rewritten
as 'should return Accepted for matching vendor with messageId (no
messageId registry per §4.3)' to lock in the spec-correct outcome.

Gates pass (typecheck / test, 2955 pass / 0 fail).

* fix(ocpp/1.6): emit FirmwareStatusNotification(Installed) after Installing

Per OCPP 1.6 §4.5, the Charge Point SHALL notify the CSMS of firmware
update progress via FirmwareStatusNotification. `updateFirmwareSimulation`
already emitted the intermediate statuses (Downloading -> Downloaded ->
Installing) but never signalled successful completion — after emitting
`Installing`, the simulation went straight to the optional reset step
(if configured) or returned, leaving the CSMS with no confirmation
that installation actually finished.

Insert an `Installed` status emission between the InstallationFailed
early-return branch and the optional reset:

    sleep(...)
    requestHandler(FIRMWARE_STATUS_NOTIFICATION, { status: Installed })
    stationInfo.firmwareStatus = Installed
    if (reset === true) { sleep(...); reset() }

Behaviour on the failure path is unchanged: the InstallationFailed
branch still returns early without emitting `Installed`. Behaviour
when `reset` is disabled is unchanged apart from the additional
notification (which is the whole point of the fix).

Gates pass (typecheck / test, 2935 pass / 0 fail).

* fix(ocpp/1.6): return Failed (not NotSupported) when LocalAuthList is disabled or manager missing

Per OCPP 1.6 §5.15, the SendLocalList response statuses have distinct
semantics:
- NotSupported: the LocalAuthListManagement feature profile is not
  implemented by the Charge Point
- Failed: the feature is implemented but the specific update failed
  (disabled, unavailable, or otherwise unable to apply the change)

`handleRequestSendLocalList` correctly returned NotSupported when the
LocalAuthListManagement profile is absent (line 1534). But two
subsequent guards also returned NotSupported when:
- `LocalAuthListEnabled` config is false (feature supported but disabled)
- `getLocalAuthListManager()` returns null (feature supported but manager
  not wired)

Both scenarios describe a Charge Point that supports the feature — the
LocalAuthListManagement profile is present — but cannot apply this
particular update. Per spec, that is Failed, not NotSupported.

Change both guards to return
`OCPP16Constants.OCPP_SEND_LOCAL_LIST_RESPONSE_FAILED`. The profile-
absent guard (line 1534) is unchanged — it correctly reports
NotSupported when the feature is not implemented at all.

Test updates: two existing tests locked in the old NotSupported result.
Renamed and updated to assert Failed with an inline reference to §5.15
so the spec-correct outcome is regression-locked.

Gates pass (typecheck / test, 2945 pass / 0 fail).

* chore(auth): remove OCPPAuthServiceImpl from public barrel

`OCPPAuthServiceImpl` is the concrete implementation of the
`OCPPAuthService` interface. External code should construct instances
through `OCPPAuthServiceFactory` (which internally uses the class)
rather than instantiating it directly. Exporting the concrete class
from the auth barrel published an implementation detail that:

- Adds public API surface with no consumer benefit
- Encourages callers to bypass the factory's per-station singleton
- Complicates future refactoring of the concrete class

Grep across src/ and ui/ confirms zero consumers of
`OCPPAuthServiceImpl` via the barrel (`ocpp/auth/index.js`); the
factory imports it via the direct file path within the same subtree.
Two test files (`OCPP20ResponseService-CacheUpdate.test.ts`,
`OCPP20ServiceUtils-AuthCache.test.ts`) did import it via the barrel;
routed to the direct path so they still exercise the concrete class
where explicitly needed.

Also correct the module-level JSDoc from 'OCPP 1.6 and 2.0' to
'OCPP 1.6 and 2.0.1' to match the canonical spec-version convention
used throughout the codebase.

Gates pass (typecheck / lint / test, 2955 pass / 0 fail).

* refactor: extend OCPP 2.0.1 terminology to non-auth paths and compress physics comment

Two round-3 review findings from PR #1960 self-review addressed together.

1. Round-3 High: terminology scope creep

Prior commit dbc3f3ff bulk-replaced OCPP 2.0 -> OCPP 2.0.1 in
src/charging-station/ocpp/auth/ only (65 occurrences). Identical drift
existed across the rest of the OCPP 2.0.x code paths, including the
invalid OCPP 2.0+ form that dbc3f3ff had explicitly corrected in auth/
but left intact in ~10 sites of OCPP20IncomingRequestService JSDoc.
After dbc3f3ff, auth/ said OCPP 2.0.1 while the rest of the OCPP 2.0.x
subtree still said OCPP 2.0 / OCPP 2.0+ — the terminology divergence
was worse, not better.

Apply the same three-pass perl regex (strip +, collapse /2.1,
negative-lookahead on .digit for bare 2.0) to:

- src/charging-station/ocpp/2.0/**/*.ts               (56 occurrences)
- src/charging-station/ocpp/OCPPServiceUtils.ts       (3)
- src/charging-station/ui-server/UIMCPServer.ts       (2)
- ui/common/tests/payloadBuilders.test.ts             (2)

63 total substitutions. Zero code behavior change; comment/JSDoc/test
description content only. Zero remaining bare OCPP 2.0 refs under the
scanned scope.

2. Round-3 Low: physics comment verbosity

The sqrt(3) physics anchor comment added in commit 34a574f9 spanned 8
lines with tangential explanations. Compress to 3 lines that keep the
essential physics anchor (V_LL = sqrt(3) * V_LN, 30-degree phase
separation, 3-phase-only constraint).

Documented as still-deferred:
- Narrative comment at OCPP16IncomingRequestService.ts:276 — kept under
  M5 bulk cleanup deferral.
- OCPP 2.0.x handleRequestDataTransfer UnknownVendorId design — a
  different intentional choice, not the same bug as OCPP 1.6 C3.

All gates green (format / typecheck / lint / build / test) across root,
ui/cli, ui/web. Circular-deps baseline of 61 preserved.

* chore: complete OCPP 2.0.1 terminology sweep and drop redundant physics comment line

Round-4 review findings from PR #1960 self-review addressed together.

1. Terminology sweep completion (Low)

The round-3 extension (commit e03368ff) covered
src/charging-station/ocpp/2.0/**, OCPPServiceUtils.ts, UIMCPServer.ts,
and payloadBuilders.test.ts, but the audit's terminology finding
applied to the whole codebase. Four bare 'OCPP 2.0' refs survived in
charging-station/ files outside ocpp/2.0/:

- src/charging-station/ConfigurationKeyUtils.ts:
  user-facing warning 'use OCPP 2.0 variable ... instead'
- src/charging-station/meter-values/CoherentMeterValueBuilder.ts:
  code comment 'Narrow the OCPP 2.0 SampledValueTemplate.unit ...'
- src/charging-station/TemplateSchema.ts (2 sites):
  JSDoc references to 'OCPP 2.0 unit-of-measure descriptor' and
  'OCPP 2.0 namespaced keys'

Apply the same negative-lookahead perl regex; 4 substitutions, zero
code behaviour change. The user-facing warning in ConfigurationKeyUtils
now cites the spec version consistently with the rest of the codebase.

2. Physics comment redundancy (Info)

The compressed sqrt(3) anchor comment from commit e03368ff sat
directly below a preexisting one-liner ('Line-to-line voltage is only
defined for 3-phase AC') that duplicated the L-L === 3-phase constraint
already stated in the compressed block ('Defined only for numberOfPhases
=== 3'). Delete the preexisting redundant line; the compressed anchor
carries the full physics rationale (V_LL = sqrt(3) * V_LN, 30-degree
phase separation, 3-phase-only constraint).

Test files (~15 refs of 'OCPP 2.0' as describe/it group labels) are
intentionally left as-is: those are test-grouping labels rather than
spec-version references, and renaming them would break dev-side test
regex filters without functional benefit.

Gates green (format / typecheck / lint / build / test) across root;
ui/cli and ui/web untouched. Circular-deps baseline of 61 preserved.

35 files changed:
README.md
src/charging-station/ConfigurationKeyUtils.ts
src/charging-station/TemplateSchema.ts
src/charging-station/meter-values/CoherentMeterValueBuilder.ts
src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/1.6/OCPP16RequestService.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts
src/charging-station/ocpp/1.6/__testable__/index.ts
src/charging-station/ocpp/2.0/OCPP20CertificateManager.ts
src/charging-station/ocpp/2.0/OCPP20Constants.ts
src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts
src/charging-station/ocpp/2.0/OCPP20RequestBuilders.ts
src/charging-station/ocpp/2.0/OCPP20ResponseService.ts
src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts
src/charging-station/ocpp/2.0/__testable__/OCPP20RequestServiceTestable.ts
src/charging-station/ocpp/2.0/__testable__/OCPP20ResponseServiceTestable.ts
src/charging-station/ocpp/2.0/__testable__/index.ts
src/charging-station/ocpp/OCPPConstants.ts
src/charging-station/ocpp/OCPPServiceUtils.ts
src/charging-station/ocpp/auth/adapters/OCPP20AuthAdapter.ts
src/charging-station/ocpp/auth/index.ts
src/charging-station/ocpp/auth/strategies/CertificateAuthStrategy.ts
src/charging-station/ocpp/auth/types/AuthTypes.ts
src/charging-station/ocpp/auth/utils/AuthValidators.ts
src/charging-station/ui-server/AbstractUIServer.ts
src/charging-station/ui-server/UIMCPServer.ts
src/charging-station/ui-server/UIServerAccessPolicy.ts
src/charging-station/ui-server/UIServerSecurity.ts
src/utils/Constants.ts
tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-LocalAuthList.test.ts
tests/charging-station/ocpp/1.6/OCPP16IncomingRequestService-SimpleHandlers.test.ts
tests/charging-station/ocpp/2.0/OCPP20ResponseService-CacheUpdate.test.ts
tests/charging-station/ocpp/2.0/OCPP20ServiceUtils-AuthCache.test.ts
ui/common/tests/payloadBuilders.test.ts

index f1c5696be7be0eba86744c97fd5dec8be7dd0c4d..ff21eae36a52bb30cc6d2d0fd8977e9a62f8711e 100644 (file)
--- a/README.md
+++ b/README.md
@@ -625,6 +625,7 @@ make SUBMODULES_INIT=true
 
 #### C. Authorization
 
+- :white_check_mark: Authorize
 - :white_check_mark: ClearCache
 
 #### D. LocalAuthorizationListManagement
index 7ef6cb934d244e1b5ac7a31989a86e16e654a919..1807c796b42b85524c51c34d3bb3c799db8c4322 100644 (file)
@@ -141,7 +141,7 @@ export const warnOnOCPP16TemplateKeys = (chargingStation: ChargingStation): void
     const resolved = OCPP2_PARAMETER_KEY_MAP.get(entry.key)
     if (resolved != null) {
       logger.warn(
-        `${chargingStation.logPrefix()} Template uses OCPP 1.6 key '${entry.key}', use OCPP 2.0 variable '${resolved}' instead`
+        `${chargingStation.logPrefix()} Template uses OCPP 1.6 key '${entry.key}', use OCPP 2.0.1 variable '${resolved}' instead`
       )
     }
   }
index 53f50b39dc134e51b951984972d8ce7d6d1ec2f7..1c1b9399c0134c3ff448ce885bea3ccfba9d2946 100644 (file)
@@ -23,7 +23,7 @@ const SignedMeterValueSchema = z
   .catchall(z.unknown())
 
 /**
- * UnitOfMeasure — OCPP 2.0 unit-of-measure descriptor.
+ * UnitOfMeasure — OCPP 2.0.1 unit-of-measure descriptor.
  */
 const UnitOfMeasureSchema = z
   .object({
@@ -93,7 +93,7 @@ const EvseTemplateSchema = z.looseObject({
 
 /**
  * ConfigurationKey — OCPP configuration key entry.
- * `key` is z.string() (open set: vendor-specific and OCPP 2.0 namespaced keys are valid).
+ * `key` is z.string() (open set: vendor-specific and OCPP 2.0.1 namespaced keys are valid).
  */
 const ConfigurationKeySchema = z.looseObject({
   key: z.string(),
index 7f7052290c2e51d27af29daa71acb05bc3531b66..0b218d7c787f29c6d98b5bb9b1b444c18a0eeccc 100644 (file)
@@ -271,15 +271,11 @@ const resolvePhasedValue = (
     case MeterValueMeasurand.VOLTAGE:
       if (family === 'Neutral') return 0
       if (family === 'LineToLine') {
-        // Line-to-line voltage is only defined for 3-phase AC
-        // (V_LL = sqrt(3) * V_LN); 1-phase has no L-L pair, and
-        // `numberOfPhases === 2` is unsupported by contract
-        // (`Helpers.getPhaseRotationValue` branches only on {0, 1, 3}).
-        // Return `undefined` on any non-3-phase configuration so the
-        // caller can log-and-skip rather than emit a physically
-        // meaningless sqrt(2) * V_LN value.
+        // V_LL = sqrt(3) * V_LN in a balanced 3-phase Y system (30-degree
+        // phase separation). Defined only for numberOfPhases === 3;
+        // 1-phase has no L-L pair, 2-phase is unsupported by contract.
         if (numberOfPhases !== 3) return undefined
-        return Math.sqrt(numberOfPhases) * sample.voltageV
+        return Math.sqrt(3) * sample.voltageV
       }
       return sample.voltageV
     default:
@@ -443,7 +439,7 @@ export const buildCoherentMeterValue = (
         )
         continue
       }
-      // Narrow the OCPP 2.0 `SampledValueTemplate.unit` open-string branch
+      // Narrow the OCPP 2.0.1 `SampledValueTemplate.unit` open-string branch
       // to the closed `MeterValueUnit` union for the Map lookup below; any
       // string outside the enum returns `undefined` from the Map and falls
       // through to divider = 1 (unit-scale emission).
index 13ad03dd7e23a33976c87c04c1200de4fba1fc6a..62b9aa42f514730a3d8af3bdb6a0fd11b364437e 100644 (file)
@@ -173,7 +173,7 @@ const moduleName = 'OCPP16IncomingRequestService'
  */
 
 export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
-  protected readonly csmsName = 'central system'
+  protected readonly csmsName = 'Central System'
   protected readonly incomingRequestHandlers: Map<IncomingRequestCommand, IncomingRequestHandler>
 
   protected readonly moduleName = moduleName
@@ -956,14 +956,16 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     chargingStation: ChargingStation,
     commandPayload: OCPP16DataTransferRequest
   ): OCPP16DataTransferResponse {
-    const { messageId, vendorId } = commandPayload
+    const { vendorId } = commandPayload
     try {
       if (vendorId !== chargingStation.stationInfo?.chargePointVendor) {
         return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID
       }
-      if (messageId != null) {
-        return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_MESSAGE_ID
-      }
+      // OCPP 1.6 §4.3: `UnknownMessageId` is reserved for the case where the
+      // vendor is known but the messageId is not recognized by the Charge
+      // Point. The simulator does not maintain a per-vendor messageId
+      // registry, so any messageId (including undefined) is accepted when
+      // the vendor matches.
       return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED
     } catch (error) {
       const errorResponse: OCPP16DataTransferResponse =
@@ -1138,7 +1140,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
           return OCPP16Constants.OCPP_RESPONSE_EMPTY
         }
         const logFiles = readdirSync(
-          resolve((fileURLToPath(import.meta.url), '../', dirname(logFile)))
+          resolve(dirname(fileURLToPath(import.meta.url)), '../', dirname(logFile))
         )
           .filter(file => file.endsWith(extname(logFile)))
           .map(file => join(dirname(logFile), file))
@@ -1249,7 +1251,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
   }
 
   /**
-   * Handles OCPP 1.6 GetLocalListVersion request from central system.
+   * Handles OCPP 1.6 GetLocalListVersion request from Central System.
    * Returns the version number of the local authorization list.
    * @param chargingStation - The charging station instance processing the request
    * @returns GetLocalListVersionResponse with list version
@@ -1283,7 +1285,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
   }
 
   /**
-   * Handles OCPP 1.6 RemoteStartTransaction request from central system
+   * Handles OCPP 1.6 RemoteStartTransaction request from Central System
    * Initiates charging transaction on specified or available connector
    * @param chargingStation - The charging station instance processing the request
    * @param commandPayload - RemoteStartTransaction request payload with connector and ID tag
@@ -1534,11 +1536,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
     try {
       const authService = OCPPAuthServiceFactory.getInstance(chargingStation)
       if (!chargingStation.getLocalAuthListEnabled()) {
-        return OCPP16Constants.OCPP_SEND_LOCAL_LIST_RESPONSE_NOT_SUPPORTED
+        return OCPP16Constants.OCPP_SEND_LOCAL_LIST_RESPONSE_FAILED
       }
       const manager = authService.getLocalAuthListManager()
       if (manager == null) {
-        return OCPP16Constants.OCPP_SEND_LOCAL_LIST_RESPONSE_NOT_SUPPORTED
+        return OCPP16Constants.OCPP_SEND_LOCAL_LIST_RESPONSE_FAILED
       }
       if (commandPayload.listVersion <= 0) {
         return OCPP16Constants.OCPP_SEND_LOCAL_LIST_RESPONSE_FAILED
@@ -1925,6 +1927,16 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService {
         chargingStation.stationInfo.firmwareUpgrade.failureStatus
       return
     }
+    await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay + 1)))
+    await chargingStation.ocppRequestService.requestHandler<
+      OCPP16FirmwareStatusNotificationRequest,
+      OCPP16FirmwareStatusNotificationResponse
+    >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, {
+      status: OCPP16FirmwareStatus.Installed,
+    })
+    if (chargingStation.stationInfo != null) {
+      chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installed
+    }
     if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) {
       await sleep(secondsToMilliseconds(randomInt(minDelay, maxDelay + 1)))
       await chargingStation.reset(OCPP16StopTransactionReason.REBOOT)
index 6b39f17c970e2616a4c4f150ebcb92f81bf0047f..e58d40b95d98f6083afb2e1d6763617881ffe37c 100644 (file)
@@ -28,7 +28,7 @@ const moduleName = 'OCPP16RequestService'
 /**
  * OCPP 1.6 Request Service
  *
- * Handles outgoing OCPP 1.6 requests from the charging station to the central system.
+ * Handles outgoing OCPP 1.6 requests from the charging station to the Central System.
  * This service is responsible for:
  * - Building and validating request payloads according to OCPP 1.6 specification
  * - Managing request-response cycles with proper error handling
@@ -71,18 +71,18 @@ export class OCPP16RequestService extends OCPPRequestService {
    * It performs the following operations:
    * - Validates that the requested command is supported by the charging station
    * - Builds and validates the request payload according to OCPP 1.6 schemas
-   * - Sends the request to the central system with proper error handling
+   * - Sends the request to the Central System with proper error handling
    * - Processes responses with comprehensive logging and error recovery
    *
    * The method ensures type safety through generic type parameters while maintaining
    * backward compatibility with the OCPP 1.6 specification.
    * @template RequestType - The expected type of the request parameters
-   * @template ResponseType - The expected type of the response from the central system
+   * @template ResponseType - The expected type of the response from the Central System
    * @param chargingStation - The charging station instance making the request
    * @param commandName - The OCPP 1.6 command to execute (e.g., 'StartTransaction', 'StopTransaction')
    * @param commandParams - Optional parameters specific to the command being executed
    * @param params - Optional request parameters for controlling request behavior
-   * @returns Promise resolving to the typed response from the central system
+   * @returns Promise resolving to the typed response from the Central System
    * @throws {OCPPError} When the command is not supported or validation fails
    */
   // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
index c04a8991594d5176ea264cc58900cbc6010e2af3..3a6fa38470775014f5a46e15daf9b7146cd9cafd 100644 (file)
@@ -126,7 +126,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
   >
 
   protected readonly bootNotificationRequestCommand = OCPP16RequestCommand.BOOT_NOTIFICATION
-  protected readonly csmsName = 'central system'
+  protected readonly csmsName = 'Central System'
   protected readonly moduleName = moduleName
 
   protected payloadValidatorFunctions: Map<OCPP16RequestCommand, ValidateFunction<JsonType>>
@@ -283,7 +283,7 @@ export class OCPP16ResponseService extends OCPPResponseService {
       }
       const logMsg = `${chargingStation.logPrefix()} ${moduleName}.handleResponseBootNotification: Charging station in '${
         payload.status
-      }' state on the central system`
+      }' state on the Central System`
       payload.status === RegistrationStatusEnumType.REJECTED
         ? logger.warn(logMsg)
         : logger.info(logMsg)
index 52011a7f1162e98048585fc50ad3a40cad565be1..4f2ed6c812420cf1de597e7a0598558e559d994c 100644 (file)
@@ -855,11 +855,11 @@ export class OCPP16ServiceUtils {
   }
 
   /**
-   * Sends a StartTransaction request to the central system for the given connector.
+   * Sends a StartTransaction request to the Central System for the given connector.
    * @param chargingStation - Target charging station
    * @param connectorId - Connector identifier to start the transaction on
    * @param idTag - Optional RFID tag for the transaction
-   * @returns Start transaction response from the central system
+   * @returns Start transaction response from the Central System
    */
   public static async startTransactionOnConnector (
     chargingStation: ChargingStation,
@@ -938,11 +938,11 @@ export class OCPP16ServiceUtils {
   }
 
   /**
-   * Sends a StopTransaction request to the central system for the given connector.
+   * Sends a StopTransaction request to the Central System for the given connector.
    * @param chargingStation - Target charging station
    * @param connectorId - Connector identifier with the active transaction
    * @param reason - Optional stop transaction reason
-   * @returns Stop transaction response from the central system
+   * @returns Stop transaction response from the Central System
    */
   public static async stopTransactionOnConnector (
     chargingStation: ChargingStation,
index 40512276c0a0c7734cc52d75035dc1ba2bd28f96..87ccb3de6faf70d230487008cd7972937cb47f51 100644 (file)
@@ -62,7 +62,7 @@ import type { OCPP16RequestService } from '../OCPP16RequestService.js'
  */
 export interface TestableOCPP16IncomingRequestService {
   /**
-   * Handles OCPP 1.6 CancelReservation request from central system.
+   * Handles OCPP 1.6 CancelReservation request from Central System.
    * Cancels an existing reservation on the charging station.
    */
   handleRequestCancelReservation: (
@@ -71,7 +71,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => Promise<GenericResponse>
 
   /**
-   * Handles OCPP 1.6 ChangeAvailability request from central system.
+   * Handles OCPP 1.6 ChangeAvailability request from Central System.
    * Changes availability status of a connector or the entire station.
    */
   handleRequestChangeAvailability: (
@@ -80,7 +80,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => Promise<OCPP16ChangeAvailabilityResponse>
 
   /**
-   * Handles OCPP 1.6 ChangeConfiguration request from central system.
+   * Handles OCPP 1.6 ChangeConfiguration request from Central System.
    * Changes the value of a configuration key.
    */
   handleRequestChangeConfiguration: (
@@ -94,7 +94,7 @@ export interface TestableOCPP16IncomingRequestService {
   handleRequestClearCache: (chargingStation: ChargingStation) => ClearCacheResponse
 
   /**
-   * Handles OCPP 1.6 ClearChargingProfile request from central system.
+   * Handles OCPP 1.6 ClearChargingProfile request from Central System.
    * Clears charging profiles matching the specified criteria.
    */
   handleRequestClearChargingProfile: (
@@ -103,7 +103,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => OCPP16ClearChargingProfileResponse
 
   /**
-   * Handles OCPP 1.6 DataTransfer request from central system.
+   * Handles OCPP 1.6 DataTransfer request from Central System.
    * Processes vendor-specific data transfer messages.
    */
   handleRequestDataTransfer: (
@@ -112,7 +112,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => OCPP16DataTransferResponse
 
   /**
-   * Handles OCPP 1.6 GetCompositeSchedule request from central system.
+   * Handles OCPP 1.6 GetCompositeSchedule request from Central System.
    * Returns the composite charging schedule for a connector.
    */
   handleRequestGetCompositeSchedule: (
@@ -121,7 +121,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => OCPP16GetCompositeScheduleResponse
 
   /**
-   * Handles OCPP 1.6 GetConfiguration request from central system.
+   * Handles OCPP 1.6 GetConfiguration request from Central System.
    * Returns configuration keys and their values.
    */
   handleRequestGetConfiguration: (
@@ -130,7 +130,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => GetConfigurationResponse
 
   /**
-   * Handles OCPP 1.6 GetDiagnostics request from central system.
+   * Handles OCPP 1.6 GetDiagnostics request from Central System.
    * Uploads diagnostics data to the specified location.
    */
   handleRequestGetDiagnostics: (
@@ -143,7 +143,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => OCPP16GetLocalListVersionResponse
 
   /**
-   * Handles OCPP 1.6 RemoteStartTransaction request from central system.
+   * Handles OCPP 1.6 RemoteStartTransaction request from Central System.
    * Initiates charging transaction on specified or available connector.
    */
   handleRequestRemoteStartTransaction: (
@@ -152,7 +152,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => Promise<GenericResponse>
 
   /**
-   * Handles OCPP 1.6 RemoteStopTransaction request from central system.
+   * Handles OCPP 1.6 RemoteStopTransaction request from Central System.
    * Stops an ongoing transaction by transaction ID.
    */
   handleRequestRemoteStopTransaction: (
@@ -161,7 +161,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => GenericResponse
 
   /**
-   * Handles OCPP 1.6 ReserveNow request from central system.
+   * Handles OCPP 1.6 ReserveNow request from Central System.
    * Creates a reservation on a connector for a specific ID tag.
    */
   handleRequestReserveNow: (
@@ -170,7 +170,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => Promise<OCPP16ReserveNowResponse>
 
   /**
-   * Handles OCPP 1.6 Reset request from central system.
+   * Handles OCPP 1.6 Reset request from Central System.
    * Performs immediate or scheduled reset of the charging station.
    */
   handleRequestReset: (
@@ -184,7 +184,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => OCPP16SendLocalListResponse
 
   /**
-   * Handles OCPP 1.6 SetChargingProfile request from central system.
+   * Handles OCPP 1.6 SetChargingProfile request from Central System.
    * Sets or updates a charging profile on a connector.
    */
   handleRequestSetChargingProfile: (
@@ -193,7 +193,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => SetChargingProfileResponse
 
   /**
-   * Handles OCPP 1.6 TriggerMessage request from central system.
+   * Handles OCPP 1.6 TriggerMessage request from Central System.
    * Triggers the station to send a specific message type.
    */
   handleRequestTriggerMessage: (
@@ -202,7 +202,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => OCPP16TriggerMessageResponse
 
   /**
-   * Handles OCPP 1.6 UnlockConnector request from central system.
+   * Handles OCPP 1.6 UnlockConnector request from Central System.
    * Unlocks a connector and optionally stops any ongoing transaction.
    */
   handleRequestUnlockConnector: (
@@ -211,7 +211,7 @@ export interface TestableOCPP16IncomingRequestService {
   ) => Promise<UnlockConnectorResponse>
 
   /**
-   * Handles OCPP 1.6 UpdateFirmware request from central system.
+   * Handles OCPP 1.6 UpdateFirmware request from Central System.
    * Initiates firmware download and installation simulation.
    */
   handleRequestUpdateFirmware: (
index 7e0a3f3d87bb579a3be6f6b88db068fe0284510c..475929fb50f85b2577826020445f8a5a11f847f2 100644 (file)
@@ -48,7 +48,7 @@ export interface GetInstalledCertificatesResult {
 }
 
 /**
- * Interface for OCPP 2.0 Certificate Manager operations
+ * Interface for OCPP 2.0.1 Certificate Manager operations
  * Used for type-safe access to certificate management functionality
  */
 export interface OCPP20CertificateManagerInterface {
@@ -94,9 +94,9 @@ export interface ValidateCertificateX509Result {
 }
 
 /**
- * OCPP 2.0 Certificate Manager
+ * OCPP 2.0.1 Certificate Manager
  *
- * Provides certificate management operations for OCPP 2.0 charging stations:
+ * Provides certificate management operations for OCPP 2.0.1 charging stations:
  * - Store/delete certificates
  * - Compute certificate hashes
  * - Validate certificate format
index cf051bf24d38850c8fd771c9fb7dc05efb304857..46aa742b6da4a5f7d56038ef46193714a4d6abd6 100644 (file)
@@ -154,7 +154,7 @@ export class OCPP20Constants extends OCPPConstants {
   static readonly FIRMWARE_STATUS_DELAY_MS = 2000
   static readonly FIRMWARE_VERIFY_DELAY_MS = 500
   /**
-   * Default timeout in milliseconds for async OCPP 2.0 handler operations
+   * Default timeout in milliseconds for async OCPP 2.0.1 handler operations
    * (e.g., certificate file I/O). Prevents handlers from hanging indefinitely.
    */
   static readonly HANDLER_TIMEOUT_MS = 30_000
index c49e8602a1f6f5f46cd6b88af44c9dca36091b4d..4ec20e2fa9b7705585b0c7984f4965ab975611ca 100644 (file)
@@ -1536,7 +1536,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
   }
 
   /**
-   * Handles OCPP 2.0 CertificateSigned request from central system
+   * Handles OCPP 2.0.1 CertificateSigned request from central system
    * Receives signed certificate chain from CSMS and stores it in the charging station
    * Triggers websocket reconnect for ChargingStationCertificate type to use the new certificate
    * @param chargingStation - The charging station instance processing the request
@@ -1854,7 +1854,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
   }
 
   /**
-   * Handles OCPP 2.0 DeleteCertificate request from central system
+   * Handles OCPP 2.0.1 DeleteCertificate request from central system
    * Deletes a certificate matching the provided hash data from the charging station
    * Per M04.FR.06: ChargingStationCertificate cannot be deleted via DeleteCertificateRequest
    * @param chargingStation - The charging station instance processing the request
@@ -1990,7 +1990,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
   }
 
   /**
-   * Handles OCPP 2.0 GetInstalledCertificateIds request from central system
+   * Handles OCPP 2.0.1 GetInstalledCertificateIds request from central system
    * Returns list of installed certificates matching the optional filter types
    * @param chargingStation - The charging station instance processing the request
    * @param commandPayload - GetInstalledCertificateIds request payload with optional certificate type filter
@@ -2542,7 +2542,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
   }
 
   /**
-   * Handles OCPP 2.0 RequestStartTransaction request from central system
+   * Handles OCPP 2.0.1 RequestStartTransaction request from central system
    * Initiates charging transaction on specified EVSE with enhanced authorization
    * @param chargingStation - The charging station instance processing the request
    * @param commandPayload - RequestStartTransaction request payload with EVSE, ID token and profiles
@@ -2618,7 +2618,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
       return buildRejectedResponse(
         ReasonCodeEnumType.TxStarted,
         `Connector ${connectorId.toString()} has a pending transaction not yet authorized`,
-        // safe: OCPP 2.0 paths always store generateUUID() output here (see line ~2740)
+        // safe: OCPP 2.0.1 paths always store generateUUID() output here (see line ~2740)
         connectorStatus.transactionId as UUIDv4
       )
     }
@@ -3904,7 +3904,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
   }
 
   /**
-   * Terminates all active transactions on the charging station using OCPP 2.0 TransactionEventRequest
+   * Terminates all active transactions on the charging station using OCPP 2.0.1 TransactionEventRequest
    * @param chargingStation - The charging station instance
    * @param reason - The reason for transaction termination
    */
@@ -3920,7 +3920,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
   }
 
   /**
-   * Terminates all active transactions on the specified EVSE using OCPP 2.0 TransactionEventRequest
+   * Terminates all active transactions on the specified EVSE using OCPP 2.0.1 TransactionEventRequest
    * @param chargingStation - The charging station instance
    * @param evseId - The EVSE identifier to terminate transactions on
    * @param reason - The reason for transaction termination
@@ -4370,17 +4370,17 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
 }
 
 /**
- * OCPP 2.0+ Incoming Request Service - handles and processes all incoming requests
- * from the Central System (CSMS) to the Charging Station using OCPP 2.0+ protocol.
+ * OCPP 2.0.1 Incoming Request Service - handles and processes all incoming requests
+ * from the Central System (CSMS) to the Charging Station using OCPP 2.0.1 protocol.
  *
  * This service class is responsible for:
- * - **Request Reception**: Receiving and routing OCPP 2.0+ incoming requests from CSMS
- * - **Payload Validation**: Validating incoming request payloads against OCPP 2.0+ JSON schemas
- * - **Request Processing**: Executing business logic for each OCPP 2.0+ request type
+ * - **Request Reception**: Receiving and routing OCPP 2.0.1 incoming requests from CSMS
+ * - **Payload Validation**: Validating incoming request payloads against OCPP 2.0.1 JSON schemas
+ * - **Request Processing**: Executing business logic for each OCPP 2.0.1 request type
  * - **Response Generation**: Creating and sending appropriate responses back to CSMS
- * - **Enhanced Features**: Supporting advanced OCPP 2.0+ features like variable management
+ * - **Enhanced Features**: Supporting advanced OCPP 2.0.1 features like variable management
  *
- * Supported OCPP 2.0+ Incoming Request Types:
+ * Supported OCPP 2.0.1 Incoming Request Types:
  * - **Transaction Management**: RequestStartTransaction, RequestStopTransaction
  * - **Configuration Management**: SetVariables, GetVariables, GetBaseReport
  * - **Security Operations**: CertificatesSigned, SecurityEventNotification
@@ -4389,7 +4389,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
  * - **Display Management**: SetDisplayMessage, ClearDisplayMessage
  * - **Customer Management**: ClearCache, SendLocalList
  *
- * Key OCPP 2.0+ Enhancements:
+ * Key OCPP 2.0.1 Enhancements:
  * - **Variable Model**: Advanced configuration through standardized variable system
  * - **Enhanced Security**: Improved authentication and authorization mechanisms
  * - **Rich Messaging**: Support for display messages and customer information
@@ -4397,18 +4397,18 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService {
  * - **Flexible Charging**: Enhanced charging profile management and scheduling
  *
  * Architecture Pattern:
- * This class extends OCPPIncomingRequestService and implements OCPP 2.0+-specific
+ * This class extends OCPPIncomingRequestService and implements OCPP 2.0.1-specific
  * request handling logic. It integrates with the OCPP20VariableManager for advanced
  * configuration management and maintains backward compatibility concepts while
  * providing next-generation OCPP features.
  *
  * Validation Workflow:
  * 1. Incoming request received and parsed
- * 2. Payload validated against OCPP 2.0+ JSON schema
+ * 2. Payload validated against OCPP 2.0.1 JSON schema
  * 3. Request routed to appropriate handler method
  * 4. Business logic executed with variable model integration
  * 5. Response payload validated and sent back to CSMS
  * @see {@link validateIncomingRequestPayload} Request payload validation method
- * @see {@link handleRequestStartTransaction} Example OCPP 2.0+ request handler
+ * @see {@link handleRequestStartTransaction} Example OCPP 2.0.1 request handler
  * @see {@link OCPP20VariableManager} Variable management integration
  */
index e8024a9213b2314c04af8f4078d4ec3b1bb4032a..29c34ee39d546bafd309f9dac4db2a9737ca0125 100644 (file)
@@ -48,13 +48,13 @@ export const buildOCPP20BootNotificationRequest = (
 })
 
 /**
- * Builds an OCPP 2.0 sampled value from a template and measurement data.
+ * Builds an OCPP 2.0.1 sampled value from a template and measurement data.
  * @param sampledValueTemplate - The sampled value template to use.
  * @param value - The measured value.
  * @param context - The reading context.
  * @param phase - The phase of the measurement.
  * @param signingConfig - Optional signing configuration for generating signedMeterValue.
- * @returns The built OCPP 2.0 sampled value with signing metadata.
+ * @returns The built OCPP 2.0.1 sampled value with signing metadata.
  */
 export function buildOCPP20SampledValue (
   sampledValueTemplate: SampledValueTemplate,
index ea27be297a0b53a57770b44ad07b93aeace88e64..edd4931d1dcb78d767f1161a704e72d15d364a83 100644 (file)
@@ -49,17 +49,17 @@ import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'
 const moduleName = 'OCPP20ResponseService'
 
 /**
- * OCPP 2.0+ Response Service - handles and processes all outgoing request responses
- * from the Charging Station to the Central System Management System (CSMS) using OCPP 2.0+ protocol.
+ * OCPP 2.0.1 Response Service - handles and processes all outgoing request responses
+ * from the Charging Station to the Central System Management System (CSMS) using OCPP 2.0.1 protocol.
  *
  * This service class is responsible for:
  * - **Response Reception**: Receiving responses to requests sent from the Charging Station
- * - **Payload Validation**: Validating response payloads against OCPP 2.0+ JSON schemas
+ * - **Payload Validation**: Validating response payloads against OCPP 2.0.1 JSON schemas
  * - **Response Processing**: Processing CSMS responses and updating station state
  * - **Variable Management**: Handling variable-based configuration responses
- * - **Enhanced State Management**: Managing OCPP 2.0+ advanced state and feature coordination
+ * - **Enhanced State Management**: Managing OCPP 2.0.1 advanced state and feature coordination
  *
- * Supported OCPP 2.0+ Response Types:
+ * Supported OCPP 2.0.1 Response Types:
  * - **Authentication**: Authorize responses with enhanced authorization mechanisms
  * - **Transaction Management**: TransactionEvent responses for flexible transaction handling
  * - **Status Updates**: BootNotification, StatusNotification, NotifyReport responses
@@ -67,24 +67,24 @@ const moduleName = 'OCPP20ResponseService'
  * - **Security**: Responses to security-related operations and certificate management
  * - **Heartbeat**: Enhanced heartbeat response processing with additional metadata
  *
- * Key OCPP 2.0+ Features:
- * - **Variable Model Integration**: Seamless integration with OCPP 2.0+ variable system
+ * Key OCPP 2.0.1 Features:
+ * - **Variable Model Integration**: Seamless integration with OCPP 2.0.1 variable system
  * - **Enhanced Transaction Model**: Support for flexible transaction event handling
  * - **Security Framework**: Advanced security response processing and validation
  * - **Rich Data Model**: Support for complex data structures and enhanced messaging
  * - **Backward Compatibility**: Maintains compatibility concepts while extending functionality
  *
  * Architecture Pattern:
- * This class extends OCPPResponseService and implements OCPP 2.0+-specific response
- * processing logic. It works closely with OCPP20VariableManager and other OCPP 2.0+
+ * This class extends OCPPResponseService and implements OCPP 2.0.1-specific response
+ * processing logic. It works closely with OCPP20VariableManager and other OCPP 2.0.1
  * components to provide comprehensive protocol support with enhanced features.
  *
  * Response Validation Workflow:
  * 1. Response received from CSMS for the corresponding request
- * 2. Response payload validated against OCPP 2.0+ JSON schema
+ * 2. Response payload validated against OCPP 2.0.1 JSON schema
  * 3. Response routed to appropriate handler based on original request type
  * 4. Charging station state and variable model updated based on response content
- * 5. Enhanced follow-up actions triggered based on OCPP 2.0+ capabilities
+ * 5. Enhanced follow-up actions triggered based on OCPP 2.0.1 capabilities
  * @see {@link validateResponsePayload} Response payload validation method
  * @see {@link handleResponse} Response processing methods
  * @see {@link OCPP20VariableManager} Variable management integration
index cedcd25bee3e1f3f624aa22413fb80477f36bd6b..1364d9fa14a15d2a25726e0437cda036e1f179b9 100644 (file)
@@ -127,7 +127,7 @@ export class OCPP20ServiceUtils {
   /**
    * @param chargingStation - Target charging station for EVSE resolution
    * @param commandParams - Status notification parameters
-   * @returns Formatted OCPP 2.0 StatusNotification request payload
+   * @returns Formatted OCPP 2.0.1 StatusNotification request payload
    */
   public static buildStatusNotificationRequest (
     chargingStation: ChargingStation,
@@ -157,7 +157,7 @@ export class OCPP20ServiceUtils {
    * Build meter values for the start of a transaction.
    * @param chargingStation - Target charging station
    * @param transactionId - Transaction identifier
-   * @returns Array of OCPP 2.0 meter values at transaction begin
+   * @returns Array of OCPP 2.0.1 meter values at transaction begin
    */
   static buildTransactionStartedMeterValues (
     chargingStation: ChargingStation,
@@ -260,7 +260,7 @@ export class OCPP20ServiceUtils {
   }
 
   /**
-   * OCPP 2.0 Incoming Request Service validator configurations
+   * OCPP 2.0.1 Incoming Request Service validator configurations
    * @returns Array of validator configuration tuples
    */
   public static createIncomingRequestPayloadConfigs = (): [
@@ -269,7 +269,7 @@ export class OCPP20ServiceUtils {
   ][] => createPayloadConfigs(OCPP20ServiceUtils.incomingRequestSchemaNames, 'Request.json')
 
   /**
-   * Configuration for OCPP 2.0 Incoming Request Response validators
+   * Configuration for OCPP 2.0.1 Incoming Request Response validators
    * @returns Array of validator configuration tuples
    */
   public static createIncomingRequestResponsePayloadConfigs = (): [
@@ -278,10 +278,10 @@ export class OCPP20ServiceUtils {
   ][] => createPayloadConfigs(OCPP20ServiceUtils.incomingRequestSchemaNames, 'Response.json')
 
   /**
-   * Factory options for OCPP 2.0 payload validators
+   * Factory options for OCPP 2.0.1 payload validators
    * @param moduleName - Name of the OCPP module
    * @param methodName - Name of the method/command
-   * @returns Factory options object for OCPP 2.0 validators
+   * @returns Factory options object for OCPP 2.0.1 validators
    */
   public static createPayloadOptions = (moduleName: string, methodName: string) =>
     PayloadValidatorOptions(
@@ -292,7 +292,7 @@ export class OCPP20ServiceUtils {
     )
 
   /**
-   * OCPP 2.0 Request Service validator configurations
+   * OCPP 2.0.1 Request Service validator configurations
    * @returns Array of validator configuration tuples
    */
   public static createRequestPayloadConfigs = (): [
@@ -301,7 +301,7 @@ export class OCPP20ServiceUtils {
   ][] => createPayloadConfigs(OCPP20ServiceUtils.outgoingRequestSchemaNames, 'Request.json')
 
   /**
-   * OCPP 2.0 Response Service validator configurations
+   * OCPP 2.0.1 Response Service validator configurations
    * @returns Array of validator configuration tuples
    */
   public static createResponsePayloadConfigs = (): [
@@ -1256,7 +1256,7 @@ export class OCPP20ServiceUtils {
       } else {
         transactionId = connectorStatus.transactionId.toString()
         logger.warn(
-          `${chargingStation.logPrefix()} ${moduleName}.resolveActiveTransaction: Non-string transaction ID ${transactionId} converted to string for OCPP 2.0`
+          `${chargingStation.logPrefix()} ${moduleName}.resolveActiveTransaction: Non-string transaction ID ${transactionId} converted to string for OCPP 2.0.1`
         )
       }
       return { connectorStatus, transactionId }
index 6a04c01b1d9b4f4f8554b6f37ff170a43dd9b0b9..20bbcab1d906239091f2721dc3a83d8c6a4bf333 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * Testable wrapper for OCPP 2.0 RequestService
+ * Testable wrapper for OCPP 2.0.1 RequestService
  *
  * This module provides type-safe testing utilities for OCPP20RequestService.
  * It enables mocking of the protected sendMessage method and provides access
index 36d7baa3e92a3253cddb0559a7c7f04b459c66ac..b22df9af95cc990b41482785942614cfc19f47ca 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * Testable wrapper for OCPP 2.0 ResponseService
+ * Testable wrapper for OCPP 2.0.1 ResponseService
  *
  * This module provides type-safe access to private handler methods of
  * OCPP20ResponseService for testing purposes. It replaces ad hoc
index 73480b98b7079b110e96d48b38b65fdb4297e7f5..98fb4e0de67bf77dfa949ba2eeb286b2949edf87 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * Testable interface for OCPP 2.0 IncomingRequestService
+ * Testable interface for OCPP 2.0.1 IncomingRequestService
  *
  * This module provides type-safe access to private handler methods for testing purposes.
  * It replaces `as any` casts with a properly typed interface, enabling:
@@ -122,7 +122,7 @@ interface TestableOCPP20IncomingRequestService {
   ) => OCPP20DataTransferResponse
 
   /**
-   * Handles OCPP 2.0 DeleteCertificate request from central system.
+   * Handles OCPP 2.0.1 DeleteCertificate request from central system.
    * Deletes a certificate matching the provided hash data from the charging station.
    */
   handleRequestDeleteCertificate: (
@@ -131,7 +131,7 @@ interface TestableOCPP20IncomingRequestService {
   ) => Promise<OCPP20DeleteCertificateResponse>
 
   /**
-   * Handles OCPP 2.0 GetBaseReport request.
+   * Handles OCPP 2.0.1 GetBaseReport request.
    * Returns device model report based on the requested report base type.
    */
   handleRequestGetBaseReport: (
@@ -140,7 +140,7 @@ interface TestableOCPP20IncomingRequestService {
   ) => OCPP20GetBaseReportResponse
 
   /**
-   * Handles OCPP 2.0 GetInstalledCertificateIds request from central system.
+   * Handles OCPP 2.0.1 GetInstalledCertificateIds request from central system.
    * Returns list of installed certificates matching the optional filter types.
    */
   handleRequestGetInstalledCertificateIds: (
@@ -171,7 +171,7 @@ interface TestableOCPP20IncomingRequestService {
   ) => OCPP20GetTransactionStatusResponse
 
   /**
-   * Handles OCPP 2.0 GetVariables request.
+   * Handles OCPP 2.0.1 GetVariables request.
    * Returns values for requested variables from the device model.
    */
   handleRequestGetVariables: (
@@ -180,7 +180,7 @@ interface TestableOCPP20IncomingRequestService {
   ) => OCPP20GetVariablesResponse
 
   /**
-   * Handles OCPP 2.0 InstallCertificate request from central system.
+   * Handles OCPP 2.0.1 InstallCertificate request from central system.
    * Installs a certificate of the specified type in the charging station.
    */
   handleRequestInstallCertificate: (
@@ -189,7 +189,7 @@ interface TestableOCPP20IncomingRequestService {
   ) => Promise<OCPP20InstallCertificateResponse>
 
   /**
-   * Handles OCPP 2.0 Reset request.
+   * Handles OCPP 2.0.1 Reset request.
    * Performs immediate or scheduled reset of charging station or specific EVSE.
    */
   handleRequestReset: (
@@ -212,7 +212,7 @@ interface TestableOCPP20IncomingRequestService {
   ) => OCPP20SetNetworkProfileResponse
 
   /**
-   * Handles OCPP 2.0 SetVariables request.
+   * Handles OCPP 2.0.1 SetVariables request.
    * Sets values for requested variables in the device model.
    */
   handleRequestSetVariables: (
@@ -221,7 +221,7 @@ interface TestableOCPP20IncomingRequestService {
   ) => OCPP20SetVariablesResponse
 
   /**
-   * Handles OCPP 2.0 RequestStartTransaction request from central system.
+   * Handles OCPP 2.0.1 RequestStartTransaction request from central system.
    * Initiates charging transaction on specified EVSE with enhanced authorization.
    */
   handleRequestStartTransaction: (
@@ -230,7 +230,7 @@ interface TestableOCPP20IncomingRequestService {
   ) => Promise<OCPP20RequestStartTransactionResponse>
 
   /**
-   * Handles OCPP 2.0 RequestStopTransaction request from central system.
+   * Handles OCPP 2.0.1 RequestStopTransaction request from central system.
    * Validates and returns Accepted/Rejected. Actual stop is performed by event listener.
    */
   handleRequestStopTransaction: (
index c28c4706d2f06cf399121bf3a2f2821a8847ab7e..b5cf91e3e2901796cf17d270fe7102033ac5b663 100644 (file)
@@ -159,8 +159,7 @@ export class OCPPConstants {
     status: TriggerMessageStatus.REJECTED,
   })
 
-  static readonly OCPP_WEBSOCKET_TIMEOUT_MS = 60000
-
+  static readonly OCPP_WEBSOCKET_TIMEOUT_MS = 60_000
   static readonly UNKNOWN_OCPP_COMMAND = 'unknown OCPP command' as
     IncomingRequestCommand | RequestCommand
 
index 3f91df21d97cd6a9b8dff5a340b81e79a0c91eb6..851598836404caee146dbaa95a01b325d556aab6 100644 (file)
@@ -911,7 +911,7 @@ interface VersionedSampledValueDispatch {
   signingConfig: SampledValueSigningConfig | undefined
   /**
    * Passed by reference; the closure assigned to `buildVersionedSampledValue`
-   * mutates its `publicKeyIncluded` flag when a signed OCPP 2.0 SampledValue
+   * mutates its `publicKeyIncluded` flag when a signed OCPP 2.0.1 SampledValue
    * is emitted. Callers rely on the reference identity to detect the mutation.
    */
   signingState: { publicKeyIncluded: boolean }
@@ -924,7 +924,7 @@ interface VersionedSampledValueDispatch {
  * @param chargingStation - Target charging station.
  * @param transactionId - Active transaction identifier.
  * @param context - Optional MeterValue reading context (drives signing
- *   configuration for OCPP 2.0).
+ *   configuration for OCPP 2.0.1).
  * @returns The dispatch bundle.
  */
 const createVersionedSampledValueDispatcher = (
@@ -1251,10 +1251,12 @@ export const buildMeterValue = (
             ? ((phase + 1) % chargingStation.getNumberOfPhases()).toString()
             : chargingStation.getNumberOfPhases().toString()
         const lineToLineLabel = `L${phase.toString()}-L${nextPhase}` as MeterValuePhase
-        const lineToLineNominalVoltage = roundTo(
-          Math.sqrt(chargingStation.getNumberOfPhases()) * chargingStation.getVoltageOut(),
-          2
-        )
+        // `V_LL = sqrt(3) * V_LN` in a balanced 3-phase Y system; the
+        // sqrt(3) factor comes from the 30-degree phase separation, not
+        // from the phase count itself. Emitting L-L values makes physical
+        // sense only for `numberOfPhases === 3`; single-phase has no L-L
+        // pair, and `numberOfPhases === 2` is unsupported by contract.
+        const lineToLineNominalVoltage = roundTo(Math.sqrt(3) * chargingStation.getVoltageOut(), 2)
         addPhaseVoltageToMeterValue(
           chargingStation,
           connectorId,
@@ -1517,7 +1519,7 @@ const isMeasurandSupported = (measurand: MeterValueMeasurand): boolean => {
  * @param connectorId - Connector ID to look up templates for
  * @param measurandsKey - Configuration key containing the list of sampled measurands
  * @param measurand - Meter value measurand to match
- * @param evseId - Optional EVSE ID for OCPP 2.0 template lookup
+ * @param evseId - Optional EVSE ID for OCPP 2.0.1 template lookup
  * @param phase - Optional phase to match in the template
  * @returns Matching sampled value template, or undefined if not found
  */
index 05d694a2132332922d728f1a35dbd15d0cd53587..f5f98d56f202b4c120650e9f79b727f883d8e3b6 100644 (file)
@@ -38,10 +38,10 @@ import {
 const moduleName = 'OCPP20AuthAdapter'
 
 /**
- * OCPP 2.0 Authentication Adapter
+ * OCPP 2.0.1 Authentication Adapter
  *
- * Handles authentication for OCPP 2.0/2.1 charging stations by translating
- * between auth types and OCPP 2.0 specific types and protocols.
+ * Handles authentication for OCPP 2.0.1 charging stations by translating
+ * between auth types and OCPP 2.0.1 specific types and protocols.
  */
 export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   readonly ocppVersion = OCPPVersion.VERSION_201
@@ -49,11 +49,11 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   constructor (private readonly chargingStation: ChargingStation) {}
 
   /**
-   * Perform remote authorization using OCPP 2.0 Authorize request.
+   * Perform remote authorization using OCPP 2.0.1 Authorize request.
    * @param identifier - Identifier containing the IdToken to authorize
    * @param connectorId - EVSE/connector ID for the authorization context
    * @param transactionId - Optional existing transaction ID for ongoing transactions
-   * @returns Authorization result with status, method, and OCPP 2.0 specific metadata
+   * @returns Authorization result with status, method, and OCPP 2.0.1 specific metadata
    */
   async authorizeRemote (
     identifier: Identifier,
@@ -64,7 +64,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
 
     try {
       logger.debug(
-        `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorizing identifier '${truncateId(identifier.value)}' via OCPP 2.0 Authorize`
+        `${this.chargingStation.logPrefix()} ${moduleName}.${methodName}: Authorizing identifier '${truncateId(identifier.value)}' via OCPP 2.0.1 Authorize`
       )
 
       const isRemoteAuth = this.isRemoteAvailable()
@@ -89,7 +89,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
         return {
           additionalInfo: {
             connectorId,
-            error: 'Invalid token format for OCPP 2.0',
+            error: 'Invalid token format for OCPP 2.0.1',
             transactionId,
           },
           isOffline: false,
@@ -154,15 +154,15 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   }
 
   /**
-   * Convert identifier to OCPP 2.0 IdToken
-   * @param identifier - Identifier to convert to OCPP 2.0 format
-   * @returns OCPP 2.0 IdTokenType with mapped type and additionalInfo
+   * Convert identifier to OCPP 2.0.1 IdToken
+   * @param identifier - Identifier to convert to OCPP 2.0.1 format
+   * @returns OCPP 2.0.1 IdTokenType with mapped type and additionalInfo
    */
   convertFromIdentifier (identifier: Identifier): OCPP20IdTokenType {
-    // Map type back to OCPP 2.0 type
+    // Map type back to OCPP 2.0.1 type
     const ocpp20Type = mapToOCPP20TokenType(identifier.type)
 
-    // Convert additionalInfo back to OCPP 2.0 format
+    // Convert additionalInfo back to OCPP 2.0.1 format
     const additionalInfo: AdditionalInfoType[] | undefined = identifier.additionalInfo
       ? Object.entries(identifier.additionalInfo)
         .filter(([key]) => key.startsWith('info_'))
@@ -187,8 +187,8 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   }
 
   /**
-   * Convert OCPP 2.0 IdToken to identifier
-   * @param identifier - OCPP 2.0 IdToken or raw string identifier
+   * Convert OCPP 2.0.1 IdToken to identifier
+   * @param identifier - OCPP 2.0.1 IdToken or raw string identifier
    * @param additionalData - Optional metadata to include in the identifier
    * @returns Identifier with normalized type and metadata
    */
@@ -233,17 +233,17 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   }
 
   /**
-   * Convert authorization result to OCPP 2.0 response format
+   * Convert authorization result to OCPP 2.0.1 response format
    * @param result - Authorization result to convert
-   * @returns OCPP 2.0 RequestStartStopStatusEnumType for transaction responses
+   * @returns OCPP 2.0.1 RequestStartStopStatusEnumType for transaction responses
    */
   convertToOCPP20Response (result: AuthorizationResult): RequestStartStopStatusEnumType {
     return mapToOCPP20Status(result.status)
   }
 
   /**
-   * Create authorization request from OCPP 2.0 context
-   * @param idTokenOrString - OCPP 2.0 IdToken or raw string identifier
+   * Create authorization request from OCPP 2.0.1 context
+   * @param idTokenOrString - OCPP 2.0.1 IdToken or raw string identifier
    * @param connectorId - Optional EVSE/connector ID for the request
    * @param transactionId - Optional transaction ID for ongoing transactions
    * @param context - Optional context string (e.g., 'start', 'stop', 'remote_start')
@@ -295,7 +295,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   }
 
   /**
-   * @returns Configuration schema object for OCPP 2.0 authorization settings
+   * @returns Configuration schema object for OCPP 2.0.1 authorization settings
    */
   getConfigurationSchema (): JsonObject {
     return {
@@ -309,7 +309,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
           minimum: 1,
           type: 'number',
         },
-        // OCPP 2.0 specific variables
+        // OCPP 2.0.1 specific variables
         authorizeRemoteStart: {
           description: 'Enable remote authorization via RequestStartTransaction',
           type: 'boolean',
@@ -337,7 +337,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   }
 
   /**
-   * @returns Always undefined — OCPP 2.0 defines capacity via LocalAuthListCtrlr.Storage (bytes), not entry count
+   * @returns Always undefined — OCPP 2.0.1 defines capacity via LocalAuthListCtrlr.Storage (bytes), not entry count
    */
   getMaxLocalAuthListEntries (): number | undefined {
     return undefined
@@ -352,7 +352,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
       isOnline: this.chargingStation.inAcceptedState(),
       localAuthEnabled: true, // Configuration dependent
       ocppVersion: this.ocppVersion,
-      remoteAuthEnabled: true, // Always available in OCPP 2.0
+      remoteAuthEnabled: true, // Always available in OCPP 2.0.1
       stationId: this.chargingStation.stationInfo?.chargingStationId,
       supportsIdTokenTypes: [
         OCPP20IdTokenEnumType.Central,
@@ -367,7 +367,7 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   }
 
   /**
-   * Check if remote authorization is available for OCPP 2.0
+   * Check if remote authorization is available for OCPP 2.0.1
    * @returns True if remote authorization is available and enabled
    */
   isRemoteAvailable (): boolean {
@@ -390,22 +390,22 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   }
 
   /**
-   * Check if identifier is valid for OCPP 2.0
-   * @param identifier - Identifier to validate against OCPP 2.0 rules
-   * @returns True if identifier meets OCPP 2.0 format requirements (max 36 chars, valid type)
+   * Check if identifier is valid for OCPP 2.0.1
+   * @param identifier - Identifier to validate against OCPP 2.0.1 rules
+   * @returns True if identifier meets OCPP 2.0.1 format requirements (max 36 chars, valid type)
    */
   isValidIdentifier (identifier: Identifier): boolean {
-    // OCPP 2.0 idToken validation
+    // OCPP 2.0.1 idToken validation
     if (!identifier.value || typeof identifier.value !== 'string') {
       return false
     }
 
-    // Check length (OCPP 2.0 spec: max 36 characters)
+    // Check length (OCPP 2.0.1 spec: max 36 characters)
     if (isEmpty(identifier.value) || identifier.value.length > 36) {
       return false
     }
 
-    // OCPP 2.0 supports multiple identifier types
+    // OCPP 2.0.1 supports multiple identifier types
     const validTypes = [
       IdentifierType.ID_TAG,
       IdentifierType.CENTRAL,
@@ -422,9 +422,9 @@ export class OCPP20AuthAdapter implements OCPPAuthAdapter<OCPP20IdTokenType> {
   }
 
   /**
-   * Validate adapter configuration for OCPP 2.0
+   * Validate adapter configuration for OCPP 2.0.1
    * @param config - Authentication configuration to validate
-   * @returns Promise resolving to true if configuration is valid for OCPP 2.0 operations
+   * @returns Promise resolving to true if configuration is valid for OCPP 2.0.1 operations
    */
   validateConfiguration (config: AuthConfiguration): boolean {
     try {
index b844de1a0838319b68611bb55ed0459b94b930be..fd9cc06042b655e7fa440f4e65aeae1e3d664697 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * OCPP Authentication System
  *
- * Authentication layer for OCPP 1.6 and 2.0 protocols.
+ * Authentication layer for OCPP 1.6 and 2.0.1 protocols.
  * This module provides a consistent API for handling authentication
  * across different OCPP versions, with support for multiple authentication
  * strategies including local lists, remote authorization, and certificate-based auth.
@@ -20,7 +20,6 @@ export type {
   OCPPAuthService,
 } from './interfaces/OCPPAuthService.js'
 export { OCPPAuthServiceFactory } from './services/OCPPAuthServiceFactory.js'
-export { OCPPAuthServiceImpl } from './services/OCPPAuthServiceImpl.js'
 export {
   type AuthConfiguration,
   AuthContext,
index 2cf6f3d2dc4227a297d1e014b54eeff51cfb6567..fffc94e01c7afaa7765239bf03d6e8123862c326 100644 (file)
@@ -17,10 +17,10 @@ const moduleName = 'CertificateAuthStrategy'
 const CERTIFICATE_VERIFY_DELAY_MS = 100
 
 /**
- * Certificate-based authentication strategy for OCPP 2.0+
+ * Certificate-based authentication strategy for OCPP 2.0.1
  *
  * This strategy handles PKI-based authentication using X.509 certificates.
- * It's primarily designed for OCPP 2.0 where certificate-based authentication
+ * It's primarily designed for OCPP 2.0.1 where certificate-based authentication
  * is supported and can provide higher security than traditional ID token auth.
  *
  * Priority: 3 (lowest - used as fallback or for high-security scenarios)
@@ -76,7 +76,7 @@ export class CertificateAuthStrategy implements AuthStrategy {
 
       const adapter = this.adapter
 
-      // For OCPP 2.0, we can use certificate-based validation
+      // For OCPP 2.0.1, we can use certificate-based validation
       if (this.adapter.ocppVersion === OCPPVersion.VERSION_201) {
         const result = await this.validateCertificateWithOCPP20(request, adapter, config)
         this.updateStatistics(result, startTime)
@@ -114,7 +114,7 @@ export class CertificateAuthStrategy implements AuthStrategy {
       return false
     }
 
-    // Only supported in OCPP 2.0+
+    // Only supported in OCPP 2.0.1
     if (this.adapter.ocppVersion === OCPPVersion.VERSION_16) {
       return false
     }
@@ -341,9 +341,9 @@ export class CertificateAuthStrategy implements AuthStrategy {
   }
 
   /**
-   * Validate certificate using OCPP 2.0 mechanisms
+   * Validate certificate using OCPP 2.0.1 mechanisms
    * @param request - Authorization request with certificate identifier
-   * @param adapter - OCPP 2.0 adapter for protocol-specific operations
+   * @param adapter - OCPP 2.0.1 adapter for protocol-specific operations
    * @param config - Authentication configuration settings
    * @returns Authorization result indicating certificate validation outcome
    */
@@ -393,7 +393,7 @@ export class CertificateAuthStrategy implements AuthStrategy {
         )
       }
     } catch (error) {
-      logger.error(`${moduleName}: OCPP 2.0 certificate validation error:`, error)
+      logger.error(`${moduleName}: OCPP 2.0.1 certificate validation error:`, error)
       return this.createFailureResult(
         AuthorizationStatus.INVALID,
         'Certificate validation error',
index ce0a0488e2408442c118e7b43766f9f635212fb8..c3ae97664e93d7d4a13b8854098abe1dbe3e1d54 100644 (file)
@@ -64,7 +64,7 @@ export enum AuthorizationStatus {
 
   INVALID = 'Invalid',
 
-  // OCPP 2.0 specific
+  // OCPP 2.0.1 specific
   NO_CREDIT = 'NoCredit',
 
   NOT_ALLOWED_TYPE_EVSE = 'NotAllowedTypeEVSE',
@@ -81,7 +81,7 @@ export enum AuthorizationStatus {
 export enum IdentifierType {
   BIOMETRIC = 'Biometric',
 
-  // OCPP 2.0 types (mapped from OCPP20IdTokenEnumType)
+  // OCPP 2.0.1 types (mapped from OCPP20IdTokenEnumType)
   CENTRAL = 'Central',
   // Future extensibility
   CERTIFICATE = 'Certificate',
@@ -124,7 +124,7 @@ export interface AuthConfiguration extends JsonObject {
   certificateValidationStrict?: boolean
 
   /**
-   * Disable post-authorize behavior for non-Accepted cached/local list tokens (OCPP 2.0).
+   * Disable post-authorize behavior for non-Accepted cached/local list tokens (OCPP 2.0.1).
    * When true, non-Accepted tokens from cache or local list are accepted locally without
    * triggering a remote AuthorizationRequest (C10.FR.03, C12.FR.05, C14.FR.03).
    * When false, non-Accepted cached/local tokens trigger re-authorization via remote.
@@ -212,7 +212,7 @@ export interface AuthRequest {
   /** Authentication context */
   readonly context: AuthContext
 
-  /** EVSE ID for OCPP 2.0 */
+  /** EVSE ID for OCPP 2.0.1 */
   readonly evseId?: number
 
   /** Identifier to authenticate */
@@ -235,7 +235,7 @@ export interface AuthRequest {
 }
 
 /**
- * Certificate hash data for PKI-based authentication (OCPP 2.0+)
+ * Certificate hash data for PKI-based authentication (OCPP 2.0.1)
  */
 export interface CertificateHashData {
   /** Hash algorithm used (SHA256, SHA384, SHA512, etc.) */
@@ -255,13 +255,13 @@ export interface CertificateHashData {
  * Identifier that works across OCPP versions
  */
 export interface Identifier {
-  /** Additional info for OCPP 2.0 tokens */
+  /** Additional info for OCPP 2.0.1 tokens */
   readonly additionalInfo?: Record<string, string>
 
   /** Certificate hash data for PKI-based authentication */
   readonly certificateHashData?: CertificateHashData
 
-  /** Group identifier for group-based authorization (OCPP 2.0) */
+  /** Group identifier for group-based authorization (OCPP 2.0.1) */
   readonly groupId?: string
 
   /** Parent ID for hierarchical authorization (OCPP 1.6) */
@@ -326,9 +326,9 @@ export const isOCPP16Type = (type: IdentifierType): boolean => {
 }
 
 /**
- * Check if identifier type is OCPP 2.0 compatible
+ * Check if identifier type is OCPP 2.0.1 compatible
  * @param type - Identifier type to check
- * @returns True if OCPP 2.0 type
+ * @returns True if OCPP 2.0.1 type
  */
 export const isOCPP20Type = (type: IdentifierType): boolean => {
   return Object.values(OCPP20IdTokenEnumType).includes(type as unknown as OCPP20IdTokenEnumType)
@@ -355,11 +355,11 @@ export const requiresAdditionalInfo = (type: IdentifierType): boolean => {
  * This allows the authentication system to work seamlessly across OCPP 1.6 and 2.0.
  * @remarks
  * **Edge cases and limitations:**
- * - OCPP 2.0 specific statuses (NOT_AT_THIS_LOCATION, NOT_AT_THIS_TIME, PENDING, UNKNOWN)
+ * - OCPP 2.0.1 specific statuses (NOT_AT_THIS_LOCATION, NOT_AT_THIS_TIME, PENDING, UNKNOWN)
  *   map to INVALID when converting to OCPP 1.6
- * - OCPP 2.0 IdToken types have more granularity than OCPP 1.6 IdTag
- * - Certificate-based auth (IdentifierType.CERTIFICATE) is only available in OCPP 2.0+
- * - When mapping to OCPP 2.0, unsupported types default to Local
+ * - OCPP 2.0.1 IdToken types have more granularity than OCPP 1.6 IdTag
+ * - Certificate-based auth (IdentifierType.CERTIFICATE) is only available in OCPP 2.0.1
+ * - When mapping to OCPP 2.0.1, unsupported types default to Local
  */
 
 /**
@@ -390,8 +390,8 @@ export const mapOCPP16Status = (status: OCPP16AuthorizationStatus): Authorizatio
 }
 
 /**
- * Maps OCPP 2.0 authorization status enum to authorization status
- * @param status - OCPP 2.0 authorization status
+ * Maps OCPP 2.0.1 authorization status enum to authorization status
+ * @param status - OCPP 2.0.1 authorization status
  * @returns Authorization status
  * @example
  * ```typescript
@@ -429,8 +429,8 @@ export const mapOCPP20AuthorizationStatus = (
 }
 
 /**
- * Maps OCPP 2.0 token type to identifier type
- * @param type - OCPP 2.0 token type
+ * Maps OCPP 2.0.1 token type to identifier type
+ * @param type - OCPP 2.0.1 token type
  * @returns Identifier type
  * @example
  * ```typescript
@@ -492,9 +492,9 @@ export const mapToOCPP16Status = (status: AuthorizationStatus): OCPP16Authorizat
 }
 
 /**
- * Maps authorization status to OCPP 2.0 RequestStartStopStatus
+ * Maps authorization status to OCPP 2.0.1 RequestStartStopStatus
  * @param status - Authorization status
- * @returns OCPP 2.0 RequestStartStopStatus
+ * @returns OCPP 2.0.1 RequestStartStopStatus
  * @example
  * ```typescript
  * const ocpp20Status = mapToOCPP20Status(AuthorizationStatus.ACCEPTED)
@@ -519,9 +519,9 @@ export const mapToOCPP20Status = (status: AuthorizationStatus): RequestStartStop
 }
 
 /**
- * Maps identifier type to OCPP 2.0 token type
+ * Maps identifier type to OCPP 2.0.1 token type
  * @param type - Identifier type
- * @returns OCPP 2.0 token type
+ * @returns OCPP 2.0.1 token type
  * @example
  * ```typescript
  * const ocpp20Type = mapToOCPP20TokenType(IdentifierType.CENTRAL)
index 2a1f3f07ad47845b619cc813f13d0c828f109153..c4afa98de2bdc12ad79eb59333211b41a5358f9f 100644 (file)
@@ -16,7 +16,7 @@ import { IdentifierType } from '../types/AuthTypes.js'
 const MAX_IDTAG_LENGTH = 20
 
 /**
- * Maximum length for OCPP 2.0 IdToken
+ * Maximum length for OCPP 2.0.1 IdToken
  */
 const MAX_IDTOKEN_LENGTH = 36
 
@@ -72,9 +72,9 @@ function sanitizeIdTag (idTag: unknown): string {
 }
 
 /**
- * Sanitize IdToken for OCPP 2.0 (max 36 characters)
+ * Sanitize IdToken for OCPP 2.0.1 (max 36 characters)
  * @param idToken - Raw IdToken input to sanitize (may be any type)
- * @returns Trimmed and truncated IdToken string conforming to OCPP 2.0 length limit, or empty string for non-string input
+ * @returns Trimmed and truncated IdToken string conforming to OCPP 2.0.1 length limit, or empty string for non-string input
  */
 function sanitizeIdToken (idToken: unknown): string {
   // Return empty string for non-string input
@@ -161,7 +161,7 @@ function validateIdentifier (identifier: unknown): boolean {
   // Check length constraints based on identifier type
   switch (typedIdentifier.type) {
     case IdentifierType.BIOMETRIC:
-    // Fallthrough intentional: all these OCPP 2.0 types share the same validation
+    // Fallthrough intentional: all these OCPP 2.0.1 types share the same validation
     case IdentifierType.CENTRAL:
     case IdentifierType.CERTIFICATE:
     case IdentifierType.E_MAID:
@@ -172,7 +172,7 @@ function validateIdentifier (identifier: unknown): boolean {
     case IdentifierType.MAC_ADDRESS:
     case IdentifierType.MOBILE_APP:
     case IdentifierType.NO_AUTHORIZATION:
-      // OCPP 2.0 types - use IdToken max length
+      // OCPP 2.0.1 types - use IdToken max length
       return (
         isNotEmptyString(typedIdentifier.value) &&
         typedIdentifier.value.length <= MAX_IDTOKEN_LENGTH
index 97ff29f092bdfb3fae60e06ad73b6168d93f1ccd..73f8fa91dc227904461ef67264166ea8b0d7bc1b 100644 (file)
@@ -41,6 +41,7 @@ import {
   resolveUIServerAccess,
   type UIServerAccessCache,
   type UIServerAccessDecision,
+  WILDCARD_HOSTS,
 } from './UIServerAccessPolicy.js'
 import {
   createRateLimiter,
@@ -1322,8 +1323,7 @@ export abstract class AbstractUIServer {
     const allowedHosts = accessPolicy?.allowedHosts ?? []
     const trustedProxies = accessPolicy?.trustedProxies ?? []
     const requireTls = accessPolicy?.requireTlsForNonLoopback ?? true
-    const isWildcard =
-      configuredHost === '' || configuredHost === '0.0.0.0' || configuredHost === '::'
+    const isWildcard = WILDCARD_HOSTS.has(configuredHost)
 
     if (isWildcard && isEmpty(allowedHosts)) {
       logger.warn(
index 3c3d667c70818b8ca630269972b97ce428d77824..078470edcef2ab313682671db22ed4bcc8eb1748 100644 (file)
@@ -350,7 +350,7 @@ export class UIMCPServer extends AbstractUIServer {
 
     if (ocpp16Payload != null && ocpp20Payload != null) {
       return UIMCPServer.createToolErrorResponse(
-        'Cannot provide both ocpp16Payload and ocpp20Payload. Use ocpp16Payload for OCPP 1.6 stations or ocpp20Payload for OCPP 2.0 stations.'
+        'Cannot provide both ocpp16Payload and ocpp20Payload. Use ocpp16Payload for OCPP 1.6 stations or ocpp20Payload for OCPP 2.0.1 stations.'
       )
     }
 
@@ -436,7 +436,7 @@ export class UIMCPServer extends AbstractUIServer {
           )
         } catch {
           logger.warn(
-            `${this.logPrefix(moduleName, 'loadOcppSchemas')} Failed to load OCPP 2.0 schema for ${procedureName}`
+            `${this.logPrefix(moduleName, 'loadOcppSchemas')} Failed to load OCPP 2.0.1 schema for ${procedureName}`
           )
         }
       }
index dc693e78a159c64daa99f593ffbb16afb816c3e6..97244d2b0b43dc067cf2d21e6559ffcf4bf48d7f 100644 (file)
@@ -20,7 +20,7 @@ const FORWARDED_HEADER_NAMES = [
   'x-forwarded-proto',
 ] as const
 const SECURE_FORWARDED_PROTOCOLS = new Set(['https', 'wss'])
-const WILDCARD_HOSTS = new Set(['', '0.0.0.0', '::'])
+export const WILDCARD_HOSTS: ReadonlySet<string> = new Set(['', '0.0.0.0', '::'])
 
 /**
  * Reasons a UI server access decision is denied.
index 6cb5cf8359e78dad0b4e459bfee35ef52bb0aa7f..181a80c25be84153b61cfc7f2dd7d5413d68d400 100644 (file)
@@ -9,11 +9,11 @@ interface RateLimitEntry {
   resetTime: number
 }
 
-export const DEFAULT_MAX_PAYLOAD_SIZE_BYTES = 1048576
+export const DEFAULT_MAX_PAYLOAD_SIZE_BYTES = 1_048_576
 export const DEFAULT_RATE_LIMIT = 100
-export const DEFAULT_RATE_WINDOW_MS = 60000
+export const DEFAULT_RATE_WINDOW_MS = 60_000
 export const DEFAULT_MAX_STATIONS = 100
-export const DEFAULT_MAX_TRACKED_IPS = 10000
+export const DEFAULT_MAX_TRACKED_IPS = 10_000
 export const DEFAULT_COMPRESSION_THRESHOLD_BYTES = 1024
 
 export class PayloadTooLargeError extends BaseError {
index 9fd769e3060665a8e04be48d0a948238c8534c8b..760a6e778c76df8bc7034dc2ab1571a095d0b306 100644 (file)
@@ -36,12 +36,10 @@ export class Constants {
 
   static readonly DEFAULT_AUTH_CACHE_RATE_LIMIT_MAX_REQUESTS = 10
 
-  static readonly DEFAULT_AUTH_CACHE_RATE_LIMIT_WINDOW_MS = 60000
-
+  static readonly DEFAULT_AUTH_CACHE_RATE_LIMIT_WINDOW_MS = 60_000
   static readonly DEFAULT_AUTH_CACHE_TTL_SECONDS = 3600
 
-  static readonly DEFAULT_BOOT_NOTIFICATION_INTERVAL_MS = 60000
-
+  static readonly DEFAULT_BOOT_NOTIFICATION_INTERVAL_MS = 60_000
   static readonly DEFAULT_CIRCULAR_BUFFER_CAPACITY = 386
 
   /** Default coherent MeterValues ramp-up duration in milliseconds. Non-positive values disable the ramp. */
@@ -58,16 +56,13 @@ export class Constants {
 
   static readonly DEFAULT_HASH_ALGORITHM = 'sha384'
 
-  static readonly DEFAULT_HEARTBEAT_INTERVAL_MS = 60000
-
+  static readonly DEFAULT_HEARTBEAT_INTERVAL_MS = 60_000
   static readonly DEFAULT_LOG_STATISTICS_INTERVAL_SECONDS = 60
 
-  static readonly DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL_MS = 60000
-
+  static readonly DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL_MS = 60_000
   static readonly DEFAULT_MESSAGE_TIMEOUT_SECONDS = 30
 
-  static readonly DEFAULT_METER_VALUES_INTERVAL_MS = 60000
-
+  static readonly DEFAULT_METER_VALUES_INTERVAL_MS = 60_000
   static readonly DEFAULT_PERFORMANCE_DIRECTORY = 'performance'
 
   static readonly DEFAULT_PERFORMANCE_RECORDS_DB_NAME = 'e-mobility-charging-stations-simulator'
@@ -139,12 +134,11 @@ export class Constants {
 
   static readonly ENV_SIMULATOR_COLD_START = 'SIMULATOR_COLD_START'
 
-  static readonly MAX_RANDOM_INTEGER = 281474976710655 // 2^48 - 1 (randomInit() limit)
+  static readonly MAX_RANDOM_INTEGER = 281_474_976_710_655 // 2^48 - 1 (randomInit() limit)
 
   // Node.js setInterval/setTimeout maximum safe delay value (2^31-1 ms ≈ 24.8 days)
   // Values exceeding this limit cause Node.js to reset the delay to 1ms
-  static readonly MAX_SETINTERVAL_DELAY_MS = 2147483647
-
+  static readonly MAX_SETINTERVAL_DELAY_MS = 2_147_483_647
   /** Milliseconds per day; equal to `24 * MS_PER_HOUR`. */
   static readonly MS_PER_DAY = DAY_IN_MS
 
@@ -159,10 +153,8 @@ export class Constants {
   /** State of Charge maximum percentage (upper bound for SoC measurand emission). */
   static readonly SOC_MAXIMUM_PERCENT = 100
 
-  static readonly STOP_CHARGING_STATIONS_TIMEOUT_MS = 60000
-
-  static readonly STOP_MESSAGE_SEQUENCE_TIMEOUT_MS = 30000
-
+  static readonly STOP_CHARGING_STATIONS_TIMEOUT_MS = 60_000
+  static readonly STOP_MESSAGE_SEQUENCE_TIMEOUT_MS = 30_000
   /** Divider between base units (A) and centi units (cA). */
   static readonly UNIT_DIVIDER_CENTI = 100
 
index 221b3915d85c0ba18040d00fdbbce9b52e5d2065..1f4aaf28f4daf4702d64f41475a47c8efaae1a80 100644 (file)
@@ -248,7 +248,7 @@ await describe('OCPP16IncomingRequestService — LocalAuthList', async () => {
       assert.strictEqual(response.status, OCPP16UpdateStatus.FAILED)
     })
 
-    await it('should return NotSupported when manager is undefined', () => {
+    await it('should return Failed when manager is undefined (feature supported but unavailable per §5.15)', () => {
       const { station, testableService } = context
       enableLocalAuthListProfile(context)
       setupMockAuthService(station, undefined)
@@ -261,7 +261,7 @@ await describe('OCPP16IncomingRequestService — LocalAuthList', async () => {
 
       const response = testableService.handleRequestSendLocalList(station, request)
 
-      assert.strictEqual(response.status, OCPP16UpdateStatus.NOT_SUPPORTED)
+      assert.strictEqual(response.status, OCPP16UpdateStatus.FAILED)
     })
 
     await it('should accept Full update with empty list to clear all entries', () => {
@@ -311,7 +311,7 @@ await describe('OCPP16IncomingRequestService — LocalAuthList', async () => {
       assert.strictEqual(response.status, OCPP16UpdateStatus.FAILED)
     })
 
-    await it('should return NotSupported when LocalAuthListEnabled is false', () => {
+    await it('should return Failed when LocalAuthListEnabled is false (feature supported but disabled per §5.15)', () => {
       const { station, testableService } = context
       enableLocalAuthListProfile(context)
       upsertConfigurationKey(station, OCPP16StandardParametersKey.LocalAuthListEnabled, 'false')
@@ -326,7 +326,7 @@ await describe('OCPP16IncomingRequestService — LocalAuthList', async () => {
 
       const response = testableService.handleRequestSendLocalList(station, request)
 
-      assert.strictEqual(response.status, OCPP16UpdateStatus.NOT_SUPPORTED)
+      assert.strictEqual(response.status, OCPP16UpdateStatus.FAILED)
     })
 
     await it('should return VersionMismatch for differential update with version <= current', () => {
index e6b49eac37aaee2447dea11fc2bd537b7457b777..2c3b9920d3365c652b3f549e647df5670a7e6a13 100644 (file)
@@ -81,7 +81,7 @@ await describe('OCPP16IncomingRequestService — SimpleHandlers', async () => {
       assert.strictEqual(typeof response.status, 'string')
     })
 
-    await it('should return UnknownMessageId for matching vendor with messageId', () => {
+    await it('should return Accepted for matching vendor with messageId (no messageId registry per §4.3)', () => {
       // Arrange
       const { station, testableService } = context
       const matchingVendor = 'test-vendor-match'
@@ -99,7 +99,7 @@ await describe('OCPP16IncomingRequestService — SimpleHandlers', async () => {
 
       // Assert
       assert.notStrictEqual(response, undefined)
-      assert.strictEqual(response.status, OCPP16DataTransferStatus.UNKNOWN_MESSAGE_ID)
+      assert.strictEqual(response.status, OCPP16DataTransferStatus.ACCEPTED)
       assert.strictEqual(typeof response.status, 'string')
     })
   })
index 677af93ac3777e4855ecea5a03c8ca921d2fcdd1..48d067aa926a756e1593c86e7e1cb4c33c854d1a 100644 (file)
@@ -14,8 +14,8 @@ import {
   AuthorizationStatus,
   IdentifierType,
   OCPPAuthServiceFactory,
-  OCPPAuthServiceImpl,
 } from '../../../../src/charging-station/ocpp/auth/index.js'
+import { OCPPAuthServiceImpl } from '../../../../src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.js'
 import { OCPPVersion } from '../../../../src/types/index.js'
 import { standardCleanup } from '../../../helpers/TestLifecycleHelpers.js'
 import {
index 502b019ced4304eb534daee1f130b105a7721d45..4ab7c67a3db0ad9747c2a1e4bb9ed46f9156fdcc 100644 (file)
@@ -14,8 +14,8 @@ import { OCPP20ServiceUtils } from '../../../../src/charging-station/ocpp/2.0/OC
 import {
   AuthorizationStatus,
   OCPPAuthServiceFactory,
-  OCPPAuthServiceImpl,
 } from '../../../../src/charging-station/ocpp/auth/index.js'
+import { OCPPAuthServiceImpl } from '../../../../src/charging-station/ocpp/auth/services/OCPPAuthServiceImpl.js'
 import {
   OCPP20AuthorizationStatusEnumType,
   OCPP20IdTokenEnumType,
index 95eac4fdaff2f19825cd4d35819f34bfbb24e460..c659ae566b6f85a6ebdf754c1cdb6042dde87fb9 100644 (file)
@@ -44,7 +44,7 @@ await describe('payloadBuilders', async () => {
       assert.deepStrictEqual(result, { idTag: 'RFID123' })
     })
 
-    await it('should build idToken payload for OCPP 2.0', () => {
+    await it('should build idToken payload for OCPP 2.0.1', () => {
       const result = buildAuthorizePayload('RFID123', OCPPVersion.VERSION_20)
       assert.deepStrictEqual(result, {
         idToken: { idToken: 'RFID123', type: OCPP20IdTokenEnumType.ISO14443 },
@@ -294,7 +294,7 @@ await describe('payloadBuilders', async () => {
       assert.strictEqual((result as Record<string, unknown>).errorCode, undefined)
     })
 
-    await it('should build OCPP 2.0 payload with connectorStatus when evseId omitted', () => {
+    await it('should build OCPP 2.0.1 payload with connectorStatus when evseId omitted', () => {
       const result = buildStatusNotificationPayload(
         connectorId,
         OCPP20ConnectorStatusEnumType.OCCUPIED,