Commit 1986b39
authored
* refactor(meter-values): extract CoherentMeterValuesManager (issue #1936)
Extract per-station coherent MeterValues lifecycle owner from
ChargingStation into a dedicated singleton-per-station manager,
mirroring the multiton pattern of AutomaticTransactionGenerator /
IdTagsCache / SharedLRUCache (keyed by stationInfo.hashId).
The manager owns the EV profile file, the active-session Map, and
the create/destroy/inject lifecycle. ChargingStation keeps the four
public methods as thin delegators for API stability — external OCPP
handler call sites, the test helper mock, and existing tests are
unchanged.
Behavior is preserved:
- Sessions are still created only when coherentMeterValues=true AND
a valid EV profile file loads.
- The __injectCoherentSession NODE_ENV production guard is preserved
on the manager side (BaseError throw).
- Session runtime state is disposed at every reset/stop/disconnect
path via destroySession, and at station stop/delete via dispose /
deleteInstance.
Closes issue #1936 item (a).
* refactor(meter-values): thread coherent session, shrink ICoherentContext (issue #1936)
Drop `getCoherentSession` from `ICoherentContext`: the port now
exposes only what the physics chain needs to query about the station
itself. Session lookup moves to the caller (the strategy gate), which
looks up via `ChargingStation.getCoherentSession` — the A1 delegator
that routes through `CoherentMeterValuesManager` in production and
through the test-mock's own Map in tests.
Signature changes:
- `isCoherentModeActive(session): session is CoherentSession` — pure
type guard replacing the two-tier (stationInfo + session-lookup)
predicate. The stationInfo gate is implied by session existence
(sessions are only created via the manager when
`coherentMeterValues=true`).
- `buildCoherentMeterValue(context, session, ...)` — takes the session
directly. The internal `context.getCoherentSession` lookup and the
"missing session" warning branch collapse to a single connector-lookup
guard.
Strategy gate call sites in `OCPPServiceUtils.buildMeterValue` and
`OCPP16ServiceUtils.buildTransactionBeginMeterValue` are threaded
accordingly.
Behavior preserved: sessions are still only routed to the coherent path
when they exist; random/fixed path is untouched. Test suite invariant
`fail: 0, skipped: 6` holds.
Closes issue #1936 item (b).
* refactor(ocpp16): extract buildCoherentTransactionBeginMeterValue (issue #1936)
Split OCPP16ServiceUtils.buildTransactionBeginMeterValue's dual
responsibility. The coherent short-circuit (route through
buildMeterValue with the vendor StartTxnSampledData override) moves to
a named private static helper; the outer function keeps only the
random/fixed default path plus the strategy gate.
The strategy gate in OCPP 1.6 now lives at a single well-labeled
boundary in this file, mirroring the pattern established by
OCPPServiceUtils.buildMeterValue for the periodic MeterValues path.
Behavior is unchanged: same measurand-key resolution, same
buildMeterValue re-dispatch, same TRANSACTION_BEGIN context.
Closes issue #1936 item (e).
* refactor(meter-values): add peekInstance to avoid phantom manager allocation (issue #1936)
PR-A round-1 Oracle review (lanes A + D converged on this MAJOR
finding): the strategy gate in `OCPPServiceUtils.buildMeterValue`
reaches `ChargingStation.getCoherentSession` unconditionally on every
MeterValue tick, and the delegator was routed through
`CoherentMeterValuesManager.getInstance` — a lazy-create factory.
Result: every station that ever emits a MeterValue allocated a
`CoherentMeterValuesManager` + Map, regardless of the `coherentMeterValues`
opt-in flag. This contradicted the file-header design contract that
'stations with the option off never allocate a manager'.
Fix: add `CoherentMeterValuesManager.peekInstance(cs)` — a lookup-only
sibling of `getInstance` that never constructs. Route the four
read/destroy/dispose sites through `peekInstance`:
- `ChargingStation.createCoherentSession` (opt-in station's manager
is warmed up at initialize; non-opt-in stations return `undefined`
without allocating).
- `ChargingStation.destroyCoherentSession`
- `ChargingStation.getCoherentSession` (the strategy-gate hot path).
- `ChargingStation.stop()` finally-block dispose.
Keep `getInstance` (create-if-missing) at:
- `ChargingStation.__injectCoherentSession` — production-guard
BaseError throw must remain reachable if this test seam is
accidentally called in prod.
- `ChargingStation.initialize` — the opt-in eager warm-up that surfaces
EV-profile-file warnings at startup rather than at first transaction.
Also tighten `getInstance` JSDoc to state precisely that `undefined` is
returned iff `stationInfo.hashId` is not yet resolved (the sole failure
mode), and cross-reference `peekInstance` for read paths.
Behavior for opt-in stations is unchanged (warm-up allocates the same
manager, subsequent reads find it in the cache). Non-opt-in stations
no longer allocate. Test invariant `fail: 0, skipped: 6` holds.
Closes issue #1936 item (a) sub-finding from round-1 review.
* docs(meter-values): tighten getInstance/peekInstance JSDoc post round-2 review (issue #1936)
PR-A round-2 Oracle review (lanes A + B converged on JSDoc precision):
- Lane A: `getInstance` JSDoc still listed `createSession` as a caller,
but the round-1 fix routed `createSession` through `peekInstance`.
A future reader following the JSDoc could reintroduce the phantom-
allocation bug at the exact site it was just patched.
- Lane B: JSDoc labeled `destroySession` and `stop()` dispose as
"read-only paths". Both are actually mutations (Map.delete +
PRNG-closure disposal). Reword to "paths that must not allocate a
manager on behalf of non-opt-in stations".
- Lane A NIT: the eager-init invariant is load-bearing — if
`ChargingStation.initialize` stops warming up the manager for opt-in
stations, `createCoherentSession` silently no-ops. The invariant was
implicit; make it explicit in `peekInstance` JSDoc.
Docs-only. No runtime change. Test invariant `fail: 0, skipped: 6` holds.
* fix(meter-values): propagate template reload to coherent manager (issue #1936)
The template file watcher and reset() path already flush sharedLRUCache,
idTagsCache, and OCPPAuthServiceFactory before calling initialize(). The
coherent manager was left behind: getInstance returned the cached
manager for the unchanged hashId, whose evProfiles were snapshotted at
first-construction, so runtime mutations of evProfilesFile or its file
contents did not take effect until process restart.
Wire reloadEvProfiles() into every initialize() invocation so template
mutations propagate. Symmetrically drop the manager entirely when the
coherentMeterValues opt-in flips true to false so cached sessions and
stale profile data do not leak across the config change.
* refactor(meter-values): gate injectSession, harmonize banner and JSDoc (issue #1936)
injectSession test seam now honors the stationInfo.coherentMeterValues
opt-in gate. Pre-refactor, isCoherentModeActive checked the flag on
every tick; post-refactor it trusts session existence. Without the gate,
a test could inject a session on a non-opt-in station and activate the
coherent wire path — contradicting the type-guard precondition.
Production is already protected by the NODE_ENV production throw.
Align copyright banner to the sibling verbatim form
('Partial Copyright Jerome Benoit. 2021-2025').
Harmonize JSDoc: peekInstance MUST-use list now includes createSession
(mirrors getInstance JSDoc); reloadEvProfiles fail-soft description
covers non-opt-in stations and any error; 'cache miss' prose replaced
with 'instances-map miss' to match the field name.
* refactor(meter-values): preserve in-flight coherent sessions across opt-in flag flip (issue #1936)
The previous initialize() logic dropped the CoherentMeterValuesManager
via deleteInstance() when coherentMeterValues flipped true to false.
That immediately disposed every in-flight coherent session — and, in
the template file watcher flow where initialize() runs before
stopAutomaticTransactionGenerator(), that produced mixed-provenance
TransactionEvent frames on OCPP 2.0.x: the Begin frame was coherent,
the Update/End frames after the flip fell through the strategy gate
to random path, and the CSMS saw a discontinuous transaction.
Remove the else branch. New sessions are already blocked by the
coherentMeterValues gate in createSession; existing in-flight
sessions drain naturally via destroyCoherentSession on transaction
end. Provenance is preserved for transactions started before the
flag flip, and the reloadEvProfiles propagation on the true branch
(the round-1 MAJOR fix) is unaffected.
* docs(meter-values): calibrate injectSession JSDoc for mock-helper reality (issue #1936)
The prior JSDoc claimed the opt-in guard means isCoherentModeActive
cannot be tricked into activating the coherent wire path by
injecting on a non-opt-in station. That is true only for the
production-backed injection path (through the real
CoherentMeterValuesManager.injectSession). Tests that mock
ChargingStation write to their own session store bypassing this
seam entirely, so the mock is responsible for enforcing its own
opt-in invariant where relevant. Calibrate the JSDoc scope
accordingly.
1 parent b907992 commit 1986b39
8 files changed
Lines changed: 387 additions & 139 deletions
File tree
- src/charging-station
- meter-values
- ocpp
- 1.6
- tests/charging-station/meter-values
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
| |||
110 | 112 | | |
111 | 113 | | |
112 | 114 | | |
| 115 | + | |
113 | 116 | | |
114 | 117 | | |
115 | 118 | | |
| |||
132 | 135 | | |
133 | 136 | | |
134 | 137 | | |
135 | | - | |
136 | 138 | | |
137 | 139 | | |
138 | 140 | | |
| |||
148 | 150 | | |
149 | 151 | | |
150 | 152 | | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | | - | |
159 | 153 | | |
160 | 154 | | |
161 | 155 | | |
| |||
215 | 209 | | |
216 | 210 | | |
217 | 211 | | |
218 | | - | |
219 | | - | |
220 | 212 | | |
221 | 213 | | |
222 | 214 | | |
| |||
252 | 244 | | |
253 | 245 | | |
254 | 246 | | |
255 | | - | |
256 | 247 | | |
257 | 248 | | |
258 | 249 | | |
| |||
316 | 307 | | |
317 | 308 | | |
318 | 309 | | |
319 | | - | |
320 | | - | |
321 | | - | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
322 | 313 | | |
323 | 314 | | |
324 | 315 | | |
325 | 316 | | |
326 | 317 | | |
327 | | - | |
328 | | - | |
329 | | - | |
330 | | - | |
331 | | - | |
332 | | - | |
| 318 | + | |
333 | 319 | | |
334 | 320 | | |
335 | 321 | | |
| |||
377 | 363 | | |
378 | 364 | | |
379 | 365 | | |
380 | | - | |
381 | | - | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
382 | 374 | | |
383 | 375 | | |
384 | 376 | | |
| |||
388 | 380 | | |
389 | 381 | | |
390 | 382 | | |
391 | | - | |
392 | | - | |
393 | | - | |
394 | | - | |
395 | | - | |
396 | | - | |
397 | | - | |
398 | | - | |
399 | | - | |
400 | | - | |
401 | | - | |
402 | | - | |
403 | | - | |
404 | | - | |
405 | | - | |
406 | | - | |
407 | | - | |
408 | | - | |
409 | | - | |
410 | | - | |
411 | | - | |
| 383 | + | |
412 | 384 | | |
413 | 385 | | |
414 | 386 | | |
| |||
428 | 400 | | |
429 | 401 | | |
430 | 402 | | |
| 403 | + | |
431 | 404 | | |
432 | 405 | | |
433 | 406 | | |
| |||
467 | 440 | | |
468 | 441 | | |
469 | 442 | | |
470 | | - | |
471 | | - | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
472 | 448 | | |
473 | 449 | | |
474 | 450 | | |
475 | 451 | | |
476 | | - | |
477 | | - | |
478 | | - | |
479 | | - | |
480 | | - | |
| 452 | + | |
481 | 453 | | |
482 | 454 | | |
483 | 455 | | |
| |||
540 | 512 | | |
541 | 513 | | |
542 | 514 | | |
543 | | - | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
544 | 521 | | |
545 | 522 | | |
546 | 523 | | |
547 | 524 | | |
548 | | - | |
| 525 | + | |
549 | 526 | | |
550 | 527 | | |
551 | 528 | | |
| |||
1314 | 1291 | | |
1315 | 1292 | | |
1316 | 1293 | | |
1317 | | - | |
1318 | | - | |
1319 | | - | |
1320 | | - | |
| 1294 | + | |
1321 | 1295 | | |
1322 | 1296 | | |
1323 | 1297 | | |
| |||
1911 | 1885 | | |
1912 | 1886 | | |
1913 | 1887 | | |
1914 | | - | |
| 1888 | + | |
| 1889 | + | |
| 1890 | + | |
| 1891 | + | |
| 1892 | + | |
| 1893 | + | |
| 1894 | + | |
| 1895 | + | |
| 1896 | + | |
| 1897 | + | |
| 1898 | + | |
| 1899 | + | |
1915 | 1900 | | |
1916 | 1901 | | |
1917 | 1902 | | |
| |||
1942 | 1927 | | |
1943 | 1928 | | |
1944 | 1929 | | |
1945 | | - | |
1946 | | - | |
1947 | | - | |
1948 | | - | |
1949 | | - | |
1950 | | - | |
1951 | | - | |
1952 | | - | |
1953 | | - | |
1954 | | - | |
1955 | | - | |
1956 | | - | |
1957 | | - | |
1958 | | - | |
1959 | | - | |
1960 | | - | |
1961 | | - | |
1962 | | - | |
1963 | | - | |
1964 | | - | |
1965 | | - | |
1966 | | - | |
1967 | | - | |
1968 | | - | |
1969 | | - | |
1970 | | - | |
1971 | | - | |
1972 | 1930 | | |
1973 | 1931 | | |
1974 | 1932 | | |
| |||
0 commit comments