]>
Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commit
refactor(charging-station,meter-values): close #1936 (M-08 + f-2..f-5 + M-09) (#1949)
* refactor(tests): split StationHelpers.ts into modular files (issue #1936 M-08)
Split the 962-LOC monolithic StationHelpers.ts into five focused modules:
- StationHelpers.types.ts (149 LOC): 7 interfaces + MockChargingStation type
- StationHelpers.cleanup.ts (160 LOC): cleanup + reset helpers
- StationHelpers.connector.ts (65 LOC): connector-status + EVSE-usage helpers
- StationHelpers.template.ts (20 LOC): mock template factory
- StationHelpers.factory.ts (607 LOC): createMockChargingStation factory
StationHelpers.ts is now a pure re-export barrel preserving the public API,
so all 77 consumer test files keep importing from './StationHelpers.js'
without change. Mechanical move only: no logic, signature, or behavior
change.
determineEvseUsage is now exported (previously module-private) because it
is consumed by the factory across module boundaries.
* feat(meter-values): add powerFactor + rampShape to EvProfile (issue #1936 M-09)
Two optional EvProfile refinements. Both default to current behavior so
existing profiles and golden tests are unchanged.
- powerFactor (0, 1], absent => 1: cos phi factor. Per-phase current
becomes I = P / (V . phases . powerFactor); INV-1 extends to
P_active = V.I.phases.powerFactor.
- rampShape 'linear'|'sigmoid', absent => 'linear': sigmoid uses a
shifted-scaled logistic (k=10) pinned at f(0)=0 and f(1)=1 exactly
after endpoint normalization.
* refactor(charging-station): extract charging-profile helpers to HelpersChargingProfile (#1936 f-2)
Split Helpers.ts (Phase 2 of 5). Moves 14 charging-profile symbols into
sibling HelpersChargingProfile.ts and re-exports the public ones from
Helpers.ts so the barrel import path is preserved. Zero behavior change:
same signatures, same code, same log content. The moduleName local in
the new file switches log prefixes from 'Helpers.<fn>' to
'HelpersChargingProfile.<fn>', reflecting the actual source location.
Moved (private inside new file):
- getChargingStationChargingProfiles, buildChargingProfilesLimit,
ChargingProfilesLimit interface, getChargingProfileId,
getChargingProfilesLimit, canProceedRecurringChargingProfile,
prepareRecurringChargingProfile, checkRecurringChargingProfileDuration
Moved (exported from new file):
- getChargingStationChargingProfilesLimit, getConnectorChargingProfiles,
getConnectorChargingProfilesLimit, prepareChargingProfileKind,
canProceedChargingProfile (all re-exported from Helpers.ts)
- getSingleChargingSchedule (exported for Phase 3 direct import; not
re-exported from Helpers.ts to keep the public API unchanged)
Follows the barrel pattern established by HelpersReservation.ts in
Phase 1 (PR #1946).
* refactor(charging-station): extract connector-status helpers to HelpersConnectorStatus (#1936 f-3)
Split Helpers.ts (Phase 3 of 5). Moves 8 connector-status symbols into
sibling HelpersConnectorStatus.ts and re-exports the public ones from
Helpers.ts so the barrel import path is preserved. Zero behavior change:
same signatures, same code, same log content. The moduleName local in
the new file switches log prefixes from 'Helpers.<fn>' to
'HelpersConnectorStatus.<fn>', reflecting the actual source location.
Moved (private inside new file):
- initializeConnectorStatus
Moved (exported from new file, re-exported from Helpers.ts):
- buildConnectorsMap, initializeConnectorsMapStatus, resetConnectorStatus,
resetAuthorizeConnectorStatus, prepareConnectorStatus,
checkStationInfoConnectorStatus, getBootConnectorStatus
Follows the barrel pattern established by HelpersReservation.ts in
Phase 1 (PR #1946).
* refactor(charging-station): extract serial-number/id helpers to HelpersId (#1936 f-4)
Extracts 4 identity helpers (getChargingStationId, getHashId, createSerialNumber,
propagateSerialNumber) plus the ChargingStationNameTemplate type alias into a
dedicated HelpersId.ts. The getRandomSerialNumberSuffix helper had a single
internal caller (createSerialNumber) and no external consumer; it is demoted to
module-private in the new file, matching its actual usage.
Helpers.ts continues to re-export the 4 public symbols and the type alias via
the barrel, so all 5 consumer files work unchanged.
Refs #1936
* refactor(charging-station): extract config/template helpers to HelpersConfig (#1936 f-5)
Extracts 10 configuration/template helpers (buildTemplateName, validateStationInfo,
getDefaultConnectorMaximumPower, checkConfiguration, setChargingStationOptions,
stationTemplateToStationInfo, getAmperageLimitationUnitDivider, getDefaultVoltageOut,
getIdTagsFile, getEvProfilesFile) plus the two template-topology helpers
getMaxNumberOfEvses and getMaxNumberOfConnectors into HelpersConfig.ts.
The two template-topology helpers were originally listed as core-station helpers
in the issue body, but getDefaultConnectorMaximumPower consumes both of them;
moving them into HelpersConfig avoids a cross-module dependency back into
Helpers.ts that would risk an ESM live-binding cycle. Helpers.ts imports
getMaxNumberOfConnectors from HelpersConfig for use in the retained
getConfiguredMaxNumberOfConnectors, closing the split with no cycle.
Helpers.ts is now 185 LOC (core station helpers + 5 barrel re-exports),
down from 1389 LOC before the phased split.
Refs #1936
* [autofix.ci] apply automated fixes
* fix(charging-station,meter-values): apply Round 1 review fixes to #1949 (issue #1936)
1. HelpersConnectorStatus.ts imported getMaxNumberOfConnectors via the
Helpers.js barrel, which re-exports it from HelpersConfig.js while
also re-exporting HelpersConnectorStatus.js. That is the exact ESM
barrel cycle the config/template extraction was designed to avoid.
Import getMaxNumberOfConnectors directly from HelpersConfig.js to
keep the dependency direction one-way.
2. EvProfile powerFactor Zod schema tightened from (0, 1] to [0.5, 1].
The previous positive() lower bound accepted tiny values that made
the divisor V*phases*powerFactor collapse toward zero and produce
non-physical current. Real onboard chargers sit at 0.98..1.0; the
0.5 floor blocks configuration errors while keeping physically
defensible room. Reflected in JSDoc, README, and the schema.
3. powerFactor is now AC-only. DC has no reactive component (P=V*I),
so applying cos phi on DC profiles produces non-physical current.
Gate on session.currentType === CurrentType.AC when computing the
divisor; DC pins powerFactor to 1 regardless of the profile field.
Test locks the DC-gate contract with an explicit regression case.
4. sigmoidRamp JSDoc reworded. Endpoint bit-exactness is pinned by
the short-circuit paths at progress <= 0 and >= 1; the shift-scale
normalization aligns interior open-interval values. The prior
wording overstated what the normalization does on its own.
5. StationHelpers.ts barrel comment reworded to describe the public
surface directly instead of framing it as backward compatibility.
Refs #1936
* fix(charging-station,meter-values): apply Round 2 review fixes to #1949 (issue #1936)
1. INV-1 DC docstring stale: CoherentSampleComputer.ts @file block said
DC: P = V*I*powerFactor but the implementation and README both pin
powerFactor to 1 on DC (no reactive component in DC). Corrected to
DC: P = V*I and cross-referenced the AC-only gate.
2. resolvePhasedValue non-emission of OCPP measurands documented:
Power.Factor and Power.Reactive.Import are defined in OCPP 1.6 and
2.0.1 measurand enums but the coherent path does not emit them.
EvProfile.powerFactor scales the AC current/power chain internally
but is not surfaced as Power.Factor. Templates configuring these
measurands under coherent mode are skipped with a warning. The
JSDoc now lists the supported measurand set and calls out the
unsupported ones explicitly.
3. propagateSerialNumber (public export via Helpers barrel) gained a
JSDoc block with @throws {BaseError}, matching the coverage pattern
established for HelpersConfig.ts public exports.
4. buildChargingProfilesLimit (module-private but non-trivial) gained
a JSDoc block with @throws {BaseError} for parallelism.
5. Test describe block renamed from 'rampShape sigmoid' to
'rampShape sigmoid' with the literal quoted, matching the string-
literal-type convention used everywhere else for rampShape values.
Refs #1936
* docs(charging-station): complete R2 findings coverage on helper split (#1936)
Applies design decisions for all remaining R2 findings on PR #1949:
1. JSDoc completeness (Lane A M-R2.2 + Lane B M-R2.6): every public
export re-exported through the Helpers.ts barrel now carries a
symbol-level JSDoc block matching the HelpersConfig.ts style
established during Phase 5 extraction. Coverage: 3 exports in
HelpersId.ts (getChargingStationId, getHashId, createSerialNumber),
5 exports in HelpersChargingProfile.ts (getSingleChargingSchedule,
getChargingStationChargingProfilesLimit,
getConnectorChargingProfilesLimit, prepareChargingProfileKind,
canProceedChargingProfile), and 7 exports in HelpersConnectorStatus.ts
(getBootConnectorStatus, checkStationInfoConnectorStatus,
buildConnectorsMap, initializeConnectorsMapStatus,
resetAuthorizeConnectorStatus, resetConnectorStatus,
prepareConnectorStatus). ChargingStationNameTemplate type also
documented.
2. Barrel style divergence (Lane A N-R2.1): the two barrels use
different re-export shapes on purpose. Helpers.ts uses explicit
name lists to control and document the external API surface;
StationHelpers.ts uses 'export *' because its consumers are
internal to the test tree. A one-line justification comment now
lives in each barrel so the divergence is intentional-by-record.
3. HelpersId moduleName absence (Lane A N-R2.3): every helper in
this file is pure (no logger.* calls), so declaring an unused
moduleName constant would violate the no-dead-code convention.
Documented as an explicit design decision in the @file block.
Refs #1936
* docs(charging-station): rewrite R2b JSDoc blocks that misdescribed the code (#1936)
R3 review caught 5 R2b JSDoc blocks whose narrative did not match the
actual code path. Every block is rewritten to reflect current behavior:
1. getSingleChargingSchedule: the code returns undefined for ANY array
shape (OCPP 2.0.x), not just for zero-or-multi-entry arrays. Doc now
states arrays are logged (debug) and skipped, only the OCPP 1.6
single-schedule shape is consumed. The pre-existing OCPP 2.0.x
array-shape handling gap is out of scope for this PR.
2. getChargingStationChargingProfilesLimit: the code filters
CHARGE_POINT_MAX_PROFILE (OCPP 1.6 value). Doc now uses that name
explicitly and calls out that the OCPP 2.0.1 equivalent
ChargingStationMaxProfile is not handled by this filter. Also
out-of-scope pre-existing behavior.
3. getBootConnectorStatus: doc claimed a fallback to Available, but
the code returns Unavailable when the station or connector is
unavailable. Doc now enumerates the four branches explicitly
(Unavailable / persisted status / bootStatus / Available).
4. initializeConnectorsMapStatus: doc had two independent errors.
Connector 0 is mutated (availability=Operative, chargingProfiles
default to empty), not left untouched. And initializeConnectorStatus
runs on the transactionStarted-unset branch, not on the pending
flag branch. Doc now describes all four actual branches.
5. resetConnectorStatus: doc said energy counters (plural); only the
transaction-scoped register is reset. Doc said connector identity
(id, availability) is preserved; ConnectorStatus has no id field,
only the outer Map key. Doc now says transaction-scoped energy
counter and preserves availability only.
Also replaced {@link initializeConnectorStatus} references with prose
since the target is module-private and would emit unresolved-reference
warnings from TypeDoc.
Refs #1936
* fix(charging-station): close R3 findings — OCPP 2.0.x profile support + Readonly type (#1936)
Applies the 3 remaining R3 findings as proper code fixes (not docs-only):
1. getSingleChargingSchedule unwraps length-1 OCPP 2.0.x arrays.
Previously any array shape returned undefined, so all 8 callers
silently dropped OCPP 2.0.x profiles at limit-resolution and
preparation time. The fix unwraps chargingSchedule[0] when the
array has exactly one entry (the OCPP 2.0.x single-schedule shape),
and keeps the debug log + skip path for zero or multi-entry arrays
(2-3 concurrent schedules are valid per spec but the coherent path
does not pick between them). JSDoc rewritten to describe the
post-fix contract. 4 regression tests added: OCPP 1.6 shape,
length-1 unwrap, length-2 skip, empty-array skip.
2. getChargingStationChargingProfilesLimit filter now accepts both
ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE (OCPP 1.6
value 'ChargePointMaxProfile') and
ChargingProfilePurposeType.ChargingStationMaxProfile (OCPP 2.0.1
value 'ChargingStationMaxProfile'). Previously only the OCPP 1.6
value matched, so OCPP 2.0.1 station-scope profiles were excluded
from station-level limit resolution. JSDoc updated.
3. ChargingStationNameTemplate is now Readonly<Pick<...>>. The type
was already used read-only by getChargingStationId; the Readonly
wrapper makes the pure-read contract explicit at the type level.
Note: findings 1 and 2 are pre-existing behavior gaps exposed by the
R2b/R3 JSDoc accuracy audit, not regressions introduced by the
refactor. Fixing them here rather than deferring to a follow-up issue
matches the session precedent (PR-J F06.FR.06, PR-K
RegisterValuesWithoutPhases, PR-H physics refinements).
Refs #1936
* fix(charging-station): close R4 findings, add station-scope filter regression tests (#1936)
R4 review surfaced two convergent findings on the R3b station-scope
profile filter fix:
1. N-R4.1: the R3b filter accepted CHARGE_POINT_MAX_PROFILE (OCPP 1.6)
and ChargingStationMaxProfile (OCPP 2.0.1), but OCPP 2.0.1 has a
second station-scope purpose ChargingStationExternalConstraints
(OCA J01 use-case: external LMS/EMS-imposed caps). The local
validator at OCPP20IncomingRequestService.ts:4157-4164 already
treats it identically to ChargingStationMaxProfile (must apply to
EVSE 0). Filter now accepts all three station-scope purposes.
JSDoc updated to describe the OCA J01 semantic distinction and the
coherent path's decision to treat both OCPP 2.0.1 purposes as
equivalent inputs to stack-level tie-break.
2. N-R4.3: R3b Fix #2 had no regression test locking the filter
acceptance. 4 targeted tests added covering the 3 station-scope
purposes (CHARGE_POINT_MAX_PROFILE, ChargingStationMaxProfile,
ChargingStationExternalConstraints) plus a negative TX_PROFILE
rejection. Tests seed connector 0's chargingProfiles directly via
MockChargingStation and assert getChargingStationChargingProfilesLimit
returns the expected watt limit or undefined.
Refs #1936
* docs(charging-station): apply Round 5 review fixes to #1949 (issue #1936)
R5 review surfaced one MAJOR spec-citation error and three test-quality
MINORs. Only actionable items are fixed here; prior R4b commit body is
immutable per session convention (squash-safe branch history).
1. M1 spec citation: OCA J01 in R4b JSDoc is factually wrong. J01 is the
Metering functional block (Sending Meter Values not related to a
transaction). ChargingStationExternalConstraints belongs to the K
SmartCharging block: K04 for the internal Load Balancing use-case
(matches ChargingStationMaxProfile semantic) and K11-K14 for the
External Charging Limit family that the station reports to the CSMS
(matches ChargingStationExternalConstraints semantic). JSDoc rewrites
the citation to K04 vs K11-K14.
2. n2 test coverage: getSingleChargingSchedule regression tests missed
the OCPP 2.0.1 spec upper bound of a length-3 chargingSchedule array
(spec cardinality is 1..3). Added one test verifying the length-3
case returns undefined (matching the current always-skip-non-length-1
behavior).
3. n3 test coverage: the R4b station-scope filter tests all seeded a
single profile at stackLevel 0, missing (a) tie-break correctness
between two station-scope profiles at different stack levels and (b)
filter + purpose interaction when a station-scope and a TX_PROFILE
are both seeded on connector 0. Added two tests covering both cases.
4. n1 test setup rationale: the seed helper reassigns station.stationInfo
after mock construction. This only reaches code paths that read
chargingStation.stationInfo directly (the maximumPower sanity check);
factory methods like getNumberOfPhases and getVoltageOut capture the
constructor-time options in a closure and do not observe the mutation.
Tests use ChargingRateUnitType.WATT to bypass AC/DC conversion, so
currentOutType is not exercised. Added an inline comment locking the
design decision so future contributors know why the mutation is inert
for phase/voltage getters.
Renamed the seed helper from seedConnectorZeroWithProfile (singular) to
seedConnectorZeroWithProfiles (plural) to fit the multi-profile cases.
Refs #1936
* docs(charging-station): refine R5b OCA citation with K08.FR.04 merge behavior (#1936)
R6 review surfaced one MINOR precision gap in the R5b OCA citation and
one false-positive on issue linkage (rejected — issue #1936 IS the
correct closing target per session-verified reconciliation of 11
coordinated PRs).
R5b already fixed OCA J01 (Metering) to OCA K04 (Internal LMS) and
OCA K11-K14 (External Charging Limit). R6 Lane A + Lane B convergent
on a semantic distinction: K11-K14 describes the profile's ORIGIN
(external system sets the limit, station stores it internally), not
the coherent path's BEHAVIOR (composite-schedule merge input). The
authoritative citation for the merge-input behavior is OCA K08.FR.04
(Get Composite Schedule) and safety invariant SC.01. R5b also called
ChargingStationExternalConstraints an LMS/EMS-imposed cap the
station reports to the CSMS — accurate for the reporting flow via
NotifyChargingLimit / ReportChargingProfiles, but the code path uses
the profile as merge input rather than emit-source.
JSDoc now cites both K11-K14 (origin: external system sets limit,
station stores internally, reports upstream to CSMS) and K08.FR.04 /
SC.01 (behavior: composite-schedule merge input consumed by the
coherent path).
Refs #1936
* test(charging-station,meter-values): apply Round 7 test-strengthening fixes to #1949 (#1936)
R7 review surfaced two valid regression-proof gaps in the test suite
(other Lane B claims rejected as too-strict interpretation of boundary
and purpose-guard tests). Only actionable strengthening applied here.
1. Sigmoid ramp test at CoherentMeterValues.test.ts:1782+ only pinned
endpoints (0, 1) via short-circuit and midpoint (0.5) via symmetry —
all three points collapse to the same values on linear ramp. A
silent revert of the sigmoid branch back to linear would pass the
assertion. Added two interior-curvature assertions at rampUpDurationMs/4
and 3*rampUpDurationMs/4 where sigmoid (k=10) emits ~0.07 and ~0.93
respectively vs linear 0.25 and 0.75. Tolerance 0.15/0.85 sits well
below the ~0.18 sigmoid-vs-linear gap, so the sigmoid feature is now
contractually locked at interior points.
2. Readonly contract on ChargingStationNameTemplate had no regression
lock. Added a compile-time assertion via @ts-expect-error on a
mutation attempt. If the Readonly<Pick<...>> wrapper is silently
removed, tsc --noEmit fails with 'Unused @ts-expect-error directive'
at build time. Runtime tolerates the mutation (Readonly is
compile-time only); the compile-time directive is the regression lock.
Refs #1936
* docs(charging-station): fix HelpersId JSDoc warnings on createSerialNumber (#1936)
pnpm format surfaced 3 lint warnings on HelpersId.ts:createSerialNumber:
- cspell/spellchecker: unknown word 'uppercased'
- jsdoc/require-param-description: missing params.randomSerialNumber description
- jsdoc/require-param-description: missing params.randomSerialNumberUpperCase description
Rewrote the JSDoc to spell 'upper case' with a space (standard English)
and added explicit descriptions for the two nested params. pnpm format
and pnpm lint are now warning-free.
Refs #1936
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
18 files changed: