Add feedback-aligned material efficiency KPI

This commit is contained in:
2026-09-06 05:45:11 +02:00
parent 0856629e77
commit c411581a1a
5 changed files with 524 additions and 4 deletions
+74 -3
View File
@@ -297,9 +297,80 @@ finished-product width:
it belongs to the MAHLO measurement system and observed values differ from nominal
article widths. Structured ERP product master data would be preferred when available.
These helpers are independent of database access and are not yet wired into live
processing. No normalized context model, kg/m² or material-efficiency calculation,
persistence, polling, runner, timeseries, or Grafana changes are introduced here.
These helpers are independent of database access. The service below composes them
for material efficiency; no background processing is attached.
## Feedback-aligned material efficiency
`MaterialEfficiencyService` in `service.material_efficiency` combines ERP good area
with cumulative material consumption for one exact production order. Its
`evaluate_current()` reads the existing ERP workplace adapter once; `evaluate(status)`
evaluates an already supplied `CurrentWorkplaceStatus`. Both return an immutable
`MaterialEfficiencySnapshot` or `None` when inputs are unavailable.
`PostgresMaterialSnapshotRepository(settings).latest_at_or_before(...)` in
`service.postgres_material` takes keyword arguments `calculation_id`, `machine_id`,
`production_order`, and an aware `timestamp`. A parameterized query selects the
latest snapshot **at or before ERP feedback time**, matching calculation, machine,
and mapped order exactly. It returns `MaterialConsumptionSnapshot` or `None`.
Run IDs do not restrict this lookup: stored consumption is already cumulative
across the order's runs. The service neither reintegrates nor sums snapshots and
does not bridge run boundaries.
The result includes workplace/machine/calculation identifiers, both order identifiers,
article context, nominal width, both original source timestamps, good area, aligned
consumption, `material_consumption_kg_per_m2 = consumption_kg / good_quantity_m2`,
and `material_consumption_g_per_m2 = material_consumption_kg_per_m2 * 1000`.
Nominal width comes from generic ERP description parsing and is context only;
ERP good square metres remain authoritative even when width cannot be parsed.
Missing ERP status, missing aligned material, missing/non-positive/non-finite good
area, invalid consumption (negative or non-finite), or non-finite computed ratios
produce `None`. Zero consumption is valid. Configuration errors, invalid timestamp
alignment, and database failures raise rather than masquerading as missing data.
All machine/workplace settings are supplied explicitly. Example using the existing
K7 material calculation (with previously constructed ERP gateway and PG settings):
```python
from zoneinfo import ZoneInfo
from production_analytics.service.material_efficiency import MaterialEfficiencyService
from production_analytics.service.postgres_material import PostgresMaterialSnapshotRepository
service = MaterialEfficiencyService(
erp_gateway, PostgresMaterialSnapshotRepository(postgres_settings),
workplace="K7",
machine_id="c220f95c-a65e-4cb7-99b7-0626d6c7508c",
calculation_id="k7-fiber-consumption",
format_template="K 7-{production_order}",
# Supply only after verifying the ERP timestamp's source timezone:
erp_timezone=ZoneInfo("Europe/Berlin"),
)
result = service.evaluate_current()
```
Aware ERP timestamps are compared as UTC instants. Naive ERP timestamps require
explicit `erp_timezone`; there is no inferred system/database timezone. Ambiguous
or nonexistent local times during DST transitions are rejected. The original ERP
timestamp (including naivety) and selected material timestamp are preserved in the
result; retain the source timezone configuration alongside it for interpretation.
This alignment avoids knowingly including future material consumption, but does
not eliminate ERP roll-feedback timing uncertainty (feedback may be roughly one
roll ahead or otherwise offset). Live values are plausibility indicators; final
production-order values are more meaningful as relative timing error diminishes.
There is no interpolation, lag correction, smoothing, freshness threshold, or
estimated timestamp. Historical bootstrap snapshots are usable only when their
stored timestamps satisfy the cutoff; a later cumulative total cannot reconstruct
an earlier value. Reliable final evaluation requires retaining final ERP feedback;
the current-workplace adapter alone does not provide historical completed orders.
Call on changed ERP feedback; no new runner or polling loop is provided. Results
are returned in memory without modifying earlier results or material snapshots.
No schema, Grafana, or timeseries changes are needed. Daily per-machine 24h reporting
and aggregation remain future work. Repository SQL selection tests use an in-memory
SQLite fixture with driver transport adaptation; they require no live PostgreSQL.
## Peak-cycle detection