Skip to main content

traci_rs/scopes/
simulation.rs

1// SPDX-License-Identifier: EPL-2.0
2//! TraCI Simulation domain scope.
3
4use crate::{
5    client::TraciClient,
6    constants::*,
7    error::TraciError,
8    storage::Storage,
9    types::{
10        ContextSubscriptionResults, SubscriptionResults, TraciPosition, TraciRoadPosition,
11        TraciStage,
12    },
13};
14
15/// Scope for querying and controlling the SUMO simulation.
16#[derive(Debug, Default)]
17pub struct SimulationScope {
18    pub subscription_results: SubscriptionResults,
19    pub context_subscription_results: ContextSubscriptionResults,
20}
21
22impl SimulationScope {
23    crate::impl_scope_accessors!();
24
25    // -----------------------------------------------------------------------
26    // Simple getters
27    // -----------------------------------------------------------------------
28
29    pub fn get_current_time(&self, client: &mut TraciClient) -> Result<i32, TraciError> {
30        client.create_command(CMD_GET_SIM_VARIABLE, VAR_TIME_STEP, "", None);
31        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
32        client.read_int_from_input()
33    }
34
35    pub fn get_time(&self, client: &mut TraciClient) -> Result<f64, TraciError> {
36        client.create_command(CMD_GET_SIM_VARIABLE, VAR_TIME, "", None);
37        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_DOUBLE))?;
38        client.read_double_from_input()
39    }
40
41    pub fn get_loaded_number(&self, client: &mut TraciClient) -> Result<i32, TraciError> {
42        client.create_command(CMD_GET_SIM_VARIABLE, VAR_LOADED_VEHICLES_NUMBER, "", None);
43        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
44        client.read_int_from_input()
45    }
46
47    pub fn get_loaded_id_list(&self, client: &mut TraciClient) -> Result<Vec<String>, TraciError> {
48        client.create_command(CMD_GET_SIM_VARIABLE, VAR_LOADED_VEHICLES_IDS, "", None);
49        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_STRINGLIST))?;
50        client.read_string_list_from_input()
51    }
52
53    pub fn get_departed_number(&self, client: &mut TraciClient) -> Result<i32, TraciError> {
54        client.create_command(CMD_GET_SIM_VARIABLE, VAR_DEPARTED_VEHICLES_NUMBER, "", None);
55        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
56        client.read_int_from_input()
57    }
58
59    pub fn get_departed_id_list(&self, client: &mut TraciClient) -> Result<Vec<String>, TraciError> {
60        client.create_command(CMD_GET_SIM_VARIABLE, VAR_DEPARTED_VEHICLES_IDS, "", None);
61        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_STRINGLIST))?;
62        client.read_string_list_from_input()
63    }
64
65    pub fn get_arrived_number(&self, client: &mut TraciClient) -> Result<i32, TraciError> {
66        client.create_command(CMD_GET_SIM_VARIABLE, VAR_ARRIVED_VEHICLES_NUMBER, "", None);
67        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
68        client.read_int_from_input()
69    }
70
71    pub fn get_arrived_id_list(&self, client: &mut TraciClient) -> Result<Vec<String>, TraciError> {
72        client.create_command(CMD_GET_SIM_VARIABLE, VAR_ARRIVED_VEHICLES_IDS, "", None);
73        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_STRINGLIST))?;
74        client.read_string_list_from_input()
75    }
76
77    pub fn get_starting_teleport_number(&self, client: &mut TraciClient) -> Result<i32, TraciError> {
78        client.create_command(CMD_GET_SIM_VARIABLE, VAR_TELEPORT_STARTING_VEHICLES_NUMBER, "", None);
79        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
80        client.read_int_from_input()
81    }
82
83    pub fn get_starting_teleport_id_list(&self, client: &mut TraciClient) -> Result<Vec<String>, TraciError> {
84        client.create_command(CMD_GET_SIM_VARIABLE, VAR_TELEPORT_STARTING_VEHICLES_IDS, "", None);
85        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_STRINGLIST))?;
86        client.read_string_list_from_input()
87    }
88
89    pub fn get_ending_teleport_number(&self, client: &mut TraciClient) -> Result<i32, TraciError> {
90        client.create_command(CMD_GET_SIM_VARIABLE, VAR_TELEPORT_ENDING_VEHICLES_NUMBER, "", None);
91        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
92        client.read_int_from_input()
93    }
94
95    pub fn get_ending_teleport_id_list(&self, client: &mut TraciClient) -> Result<Vec<String>, TraciError> {
96        client.create_command(CMD_GET_SIM_VARIABLE, VAR_TELEPORT_ENDING_VEHICLES_IDS, "", None);
97        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_STRINGLIST))?;
98        client.read_string_list_from_input()
99    }
100
101    pub fn get_departed_person_number(&self, client: &mut TraciClient) -> Result<i32, TraciError> {
102        client.create_command(CMD_GET_SIM_VARIABLE, VAR_DEPARTED_PERSONS_NUMBER, "", None);
103        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
104        client.read_int_from_input()
105    }
106
107    pub fn get_departed_person_id_list(&self, client: &mut TraciClient) -> Result<Vec<String>, TraciError> {
108        client.create_command(CMD_GET_SIM_VARIABLE, VAR_DEPARTED_PERSONS_IDS, "", None);
109        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_STRINGLIST))?;
110        client.read_string_list_from_input()
111    }
112
113    pub fn get_arrived_person_number(&self, client: &mut TraciClient) -> Result<i32, TraciError> {
114        client.create_command(CMD_GET_SIM_VARIABLE, VAR_ARRIVED_PERSONS_NUMBER, "", None);
115        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
116        client.read_int_from_input()
117    }
118
119    pub fn get_arrived_person_id_list(&self, client: &mut TraciClient) -> Result<Vec<String>, TraciError> {
120        client.create_command(CMD_GET_SIM_VARIABLE, VAR_ARRIVED_PERSONS_IDS, "", None);
121        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_STRINGLIST))?;
122        client.read_string_list_from_input()
123    }
124
125    pub fn get_delta_t(&self, client: &mut TraciClient) -> Result<f64, TraciError> {
126        client.create_command(CMD_GET_SIM_VARIABLE, VAR_DELTA_T, "", None);
127        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_DOUBLE))?;
128        client.read_double_from_input()
129    }
130
131    pub fn get_net_boundary(&self, client: &mut TraciClient) -> Result<Vec<crate::types::TraciPosition>, TraciError> {
132        client.create_command(CMD_GET_SIM_VARIABLE, VAR_NET_BOUNDING_BOX, "", None);
133        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_POLYGON))?;
134        client.read_polygon_from_input()
135    }
136
137    pub fn get_min_expected_number(&self, client: &mut TraciClient) -> Result<i32, TraciError> {
138        client.create_command(CMD_GET_SIM_VARIABLE, VAR_MIN_EXPECTED_VEHICLES, "", None);
139        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
140        client.read_int_from_input()
141    }
142
143    pub fn get_option(&self, client: &mut TraciClient, option: &str) -> Result<String, TraciError> {
144        client.create_command(CMD_GET_SIM_VARIABLE, VAR_OPTION, option, None);
145        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_STRING))?;
146        client.read_string_from_input()
147    }
148
149    pub fn get_bus_stop_waiting(&self, client: &mut TraciClient, stop_id: &str) -> Result<i32, TraciError> {
150        client.create_command(CMD_GET_SIM_VARIABLE, VAR_BUS_STOP_WAITING, stop_id, None);
151        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_INTEGER))?;
152        client.read_int_from_input()
153    }
154
155    pub fn get_bus_stop_waiting_id_list(&self, client: &mut TraciClient, stop_id: &str) -> Result<Vec<String>, TraciError> {
156        client.create_command(CMD_GET_SIM_VARIABLE, VAR_BUS_STOP_WAITING_IDS, stop_id, None);
157        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_STRINGLIST))?;
158        client.read_string_list_from_input()
159    }
160
161    // -----------------------------------------------------------------------
162    // Position conversion
163    // -----------------------------------------------------------------------
164
165    /// Convert a road position (edge + offset + lane) to a 2-D or geographic position.
166    pub fn convert2d(
167        &self,
168        client: &mut TraciClient,
169        edge_id: &str,
170        pos: f64,
171        lane_index: i32,
172        to_geo: bool,
173    ) -> Result<TraciPosition, TraciError> {
174        let pos_type = if to_geo { POSITION_LON_LAT } else { POSITION_2D };
175        let mut add = Storage::new();
176        add.write_u8(TYPE_COMPOUND);
177        add.write_i32(2);
178        add.write_u8(POSITION_ROADMAP);
179        add.write_string(edge_id);
180        add.write_f64(pos);
181        add.write_u8(lane_index as u8);
182        add.write_u8(TYPE_UBYTE);
183        add.write_u8(pos_type);
184        client.create_command(CMD_GET_SIM_VARIABLE, POSITION_CONVERSION, "", Some(&add));
185        client.process_get(CMD_GET_SIM_VARIABLE, Some(pos_type))?;
186        let x = client.read_double_from_input()?;
187        let y = client.read_double_from_input()?;
188        Ok(TraciPosition::new_2d(x, y))
189    }
190
191    /// Convert a road position to a 3-D or geographic+alt position.
192    pub fn convert3d(
193        &self,
194        client: &mut TraciClient,
195        edge_id: &str,
196        pos: f64,
197        lane_index: i32,
198        to_geo: bool,
199    ) -> Result<TraciPosition, TraciError> {
200        let pos_type = if to_geo { POSITION_LON_LAT_ALT } else { POSITION_3D };
201        let mut add = Storage::new();
202        add.write_u8(TYPE_COMPOUND);
203        add.write_i32(2);
204        add.write_u8(POSITION_ROADMAP);
205        add.write_string(edge_id);
206        add.write_f64(pos);
207        add.write_u8(lane_index as u8);
208        add.write_u8(TYPE_UBYTE);
209        add.write_u8(pos_type);
210        client.create_command(CMD_GET_SIM_VARIABLE, POSITION_CONVERSION, "", Some(&add));
211        client.process_get(CMD_GET_SIM_VARIABLE, Some(pos_type))?;
212        let x = client.read_double_from_input()?;
213        let y = client.read_double_from_input()?;
214        let z = client.read_double_from_input()?;
215        Ok(TraciPosition::new_3d(x, y, z))
216    }
217
218    /// Convert a 2-D (or geo) position to a road position.
219    pub fn convert_road(
220        &self,
221        client: &mut TraciClient,
222        x: f64,
223        y: f64,
224        is_geo: bool,
225        v_class: &str,
226    ) -> Result<TraciRoadPosition, TraciError> {
227        let src_pos_type = if is_geo { POSITION_LON_LAT } else { POSITION_2D };
228        let mut add = Storage::new();
229        add.write_u8(TYPE_COMPOUND);
230        add.write_i32(3);
231        add.write_u8(src_pos_type);
232        add.write_f64(x);
233        add.write_f64(y);
234        add.write_u8(TYPE_UBYTE);
235        add.write_u8(POSITION_ROADMAP);
236        add.write_u8(TYPE_STRING);
237        add.write_string(v_class);
238        client.create_command(CMD_GET_SIM_VARIABLE, POSITION_CONVERSION, "", Some(&add));
239        client.process_get(CMD_GET_SIM_VARIABLE, Some(POSITION_ROADMAP))?;
240        let edge_id = client.read_string_from_input()?;
241        let pos = client.read_double_from_input()?;
242        let lane_index = client.read_ubyte_from_input()? as i32;
243        Ok(TraciRoadPosition { edge_id, pos, lane_index })
244    }
245
246    /// Convert between geographic and Cartesian positions.
247    pub fn convert_geo(
248        &self,
249        client: &mut TraciClient,
250        x: f64,
251        y: f64,
252        from_geo: bool,
253    ) -> Result<TraciPosition, TraciError> {
254        let pos_type = if from_geo { POSITION_2D } else { POSITION_LON_LAT };
255        let src_pos_type = if from_geo { POSITION_LON_LAT } else { POSITION_2D };
256        let mut add = Storage::new();
257        add.write_u8(TYPE_COMPOUND);
258        add.write_i32(2);
259        add.write_u8(src_pos_type);
260        add.write_f64(x);
261        add.write_f64(y);
262        add.write_u8(TYPE_UBYTE);
263        add.write_u8(pos_type);
264        client.create_command(CMD_GET_SIM_VARIABLE, POSITION_CONVERSION, "", Some(&add));
265        client.process_get(CMD_GET_SIM_VARIABLE, Some(pos_type))?;
266        let rx = client.read_double_from_input()?;
267        let ry = client.read_double_from_input()?;
268        Ok(TraciPosition::new_2d(rx, ry))
269    }
270
271    // -----------------------------------------------------------------------
272    // Distance queries
273    // -----------------------------------------------------------------------
274
275    /// Get the distance between two 2-D (or geo) positions.
276    pub fn get_distance_2d(
277        &self,
278        client: &mut TraciClient,
279        x1: f64,
280        y1: f64,
281        x2: f64,
282        y2: f64,
283        is_geo: bool,
284        is_driving: bool,
285    ) -> Result<f64, TraciError> {
286        let pos_type = if is_geo { POSITION_LON_LAT } else { POSITION_2D };
287        let dist_type: u8 = if is_driving { REQUEST_DRIVINGDIST } else { REQUEST_AIRDIST };
288        let mut add = Storage::new();
289        add.write_u8(TYPE_COMPOUND);
290        add.write_i32(3);
291        add.write_u8(pos_type);
292        add.write_f64(x1);
293        add.write_f64(y1);
294        add.write_u8(pos_type);
295        add.write_f64(x2);
296        add.write_f64(y2);
297        add.write_u8(dist_type);
298        client.create_command(CMD_GET_SIM_VARIABLE, DISTANCE_REQUEST, "", Some(&add));
299        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_DOUBLE))?;
300        client.read_double_from_input()
301    }
302
303    /// Get the distance between two road positions.
304    pub fn get_distance_road(
305        &self,
306        client: &mut TraciClient,
307        edge_id1: &str,
308        pos1: f64,
309        edge_id2: &str,
310        pos2: f64,
311        is_driving: bool,
312    ) -> Result<f64, TraciError> {
313        let dist_type: u8 = if is_driving { REQUEST_DRIVINGDIST } else { REQUEST_AIRDIST };
314        let mut add = Storage::new();
315        add.write_u8(TYPE_COMPOUND);
316        add.write_i32(3);
317        add.write_u8(POSITION_ROADMAP);
318        add.write_string(edge_id1);
319        add.write_f64(pos1);
320        add.write_u8(0); // lane
321        add.write_u8(POSITION_ROADMAP);
322        add.write_string(edge_id2);
323        add.write_f64(pos2);
324        add.write_u8(0); // lane
325        add.write_u8(dist_type);
326        client.create_command(CMD_GET_SIM_VARIABLE, DISTANCE_REQUEST, "", Some(&add));
327        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_DOUBLE))?;
328        client.read_double_from_input()
329    }
330
331    // -----------------------------------------------------------------------
332    // Route finding
333    // -----------------------------------------------------------------------
334
335    /// Find a route between two edges, returning a [`TraciStage`].
336    pub fn find_route(
337        &self,
338        client: &mut TraciClient,
339        from_edge: &str,
340        to_edge: &str,
341        v_type: &str,
342        pos: f64,
343        routing_mode: i32,
344    ) -> Result<TraciStage, TraciError> {
345        let mut add = Storage::new();
346        add.write_u8(TYPE_COMPOUND);
347        add.write_i32(5);
348        add.write_u8(TYPE_STRING);
349        add.write_string(from_edge);
350        add.write_u8(TYPE_STRING);
351        add.write_string(to_edge);
352        add.write_u8(TYPE_STRING);
353        add.write_string(v_type);
354        add.write_u8(TYPE_DOUBLE);
355        add.write_f64(pos);
356        add.write_u8(TYPE_INTEGER);
357        add.write_i32(routing_mode);
358        client.create_command(CMD_GET_SIM_VARIABLE, FIND_ROUTE, "", Some(&add));
359        client.process_get(CMD_GET_SIM_VARIABLE, Some(TYPE_COMPOUND))?;
360        read_traci_stage(client)
361    }
362
363    // -----------------------------------------------------------------------
364    // State management
365    // -----------------------------------------------------------------------
366
367    pub fn load_state(&self, client: &mut TraciClient, path: &str) -> Result<(), TraciError> {
368        let mut add = Storage::new();
369        add.write_u8(TYPE_STRING);
370        add.write_string(path);
371        client.create_command(CMD_SET_SIM_VARIABLE, CMD_LOAD_SIMSTATE, "", Some(&add));
372        client.process_set(CMD_SET_SIM_VARIABLE)?;
373        Ok(())
374    }
375
376    pub fn save_state(&self, client: &mut TraciClient, destination: &str) -> Result<(), TraciError> {
377        let mut add = Storage::new();
378        add.write_u8(TYPE_STRING);
379        add.write_string(destination);
380        client.create_command(CMD_SET_SIM_VARIABLE, CMD_SAVE_SIMSTATE, "", Some(&add));
381        client.process_set(CMD_SET_SIM_VARIABLE)?;
382        Ok(())
383    }
384
385    pub fn write_message(&self, client: &mut TraciClient, msg: &str) -> Result<(), TraciError> {
386        let mut add = Storage::new();
387        add.write_u8(TYPE_STRING);
388        add.write_string(msg);
389        client.create_command(CMD_SET_SIM_VARIABLE, CMD_MESSAGE, "", Some(&add));
390        client.process_set(CMD_SET_SIM_VARIABLE)?;
391        Ok(())
392    }
393
394    pub fn subscribe(&self, client: &mut TraciClient, vars: &[u8], begin: f64, end: f64) -> Result<(), TraciError> {
395        client.subscribe_object_variable(CMD_SUBSCRIBE_SIM_VARIABLE, "", begin, end, vars)
396    }
397}
398
399// ============================================================================
400// Shared helper: read a TraciStage from client.input (TYPE_COMPOUND already consumed)
401// ============================================================================
402
403/// Read a `TraciStage` from the input buffer. The TYPE_COMPOUND tag and expected-type
404/// check have already been performed by `process_get`; the next bytes are the payload.
405pub(crate) fn read_traci_stage(client: &mut TraciClient) -> Result<TraciStage, TraciError> {
406    client.read_int_from_input()?; // components count
407    client.read_ubyte_from_input()?; // TYPE_INTEGER tag
408    let type_ = client.read_int_from_input()?;
409
410    client.read_ubyte_from_input()?; // TYPE_STRING tag
411    let v_type = client.read_string_from_input()?;
412
413    client.read_ubyte_from_input()?;
414    let line = client.read_string_from_input()?;
415
416    client.read_ubyte_from_input()?;
417    let dest_stop = client.read_string_from_input()?;
418
419    client.read_ubyte_from_input()?;
420    let edges = client.read_string_list_from_input()?;
421
422    client.read_ubyte_from_input()?;
423    let travel_time = client.read_double_from_input()?;
424
425    client.read_ubyte_from_input()?;
426    let cost = client.read_double_from_input()?;
427
428    client.read_ubyte_from_input()?;
429    let length = client.read_double_from_input()?;
430
431    client.read_ubyte_from_input()?;
432    let intended = client.read_string_from_input()?;
433
434    client.read_ubyte_from_input()?;
435    let depart = client.read_double_from_input()?;
436
437    client.read_ubyte_from_input()?;
438    let depart_pos = client.read_double_from_input()?;
439
440    client.read_ubyte_from_input()?;
441    let arrival_pos = client.read_double_from_input()?;
442
443    client.read_ubyte_from_input()?;
444    let description = client.read_string_from_input()?;
445
446    Ok(TraciStage {
447        type_,
448        v_type,
449        line,
450        dest_stop,
451        edges,
452        travel_time,
453        cost,
454        length,
455        intended,
456        depart,
457        depart_pos,
458        arrival_pos,
459        description,
460    })
461}