Expand description
mako-wim — WiM (Wechselprozesse im Messwesen Strom) process engine for
German smart-meter market communication (BDEW MaKo).
§Process family
WiM governs the switching processes for metering point operators in the German electricity smart-meter rollout, regulated by the MsbG and BDEW WiM process documentation:
| Process | PIDs | Message | Module | EBD |
|---|---|---|---|---|
| Anmeldung MSB (MSBN → NB) | 55042 → 55043/55044 | UTILMD | geraetewechsel | E_0201 |
| Kündigung MSB (MSBN → MSBA) | 55039 → 55040/55041 | UTILMD | geraetewechsel | E_0200 |
| Ende MSB / Abmeldung (MSBA → NB) | 55051 → 55052/55053 | UTILMD | geraetewechsel | E_0202 |
| Verpflichtungsanfrage (NB → gMSB) | 55168 → 55169/55170 | UTILMD | geraetewechsel | E_0240 |
| Weiterverpflichtung (NB → MSBA) | 17002 → 19003/19004 | ORDERS/ORDRSP | weiterverpflichtung | E_0203 |
| Ersteinbau iMS (gMSB → wMSB) | 21029 → 21030/21031 | IFTSTA | ersteinbau | E_0233 |
| Geräteübernahme Bestellung (MSBN → MSBA) | 17001 → 19001/19002 | ORDERS/ORDRSP | geraeteubernahme | E_0247 |
| Anzeige Gerätewechselabsicht (MSBN → MSBA) | 17009 → 19015/19016 | ORDERS/ORDRSP | geraeteubernahme | E_0204 |
| Messlokationsänderung (NB/LF → MSB) | 17011/17118 → 19005/19006 | ORDERS/ORDRSP | technik_aenderung | E_0249/E_0250 |
| Stammdaten Anfrage / Übermittlung | 17132 (req), 17102–17133 (resp) | ORDERS | stammdaten | — |
| Preisanfrage (REQOTE/QUOTES) | 35001/35002/35004/35005 → 15001/15002/15004/15005 | REQOTE, QUOTES | preisanfrage | — |
| Rechnungsabwicklung über den LF | 17005/17006 → 19009/19010 | ORDERS/ORDRSP | rechnungsabwicklung | E_0206/E_0209 |
| Preisliste (PRICAT) | 27001–27003 | PRICAT | preisliste | — |
| ESA Wertebestellung | 35003, 15003, 17007/17008, 39002, 19011–19014 | REQOTE/QUOTES/ORDERS/ORDCHG/ORDRSP | wertebestellung, esa_wertebestellung | — |
| MSB-Rechnung (INVOIC) | 31009 → 33001/33003/33004, 29001 | INVOIC | invoic | — |
| INSRPT Störungsmeldung | 23001 → 23003/23004/23008/23011/23012 | INSRPT | insrpt | — |
§Architecture
Each BDEW process variant is a separate mako_engine::workflow::Workflow
implementation. This crate contains only pure domain logic — no I/O,
no EDIFACT parsing, no network calls.
Parsing and validation of raw EDIFACT bytes must happen at the transport
boundary (AS4 reception layer), before constructing a domain command.
The workflow handle() function receives pre-extracted domain values:
AS4 transport layer
└── parse raw bytes (edi-energy)
└── validate (edi-energy)
└── extract fields (application code)
└── DeviceChangeCommand { pid, melo_id, device_id, … }
└── Process::execute(cmd) ← pure domain logic here§Three clocks, three messages
| Clock | Window | Message |
|---|---|---|
| APERAK — processability | 45 min for Strom UTILMD/ORDERS (APERAK AHB 1.0 §2.4.1) | APERAK BGM+312/313 |
| Antwortfrist — the business decision | 3 / 5 / 7 / 1 Werktage per process, from antwort_frist_werktage(pid) | the Antwort-PID’s UTILMD or ORDRSP |
| Vorlauffrist — was the requested date admissible? | anchored on the date the message carries, mako_fristen::vorlauf | the inbound message itself |
Only the second discharges the Antwortfrist; the APERAK decides nothing.
Where GPKE states its answer windows as clock times on the first Werktag
after the ÜT, WiM states Werktage — see
mako_fristen::antwort::GPKE_IS_NOT_TWENTY_FOUR_HOURS.
§Command construction example
use edi_energy::{AnyMessage, EdiEnergyMessage, Platform};
use mako_wim::geraetewechsel::{WimDeviceChangeWorkflow, DeviceChangeCommand};
let msg = Platform::with_all_profiles().parse(&raw_bytes)?;
let report = msg.validate()?;
let AnyMessage::Utilmd(u) = &msg else { anyhow::bail!("not UTILMD") };
let cmd = DeviceChangeCommand::ReceiveUtilmd {
pid: msg.detect_pruefidentifikator()?,
sender: u.sender().and_then(|n| n.party_id.clone()).unwrap_or_default(),
receiver: u.receiver().and_then(|n| n.party_id.clone()).unwrap_or_default(),
melo_id: u.transactions().first()
.and_then(|t| t.marktlokation()).unwrap_or_default(),
device_id: u.transactions().first()
.and_then(|t| t.device_id().cloned()).unwrap_or_default(),
document_date: u.dtm().iter().find(|d| d.is_document_date())
.and_then(|d| d.value.clone()).unwrap_or_default(),
message_ref: msg.message_ref().to_owned(),
validation_passed: report.is_valid(),
validation_errors: report.errors().iter()
.map(|i| format!("{i}")).collect(),
};
process.execute(cmd).await?;Re-exports§
pub use geraeteubernahme::ANKUENDIGUNG_PIDS as GERAETEUBERNAHME_ANKUENDIGUNG_PIDS;pub use geraeteubernahme::BESTELLUNG_PIDS;pub use geraeteubernahme::GERAETEUBERNAHME_PIDS;pub use geraeteubernahme::GeraeteubernahmeCommand;pub use geraeteubernahme::GeraeteubernahmeData;pub use geraeteubernahme::GeraeteubernahmeEvent;pub use geraeteubernahme::GeraeteubernahmeProjection;pub use geraeteubernahme::GeraeteubernahmeRecord;pub use geraeteubernahme::GeraeteubernahmeRecordData;pub use geraeteubernahme::GeraeteubernahmeState;pub use geraeteubernahme::ORDRSP_DEADLINE_LABEL as GERAETEUBERNAHME_ORDRSP_DEADLINE_LABEL;pub use geraeteubernahme::WORKFLOW_NAME as GERAETEUBERNAHME_WORKFLOW_NAME;pub use geraeteubernahme::WimGeraeteubernahmeWorkflow;pub use geraetewechsel::ANTWORT_FRIST_WINDOW_LABEL as GERAETEWECHSEL_ANTWORT_FRIST_WINDOW_LABEL;pub use geraetewechsel::AUFTRAG_ANTWORT_WINDOW_LABEL;pub use geraetewechsel::DEVICE_CHANGE_ANTWORT_PIDS;pub use geraetewechsel::DEVICE_CHANGE_PIDS;pub use geraetewechsel::DeviceChangeCommand;pub use geraetewechsel::DeviceChangeData;pub use geraetewechsel::DeviceChangeEvent;pub use geraetewechsel::DeviceChangeProjection;pub use geraetewechsel::DeviceChangeRecord;pub use geraetewechsel::DeviceChangeState;pub use geraetewechsel::WORKFLOW_NAME;pub use geraetewechsel::WimDeviceChangeWorkflow;pub use geraetewechsel::antwort_frist_werktage;pub use geraetewechsel::antwort_pid_meaning;pub use insrpt::ANTWORT_WINDOW_LABEL as INSRPT_ANTWORT_WINDOW_LABEL;pub use insrpt::ERGEBNIS_WINDOW_LABEL as INSRPT_ERGEBNIS_WINDOW_LABEL;pub use insrpt::INSRPT_ANFRAGE_PIDS;pub use insrpt::INSRPT_ANTWORT_PIDS;pub use insrpt::INSRPT_ERGEBNIS_PID;pub use insrpt::INSRPT_INFORMATIONS_PIDS;pub use insrpt::Seite as InsrptSeite;pub use insrpt::StoerungsmeldungCommand;pub use insrpt::StoerungsmeldungData;pub use insrpt::StoerungsmeldungEvent;pub use insrpt::StoerungsmeldungState;pub use insrpt::WEITERLEITUNG_WINDOW_LABEL as INSRPT_WEITERLEITUNG_WINDOW_LABEL;pub use insrpt::WORKFLOW_NAME as INSRPT_WORKFLOW_NAME;pub use insrpt::WimInsrptWorkflow;pub use invoic::GasAblehnung;pub use invoic::SETTLEMENT_WINDOW_LABEL as INVOIC_SETTLEMENT_WINDOW_LABEL;pub use invoic::WIM_COMDIS_ABLEHNUNG_PID;pub use invoic::WIM_INVOIC_PIDS;pub use invoic::WIM_REMADV_PIDS;pub use invoic::WORKFLOW_NAME as INVOIC_WORKFLOW_NAME;pub use invoic::WimInvoic;pub use invoic::WimInvoicWorkflow;pub use invoic::gas_ablehnungs_ebd;pub use preisanfrage::PREISANFRAGE_DEADLINE_LABEL;pub use preisanfrage::PreisanfrageCommand;pub use preisanfrage::PreisanfrageData;pub use preisanfrage::PreisanfrageEvent;pub use preisanfrage::PreisanfrageState;pub use preisanfrage::QUOTES_PIDS;pub use preisanfrage::REQOTE_PIDS;pub use preisanfrage::WORKFLOW_NAME as PREISANFRAGE_WORKFLOW_NAME;pub use preisanfrage::WimPreisanfrageWorkflow;pub use preisanfrage::antwort_frist_werktage as preisanfrage_antwort_frist_werktage;pub use preisliste::PRICAT_PIDS;pub use preisliste::PreislisteCommand;pub use preisliste::PreislisteData;pub use preisliste::PreislisteEvent;pub use preisliste::PreislisteState;pub use preisliste::WORKFLOW_NAME as PREISLISTE_WORKFLOW_NAME;pub use preisliste::WimPreislisteWorkflow;pub use rechnungsabwicklung::RECHNUNGSABWICKLUNG_DEADLINE_LABEL;pub use rechnungsabwicklung::RECHNUNGSABWICKLUNG_ORDERS_PIDS;pub use rechnungsabwicklung::RECHNUNGSABWICKLUNG_ORDRSP_PIDS;pub use rechnungsabwicklung::RechnungsabwicklungCommand;pub use rechnungsabwicklung::RechnungsabwicklungData;pub use rechnungsabwicklung::RechnungsabwicklungEvent;pub use rechnungsabwicklung::RechnungsabwicklungState;pub use rechnungsabwicklung::WORKFLOW_NAME as RECHNUNGSABWICKLUNG_WORKFLOW_NAME;pub use rechnungsabwicklung::WimRechnungsabwicklungWorkflow;pub use stammdaten::ANFORDERUNG_PID;pub use stammdaten::STAMMDATEN_DEADLINE_LABEL;pub use stammdaten::StammdatenCommand;pub use stammdaten::StammdatenData;pub use stammdaten::StammdatenEvent;pub use stammdaten::StammdatenProjection;pub use stammdaten::StammdatenRecord;pub use stammdaten::StammdatenRecordData;pub use stammdaten::StammdatenState;pub use stammdaten::UEBERMITTLUNG_PIDS;pub use stammdaten::WORKFLOW_NAME as STAMMDATEN_WORKFLOW_NAME;pub use stammdaten::WimStammdatenWorkflow;pub use steuerungsauftrag::STEUERUNGSAUFTRAG_DEADLINE_LABEL;pub use steuerungsauftrag::SteuerungsCommandType;pub use steuerungsauftrag::SteuerungsauftragCommand;pub use steuerungsauftrag::SteuerungsauftragData;pub use steuerungsauftrag::SteuerungsauftragEvent;pub use steuerungsauftrag::SteuerungsauftragState;pub use steuerungsauftrag::WORKFLOW_NAME as STEUERUNGSAUFTRAG_WORKFLOW_NAME;pub use steuerungsauftrag::WimSteuerungsauftragWorkflow;pub use technik_aenderung::AuftragData as TechnikAenderungAuftragData;pub use technik_aenderung::ORDERS_PIDS as TECHNIK_AENDERUNG_ORDERS_PIDS;pub use technik_aenderung::ORDRSP_PIDS as TECHNIK_AENDERUNG_ORDRSP_PIDS;pub use technik_aenderung::TechnikAenderungCommand;pub use technik_aenderung::TechnikAenderungEvent;pub use technik_aenderung::TechnikAenderungState;pub use technik_aenderung::WORKFLOW_NAME as TECHNIK_AENDERUNG_WORKFLOW_NAME;pub use technik_aenderung::WimTechnikAenderungWorkflow;pub use weiterverpflichtung::ANTWORT_WINDOW_LABEL as WEITERVERPFLICHTUNG_ANTWORT_WINDOW_LABEL;pub use weiterverpflichtung::AUFTRAG_PID as WEITERVERPFLICHTUNG_AUFTRAG_PID;pub use weiterverpflichtung::WEITERVERPFLICHTUNG_PIDS;pub use weiterverpflichtung::WORKFLOW_NAME as WEITERVERPFLICHTUNG_WORKFLOW_NAME;pub use weiterverpflichtung::WeiterverpflichtungCommand;pub use weiterverpflichtung::WeiterverpflichtungData;pub use weiterverpflichtung::WeiterverpflichtungEvent;pub use weiterverpflichtung::WeiterverpflichtungProjection;pub use weiterverpflichtung::WeiterverpflichtungState;pub use weiterverpflichtung::WimWeiterverpflichtungWorkflow;
Modules§
- consent
- ESA consent gating — the Einwilligungs-Vorbedingung of WiM Strom Teil 2 Kapitel 4, expressed as pure domain policy.
- ersteinbau
- WiM Strom Teil 1 Kap. 3.5 — Ersteinbau eines iMS in eine bestehende
Messlokation (IFTSTA 21029 → 21030 / 21031,
E_0233). - esa
- ESA vocabulary — what an Energieserviceanbieter may order, how the messages of the ordering handshake correlate, and which EBD answers which step.
- esa_
wertebestellung - WiM ESA Wertebestellung — ESA origination side.
- geraeteubernahme
- WiM Geräteübernahme und Gerätewechsel — the two ORDERS/ORDRSP legs the abgebender Messstellenbetreiber (MSBA) answers, in both Sparten.
- geraetewechsel
- WiM Messstellenbetrieb — MSB change workflow (PIDs 55039, 55042, 55051, 55168).
- insrpt
- WiM INSRPT Störungsbehebung — the fault report and everything that answers it.
- invoic
- WiM Rechnung — the INVOIC billing processes of WiM Strom and WiM Gas.
- preisanfrage
- WiM Preisanfrage / Angebot — REQOTE/QUOTES price-quote exchange for Messstellenbetreiber device services.
- preisliste
- WiM Preisliste — PRICAT price-list publication by MSB/NB.
- rechnungsabwicklung
- WiM Rechnungsabwicklung des Messstellenbetriebes über den LF — the ORDERS half of the arrangement the REQOTE/QUOTES exchange only negotiates.
- stammdaten
- Geschäftsdatenanfrage — master data request and transmission (ORDERS 17132, answered by 17102–17133).
- steuerungsauftrag
- WiM Steuerungsauftrag — grid control command workflow.
- technik_
aenderung - WiM Strom/Gas Technikänderung — device/measurement-point change requests.
- weiterverpflichtung
- WiM Weiterverpflichtung des MSB — ORDERS 17002 → ORDRSP 19003 / 19004.
- wertebestellung
- WiM ESA Wertebestellung — ordering, cancellation and termination of value delivery to an Energieserviceanbieter.
Structs§
- WimModule
- Engine module for the WiM process family.