Skip to main content

tp_lib_core/models/
path_metadata.rs

1//! Path calculation metadata
2
3use serde::{Deserialize, Serialize};
4
5use crate::models::AssociatedNetElement;
6
7/// Algorithm configuration and diagnostic metadata
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct PathMetadata {
10    /// Distance scale parameter used for probability calculation
11    pub distance_scale: f64,
12
13    /// Heading scale parameter used for probability calculation
14    pub heading_scale: f64,
15
16    /// Cutoff distance for candidate selection (meters)
17    pub cutoff_distance: f64,
18
19    /// Heading difference cutoff (degrees)
20    pub heading_cutoff: f64,
21
22    /// Probability threshold for path segment inclusion
23    pub probability_threshold: f64,
24
25    /// Resampling distance applied (meters), None if disabled
26    pub resampling_distance: Option<f64>,
27
28    /// Whether fallback mode was used
29    pub fallback_mode: bool,
30
31    /// Number of candidate paths evaluated
32    pub candidate_paths_evaluated: usize,
33
34    /// Whether path existed in both directions (bidirectional validation)
35    pub bidirectional_path: bool,
36
37    /// Snapshot of segment-level diagnostics (order, intrinsics, probabilities)
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub diagnostic_info: Option<PathDiagnosticInfo>,
40}
41
42/// Collection of segment-level diagnostics for a calculated path
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct PathDiagnosticInfo {
45    /// Ordered diagnostics per segment in the path
46    pub segments: Vec<SegmentDiagnostic>,
47}
48
49impl PathDiagnosticInfo {
50    /// Build diagnostics from associated netelements, preserving traversal order
51    pub fn from_segments(segments: &[AssociatedNetElement]) -> Self {
52        let segments = segments
53            .iter()
54            .map(|segment| SegmentDiagnostic {
55                netelement_id: segment.netelement_id.clone(),
56                probability: segment.probability,
57                start_intrinsic: segment.start_intrinsic,
58                end_intrinsic: segment.end_intrinsic,
59                gnss_start_index: segment.gnss_start_index,
60                gnss_end_index: segment.gnss_end_index,
61            })
62            .collect();
63
64        Self { segments }
65    }
66}
67
68/// Diagnostic details for a single segment in a train path
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct SegmentDiagnostic {
71    /// ID of the netelement
72    pub netelement_id: String,
73
74    /// Probability assigned to this segment
75    pub probability: f64,
76
77    /// Intrinsic coordinate where the path enters this segment
78    pub start_intrinsic: f64,
79
80    /// Intrinsic coordinate where the path exits this segment
81    pub end_intrinsic: f64,
82
83    /// Index of the first GNSS position associated with this segment
84    pub gnss_start_index: usize,
85
86    /// Index of the last GNSS position associated with this segment
87    pub gnss_end_index: usize,
88}
89
90#[cfg(test)]
91mod tests;