sumo_types/additional/
reader.rs1use super::domain::Additional;
7use crate::Result;
8use crate::schema;
9use crate::xml::{read_document, read_document_at};
10use std::io::BufRead;
11use std::path::Path;
12
13pub fn read_additional(path: &Path) -> Result<Additional> {
21 read_document_at::<schema::AdditionalType, _>(path, "additional", "SUMO additional file")
22}
23
24pub fn read_additional_from(source: impl BufRead) -> Result<Additional> {
32 read_document::<schema::AdditionalType, _, _>(source, "additional", "SUMO additional file")
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38 use crate::additional::domain::{DetectorId, LaneCoverage, LanePosition, LaneRef, Point};
39 use uom::si::f64::Length;
40 use uom::si::length::meter;
41 use uom::si::time::second;
42
43 const SAMPLE_ADDITIONAL: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
46<additional>
47 <e1Detector id="loop0" lane="e0_0" pos="12.50" period="60.00" file="out/loop0.xml"/>
48 <e2Detector id="area0" lane="e0_0" pos="0.00" endPos="50.00" file="out/area0.xml"/>
49 <e3Detector id="zone0" file="out/zone0.xml" pos="42.00,7.00">
50 <detEntry lane="e0_0" pos="0.00"/>
51 <detEntry lane="e1_0" pos="0.00"/>
52 <detExit lane="e0_0" pos="100.00"/>
53 </e3Detector>
54</additional>
55"#;
56
57 #[test]
58 fn reads_one_of_each_detector_kind_end_to_end() {
59 let additional = read_additional_from(SAMPLE_ADDITIONAL.as_bytes()).unwrap();
60
61 assert_eq!(additional.induction_loops.len(), 1);
62 let loop0 = &additional.induction_loops[0];
63 assert_eq!(loop0.id, DetectorId("loop0".into()));
64 assert_eq!(loop0.lane, LaneRef("e0_0".into()));
65 assert_eq!(
66 loop0.position,
67 LanePosition::FromStart(Length::new::<meter>(12.5))
68 );
69 assert_eq!(loop0.period.unwrap().get::<second>(), 60.0);
70
71 assert_eq!(additional.lane_area_detectors.len(), 1);
72 let area0 = &additional.lane_area_detectors[0];
73 assert_eq!(
74 area0.coverage,
75 Some(LaneCoverage::SingleLane(LaneRef("e0_0".into())))
76 );
77 assert_eq!(
78 area0.end_position,
79 Some(LanePosition::FromStart(Length::new::<meter>(50.0)))
80 );
81
82 assert_eq!(additional.entry_exit_detectors.len(), 1);
83 let zone0 = &additional.entry_exit_detectors[0];
84 assert_eq!(zone0.entries.len(), 2);
85 assert_eq!(zone0.exits.len(), 1);
86 assert_eq!(
87 zone0.exits[0].position,
88 LanePosition::FromStart(Length::new::<meter>(100.0))
89 );
90 assert_eq!(
91 zone0.icon_position,
92 Some(Point {
93 x: 42.0,
94 y: 7.0,
95 z: 0.0
96 })
97 );
98 }
99
100 #[test]
101 fn treats_sumos_alternative_element_names_as_the_same_detector() {
102 let xml = r#"<additional>
106 <inductionLoop id="loop0" lane="e0_0" pos="1.00" file="a.xml"/>
107 <laneAreaDetector id="area0" lane="e0_0" file="b.xml"/>
108 <entryExitDetector id="zone0" file="c.xml"><detEntry lane="e0_0" pos="0.00"/></entryExitDetector>
109 </additional>"#;
110
111 let additional = read_additional_from(xml.as_bytes()).unwrap();
112
113 assert_eq!(additional.induction_loops.len(), 1);
114 assert_eq!(additional.lane_area_detectors.len(), 1);
115 assert_eq!(additional.entry_exit_detectors.len(), 1);
116 }
117
118 #[test]
119 fn ignores_element_kinds_this_crate_does_not_model() {
120 let xml = r#"<additional>
121 <busStop id="stop0" lane="e0_0" startPos="0.00" endPos="20.00"/>
122 <e1Detector id="loop0" lane="e0_0" pos="1.00" file="a.xml"/>
123 </additional>"#;
124
125 let additional = read_additional_from(xml.as_bytes()).unwrap();
126
127 assert_eq!(additional.induction_loops.len(), 1);
128 assert!(additional.lane_area_detectors.is_empty());
129 }
130
131 #[test]
132 fn rejects_unrelated_root_elements() {
133 let error = read_additional_from(b"<not-additional/>".as_slice()).unwrap_err();
137 assert!(
138 error.to_string().contains("<not-additional>"),
139 "error should name the offending root element, got: {error}"
140 );
141 }
142
143 #[test]
144 fn accepts_an_empty_but_correctly_rooted_file() {
145 let additional = read_additional_from(b"<additional/>".as_slice()).unwrap();
146 assert_eq!(additional, Additional::default());
147 }
148
149 #[test]
150 fn rejects_a_non_finite_position() {
151 let xml =
152 r#"<additional><e1Detector id="l" lane="e0_0" pos="NaN" file="a.xml"/></additional>"#;
153 assert!(read_additional_from(xml.as_bytes()).is_err());
154 }
155}