Skip to main content

Crate mako_wim

Crate mako_wim 

Source
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:

ProcessPIDsMessageModuleEBD
Anmeldung MSB (MSBN → NB)55042 → 55043/55044UTILMDgeraetewechselE_0201
Kündigung MSB (MSBN → MSBA)55039 → 55040/55041UTILMDgeraetewechselE_0200
Ende MSB / Abmeldung (MSBA → NB)55051 → 55052/55053UTILMDgeraetewechselE_0202
Verpflichtungsanfrage (NB → gMSB)55168 → 55169/55170UTILMDgeraetewechselE_0240
Weiterverpflichtung (NB → MSBA)17002 → 19003/19004ORDERS/ORDRSPweiterverpflichtungE_0203
Ersteinbau iMS (gMSB → wMSB)21029 → 21030/21031IFTSTAersteinbauE_0233
Geräteübernahme Bestellung (MSBN → MSBA)17001 → 19001/19002ORDERS/ORDRSPgeraeteubernahmeE_0247
Anzeige Gerätewechselabsicht (MSBN → MSBA)17009 → 19015/19016ORDERS/ORDRSPgeraeteubernahmeE_0204
Messlokationsänderung (NB/LF → MSB)17011/17118 → 19005/19006ORDERS/ORDRSPtechnik_aenderungE_0249/E_0250
Stammdaten Anfrage / Übermittlung17132 (req), 17102–17133 (resp)ORDERSstammdaten
Preisanfrage (REQOTE/QUOTES)35001/35002/35004/35005 → 15001/15002/15004/15005REQOTE, QUOTESpreisanfrage
Rechnungsabwicklung über den LF17005/17006 → 19009/19010ORDERS/ORDRSPrechnungsabwicklungE_0206/E_0209
Preisliste (PRICAT)27001–27003PRICATpreisliste
ESA Wertebestellung35003, 15003, 17007/17008, 39002, 19011–19014REQOTE/QUOTES/ORDERS/ORDCHG/ORDRSPwertebestellung, esa_wertebestellung
MSB-Rechnung (INVOIC)31009 → 33001/33003/33004, 29001INVOICinvoic
INSRPT Störungsmeldung23001 → 23003/23004/23008/23011/23012INSRPTinsrpt

§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

ClockWindowMessage
APERAK — processability45 min for Strom UTILMD/ORDERS (APERAK AHB 1.0 §2.4.1)APERAK BGM+312/313
Antwortfrist — the business decision3 / 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::vorlaufthe 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.