#![deny(unsafe_code)]
#![deny(missing_docs)]
#![warn(clippy::pedantic)]
pub mod ack_forward;
pub mod aktivierung;
pub mod ausfallarbeit;
pub mod bilarem;
pub mod fristen;
pub mod router;
pub mod stammdaten;
pub use router::{RedispatchDocumentKind, RedispatchRouter};
use mako_engine::{builder::EngineModule, pid_router::PidRouter, profile::ProfileRequirement};
pub struct RedispatchModule;
impl RedispatchModule {
#[must_use]
pub fn build_router() -> RedispatchRouter {
let mut router = RedispatchRouter::new();
router.register(
RedispatchDocumentKind::Activation,
aktivierung::WORKFLOW_NAME,
);
router.register(
RedispatchDocumentKind::PlannedResourceSchedule,
ack_forward::planungsdaten::WORKFLOW_NAME,
);
router.register(
RedispatchDocumentKind::Stammdaten,
stammdaten::WORKFLOW_NAME,
);
router.register(
RedispatchDocumentKind::StatusRequest,
ack_forward::statusanfrage::WORKFLOW_NAME,
);
router.register(
RedispatchDocumentKind::Unavailability,
ack_forward::verfuegbarkeit::WORKFLOW_NAME,
);
router.register(
RedispatchDocumentKind::Kaskade,
ack_forward::kaskade::WORKFLOW_NAME,
);
router.register(
RedispatchDocumentKind::NetworkConstraint,
ack_forward::netzengpass::WORKFLOW_NAME,
);
router.register(
RedispatchDocumentKind::Kostenblatt,
ack_forward::kostenblatt::WORKFLOW_NAME,
);
router
}
}
impl EngineModule for RedispatchModule {
fn name(&self) -> &'static str {
"redispatch"
}
fn workflow_names(&self) -> &'static [&'static str] {
&[
stammdaten::WORKFLOW_NAME,
aktivierung::WORKFLOW_NAME,
ack_forward::verfuegbarkeit::WORKFLOW_NAME,
ack_forward::netzengpass::WORKFLOW_NAME,
ack_forward::kaskade::WORKFLOW_NAME,
ack_forward::planungsdaten::WORKFLOW_NAME,
ack_forward::statusanfrage::WORKFLOW_NAME,
ack_forward::kostenblatt::WORKFLOW_NAME,
]
}
fn register_pids(&self, router: &mut PidRouter) {
for &pid in aktivierung::IFTSTA_PIDS {
router.register(pid, aktivierung::WORKFLOW_NAME);
}
for &pid in aktivierung::MSCONS_PIDS {
router.register(pid, aktivierung::WORKFLOW_NAME);
}
for &pid in aktivierung::ORDERS_PIDS {
router.register(pid, aktivierung::WORKFLOW_NAME);
}
}
fn profile_requirements(&self) -> &'static [ProfileRequirement] {
&[
ProfileRequirement {
message_type: "IFTSTA",
label: "IFTSTA Redispatch 2.0 (21037 Ansicht NB, 21038 Ansicht BTR)",
},
ProfileRequirement {
message_type: "MSCONS",
label: "MSCONS Redispatch (13021 meteorologische Daten, 13022 Einzelzeitreihe Ausfallarbeit)",
},
ProfileRequirement {
message_type: "ORDERS",
label: "ORDERS Redispatch (17209 Anforderung Ausfallarbeit)",
},
]
}
fn configure(&self) -> Result<(), String> {
let router = Self::build_router();
for dk in [
RedispatchDocumentKind::Activation,
RedispatchDocumentKind::PlannedResourceSchedule,
RedispatchDocumentKind::Stammdaten,
RedispatchDocumentKind::StatusRequest,
RedispatchDocumentKind::Unavailability,
RedispatchDocumentKind::NetworkConstraint,
RedispatchDocumentKind::Kaskade,
RedispatchDocumentKind::Kostenblatt,
] {
router.route(dk).map_err(|e| format!("redispatch: {e}"))?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_router_covers_all_primary_doc_types() {
let router = RedispatchModule::build_router();
for dk in [
RedispatchDocumentKind::Activation,
RedispatchDocumentKind::PlannedResourceSchedule,
RedispatchDocumentKind::Stammdaten,
RedispatchDocumentKind::StatusRequest,
RedispatchDocumentKind::Unavailability,
RedispatchDocumentKind::Kaskade,
RedispatchDocumentKind::NetworkConstraint,
RedispatchDocumentKind::Kostenblatt,
] {
assert!(
router.is_registered(dk),
"RedispatchDocumentKind {dk:?} must be registered in RedispatchModule router"
);
}
assert!(
!router.is_registered(RedispatchDocumentKind::Acknowledgement),
"Acknowledgement must not be in the document-kind router"
);
}
#[test]
fn configure_succeeds() {
assert!(RedispatchModule.configure().is_ok());
}
#[test]
fn iftsta_pids_are_correct() {
assert_eq!(aktivierung::IFTSTA_PIDS, &[21_037, 21_038]);
}
#[test]
fn mscons_pids_are_correct() {
assert_eq!(aktivierung::MSCONS_PIDS, &[13_021, 13_022]);
}
#[test]
fn no_mabis_or_hkn_pid_is_claimed() {
let claimed: Vec<u32> = aktivierung::IFTSTA_PIDS
.iter()
.chain(aktivierung::MSCONS_PIDS)
.chain(aktivierung::ORDERS_PIDS)
.copied()
.collect();
for pid in [13_020_u32, 13_023, 17_210, 17_211, 19_204] {
assert!(!claimed.contains(&pid), "{pid} is a MaBiS PID");
}
for pid in [19_301_u32, 19_302] {
assert!(!claimed.contains(&pid), "{pid} is an HKN-R PID");
}
assert!(!claimed.contains(&13_026));
}
#[test]
fn the_ack_frist_is_three_minutes_everywhere() {
assert_eq!(fristen::ACK_FRIST, time::Duration::minutes(3));
}
#[test]
fn workflow_names_are_non_empty() {
assert!(!RedispatchModule.workflow_names().is_empty());
for name in RedispatchModule.workflow_names() {
assert!(
name.starts_with("redispatch-"),
"workflow name '{name}' must start with 'redispatch-'"
);
}
}
}