eebus

Home / Docs / Certification

Certification

The 203 abstract test cases of the four High-Level Test Specifications as data, a coverage number from cargo test, and the runtime signals a laboratory reads off a device.

Certification for LPC, LPP, MPC and MGCP is a paid laboratory booking. What the laboratory runs is published: each use case has a High-Level Test Specification listing its abstract test cases — an identifier, the requirements it covers, which actor is under test, and whether it is mandatory.

This crate carries all 203 of them as data, drives every one of them, and prints what that adds up to.

The catalogue

use eebus::conformance::{self, Coverage};
use eebus::usecases::descriptor::{Support, actors, names};

let mandatory: Vec<_> = conformance::for_actor(names::LPC, actors::CONTROLLABLE_SYSTEM)
    .filter(|tc| tc.level == Support::Mandatory)
    .collect();

let claimed = ["ATC_LPC_COM_PT_CSTransition5_001"];
let report = Coverage::of(mandatory.iter().copied(), &claimed);
println!("{}%", report.percent());

Each AbstractTestCase carries the specification's own description, the [LPC-TS-…] requirements it covers, and the conditions that raise an optional marking to mandatory — "if the device is black-start capable", "if the unlimited/autonomous state is implemented", "if the phase is one the device actually operates on". A device's parameter sheet is those conditions answered, so the same list tells a manufacturer what it has committed to.

A coverage number from cargo test

tests/conformance.rs drives this crate through the abstract test cases' own steps, one test per identifier, and prints what it adds up to:

$ cargo test --features full --test conformance -- --nocapture coverage

LPC   ControllableSystem      39/43   90%
LPC   EnergyGuard              5/8    62%
LPP   ControllableSystem      39/43   90%
LPP   EnergyGuard              5/8    62%
MPC   MonitoredUnit           20/20  100%
MPC   MonitoringAppliance     34/34  100%
MGCP  GridConnectionPoint     18/18  100%
MGCP  MonitoringAppliance     29/29  100%

      total                  189/203  93%

The fourteen missing are exactly the seven device-level test cases, counted once for LPC and once for LPP; a test holds the gap to that set, so the number moves neither by dropping a case nor by relabelling one. The Energy Guard's 62 % is the same arithmetic, not a shortfall: three of its eight cases are about a device rebooting. Each is listed with the reason:

Not covered here, and why (the same list for LPP):
  CSConnection_006     what the appliance actually draws, which the library reports a
                       ceiling for and does not control
  CSConnection_009     powering the device off and on again
  CSInit_002           a factory reset and the defaults it restores are the device's
  CSInit_003           where values are stored across a power cut is the device's decision
  EGConnection_001     the test rebuilds the actor rather than rebooting a device

Each is an obligation on the integrator. conformance::device_level() yields them with their reasons, so a harness that drives a real device — one that can cut its power and press its reset — can iterate exactly those cases, filtered by actor and by what the parameter sheet commits to. The suite asserts the list matches the number.

use eebus::conformance;
use eebus::usecases::descriptor::actors;

for (case, reason) in conformance::device_level()
    .filter(|(case, _)| case.dut == actors::CONTROLLABLE_SYSTEM)
{
    println!("{}: {}\n  because {reason}", case.id, case.description);
}

The catalogue is behind the default-on conformance feature, which a build with default-features = false has to name.

What a passing suite still cannot see

Both ends of every test in this repository are this crate, and that hides a whole class of defect: anything where the two sides agree on a convention the specification left to the device. Identifiers are the sharp edge. limitId, the failsafe keyIds, MGCP's curtailment key and a car's per-phase limits are all placeholders — <l1#(1..1)>, <k1#(1..1)>, <x1>…<x3> — and a client that assumes its own numbering writes a well-formed message to a real entry of the peer's and is acknowledged — and every abstract test case still passes.

Two suites cover it. tests/interop.rs runs against a live eebus-go peer, and tests/foreign_identifiers.rs builds a Controllable System by hand that numbers everything differently — its limit on 7, its failsafe keys on 5 and 6, and both directions on one LoadControl feature so that limitId 1 is the production limit an LPC guard must not touch. Neither is a conformance test; both catch what conformance cannot.

The report prints one softer case alongside them: the library notifies a change the moment the application publishes it (SPINE IG §2.4), but whether the measured value reaches it inside the 120 seconds MPC and MGCP allow is the application's own publish cadence.

The tester hooks

Every High-Level Test Specification carries the same footnote: "the manufacturer must specify conditions on how the test case can be tested (e.g. via debug interface)." Half of the LPC transitions are timers — a duration expiring, a heartbeat that stops — and nothing goes on the wire when one fires, so a tester that can only see the wire cannot tell unlimited/controlled from failsafe state.

usecases::signals is that interface, as a shape rather than a transport:

use eebus::usecases::signals::Signals;
use eebus::usecases::lpc;

println!("{}", controllable_system.signals(lpc::DIRECTION));
lpc:state             limited
lpc:limit             3000 W
lpc:duration          600 s
lpc:isActive          true
lpc:failsafeLimit     4200 W
lpc:failsafeDuration  7200 s
lpc:nominalMax        11000 W
lpc:contractualMax    -
lpc:effectiveLimit    3000 W
lpc:powerCeiling      3000 W
lpc:lastHeartbeat     11 s
lpc:nextDeadline      130 s

Three details are deliberate:

  • The state is spelled the way §2.3.2 spells it — unlimited/controlled, not UnlimitedControlled — because a laboratory compares it against the specification and against the CF_CS_… pre-conditions, not against Rust.
  • An unset value is -, never 0. A tester reading nought watts for a limit that was never set would record a device limited to zero, which is the opposite of unlimited.
  • nextDeadline is there because the timed transitions are exactly what the wire cannot show. ATC_LPC_COM_PT_CSTransition5_001 waits 120 seconds for one.

A Monitored Unit reports the same way under mpc: or mgcp:, and a measurand whose value state is not normal reports the state instead of a number — which is the whole subject of the NT_ test cases in those two specifications.

What this does not claim

A passing test here does not mean a pass in the laboratory — only that the rules are the ones the laboratory checks, under the same names, and that what is not checked is written down.