Skip to content

feat(domain): model Annex VII state-of-health parameters - #56

Merged
LKSNDRTMLKV merged 1 commit into
mainfrom
feat/annex-vii-state-of-health
Jul 25, 2026
Merged

feat(domain): model Annex VII state-of-health parameters#56
LKSNDRTMLKV merged 1 commit into
mainfrom
feat/annex-vii-state-of-health

Conversation

@LKSNDRTMLKV

Copy link
Copy Markdown
Member

Item 1 of the six left open by the citation audit. Replaces the flat stateOfHealthPct with the Annex VII Part A parameter sets, and fixes a disclosure defect found on the way.

The disclosure defect

stateOfHealthPct was not in the battery sector's disclosure map at all, so it fell through to the default and was served publicly.

Annex XIII point 4(b) puts "information on the state of health of the battery pursuant to Article 14" in the individual-battery set, which Art. 77(2)(c) grants only to persons with a legitimate interest — and Art. 77(2)(b) does not grant to notified bodies or market surveillance. Both stateOfHealth and the deprecated stateOfHealthPct now carry the individual class added in #53. This is that class's first real use.

The model

Annex VII Part A is two disjoint lists, so StateOfHealth is a sum type, not a struct of optionals — a flat struct would make "an EV battery with an ohmic resistance but no SOCE" representable, which the annex does not permit.

Category Parameters
Electric vehicle state of certified energy (SOCE) — and nothing else
Stationary storage + LMT remaining capacity, evolution of self-discharging rates, plus power capability / round trip efficiency / ohmic resistance

The optionality is the annex's own wording, not a modelling choice. Items 1 and 4 of the stationary list are unconditional; items 2, 3 and 5 are each qualified "where possible". Items 1 and 4 are therefore f64 and the rest Option<f64>, and a payload omitting either required item fails to deserialize.

dpp_rules::batteries::degradation gains annex_vii_parameter_set_for, mapping a battery type onto the applicable list. That module was a placeholder; this is its first real rule. Portable and SLI map to None — Art. 14(1) names only stationary storage, LMT and EV batteries.

One documented imprecision: Art. 3 defines a stationary battery energy storage system as a subset of industrial batteries, and batteryType cannot distinguish them. "industrial" maps to the stationary list, which is the conservative direction — an industrial battery outside Annex VII Part A merely has the parameters treated as expected rather than surplus.

Schema

New battery v2.2.0, now currentSchemaVersion. v1.0.0–v2.1.0 stay registered so existing passports remain verifiable. stateOfHealth is a oneOf over the two parameter sets, each with additionalProperties: false.

That last detail matters: serde ignores unknown fields, so the guarantee that an EV payload cannot smuggle in a stationary parameter lives in the schema, not the type. There is a test asserting it at that layer rather than pretending the type enforces it.

Not in this PR

Annex VII Part B (expected lifetime: energy throughput, capacity throughput, harmful events, full equivalent cycles). It overlaps the existing expectedLifetimeCycles and the EFC work already in dpp-rules, and deserves its own pass.

Verification

just check green: 813 tests, plugin suites included. just build-plugins green.

Nine new tests, including round-trip coverage of both parameter sets, the required/optional split, the schema-level rejection of a mixed payload, and one asserting the individual classification withholds state of health from authorities.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 17 complexity · 2 duplication

Metric Results
Complexity 17
Duplication 2

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR successfully models the Annex VII state-of-health parameter sets as requested, utilizing a sum type to differentiate between EV and Stationary/LMT configurations. The implementation correctly handles disclosure classifications and conservative mapping for industrial batteries.

While Codacy reports the PR is up to standards, two main issues require attention: a value mismatch between the sector catalog and the schema enum for 'SLI' batteries, and a lack of range-validation in the plugin layer for the new percentage fields. There is also a notable amount of code duplication in test utilities that should be centralized to simplify future domain model migrations.

About this PR

  • There is a systemic pattern of duplicating BatteryData and Passport construction logic across benches, crates/dpp-tests, and dpp-domain. Consider centralizing these into a shared factory utility (e.g., under a test-utils feature in dpp-domain) to reduce the manual effort required for future domain model updates.

Test suggestions

  • StateOfHealth enum correctly round-trips through JSON serialization/deserialization for both EV and Stationary sets.
  • Stationary/LMT parameter set fails to deserialize if unconditional parameters (capacity or self-discharge) are missing.
  • Mapping logic in dpp-rules correctly identifies the required parameter set based on battery type (EV, LMT, Industrial, vs others).
  • Disclosure classification enforcement: assert that 'individual' class data is hidden from Public and Authority audiences.
  • Schema-level validation: reject an EV battery payload that attempts to include stationary-specific parameters.

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

"industrial",
"ev",
"lmt",
"sli"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM RISK

The product category 'sli' in the sector catalog is inconsistent with the value 'starting-lighting-ignition' defined in the v2.2.0 JSON schema enum. These should be aligned to ensure that values derived from the catalog pass schema validation.

Comment thread benches/src/aas.rs
recycled_content_lithium_pct: Some(6.0),
recycled_content_nickel_pct: Some(9.0),
state_of_health_pct: Some(100.0),
state_of_health: None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ LOW RISK

Suggestion: The BatteryData and Passport construction here is duplicated from crates/dpp-tests/tests/battery_end_to_end.rs. Centralizing these sample constructors into a shared factory module in the domain crate would ensure consistency and reduce the labor required for future schema evolutions.

Try running the following prompt in your IDE agent:

Extract the battery passport construction logic from benches/src/aas.rs and crates/dpp-tests/tests/battery_end_to_end.rs into a shared factory function within a test-utility module in crates/dpp-domain.

See Clone in Codacy

Comment thread benches/src/validation.rs
recycled_content_lithium_pct: None,
recycled_content_nickel_pct: None,
state_of_health_pct: None,
state_of_health: None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ LOW RISK

Suggestion: This BatteryData construction is duplicated from crates/dpp-domain/src/test_support.rs. Consider exposing sample_battery_data as a public test utility in dpp-domain (e.g., under a test-utils feature) to avoid boilerplate duplication across crates.

Try running the following prompt in your IDE agent:

Move sample_battery_data in crates/dpp-domain/src/test_support.rs to a public module or expose it via a test-utils feature in dpp-domain, then refactor benches/src/validation.rs to use it instead of a local struct literal.

See Clone in Codacy

@LKSNDRTMLKV
LKSNDRTMLKV merged commit 7118dd6 into main Jul 25, 2026
11 checks passed
@LKSNDRTMLKV
LKSNDRTMLKV deleted the feat/annex-vii-state-of-health branch July 25, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant