1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
//! `CFlightPlan` — a reference to a flight plan.
use std::marker::PhantomData;
use euroscope_sys::FlightPlanPtr;
use crate::{
ControllerAssignedData, ExtractedRoute, FlightPlanData, PositionPredictions,
utils::{cstr, cstr_lossy},
};
/// A reference to a flight plan.
///
/// A thin handle over EuroScope's `CFlightPlan`, valid only for the duration of
/// the callback that delivered it (`'cb`).
#[derive(Clone, Copy)]
pub struct FlightPlan<'cb> {
raw: FlightPlanPtr,
_marker: PhantomData<&'cb ()>,
}
impl<'cb> FlightPlan<'cb> {
pub(crate) fn from_raw(raw: FlightPlanPtr) -> Self {
Self {
raw,
_marker: PhantomData,
}
}
/// The raw `CFlightPlan*`. Escape hatch for `euroscope-sys` calls not yet
/// wrapped here.
pub(crate) fn as_ptr(&self) -> FlightPlanPtr {
self.raw
}
/// Whether this handle references a live aircraft.
pub fn is_valid(&self) -> bool {
// SAFETY: `raw` is the pointer EuroScope handed us for this callback.
unsafe { euroscope_sys::es_flightplan_is_valid(self.raw) }
}
/// The aircraft callsign. Empty if the handle is invalid. Borrows
/// EuroScope-owned memory; valid for this callback only.
pub fn callsign(&self) -> &str {
// SAFETY: the SDK returns a borrowed NUL-terminated ANSI string, or
// null (guarded).
unsafe { cstr(euroscope_sys::es_flightplan_callsign(self.raw)) }
}
/// The pilot name extracted from statistics data. Empty if unavailable.
pub fn pilot_name(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe { cstr(euroscope_sys::es_flightplan_pilot_name(self.raw)) }
}
/// The aircraft state (`FLIGHT_PLAN_STATE_...`). `0` if no selected AC.
pub fn state(&self) -> crate::FlightPlanState {
// SAFETY: `raw` is valid for this callback.
crate::FlightPlanState::from_raw(unsafe { euroscope_sys::es_flightplan_state(self.raw) })
}
/// The flight-plan state (not-started / simulated / terminated). `0` if no
/// selected AC.
pub fn fp_state(&self) -> crate::SimulationState {
// SAFETY: `raw` is valid for this callback.
crate::SimulationState::from_raw(unsafe { euroscope_sys::es_flightplan_fp_state(self.raw) })
}
/// Whether the aircraft is out of range and ES simulates its movements.
pub fn simulated(&self) -> bool {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_simulated(self.raw) }
}
/// The callsign of the controller currently tracking. Empty if untracked.
pub fn tracking_controller_callsign(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe {
cstr(euroscope_sys::es_flightplan_tracking_controller_callsign(
self.raw,
))
}
}
/// The position ID of the controller currently tracking. Empty if
/// untracked.
pub fn tracking_controller_id(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe {
cstr(euroscope_sys::es_flightplan_tracking_controller_id(
self.raw,
))
}
}
/// Whether the plane is tracked by me.
pub fn tracking_controller_is_me(&self) -> bool {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_tracking_controller_is_me(self.raw) }
}
/// The callsign of the controller who is the target of the current
/// handoff. Empty if none.
pub fn handoff_target_controller_callsign(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe { cstr(euroscope_sys::es_flightplan_handoff_target_controller_callsign(self.raw)) }
}
/// The position ID of the controller who is the target of the current
/// handoff. Empty if none.
pub fn handoff_target_controller_id(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe {
cstr(euroscope_sys::es_flightplan_handoff_target_controller_id(
self.raw,
))
}
}
/// The full calculated distance to the destination airport (nautical
/// miles).
pub fn distance_to_destination(&self) -> f64 {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_distance_to_destination(self.raw) }
}
/// The full calculated distance from the origin airport (nautical miles).
pub fn distance_from_origin(&self) -> f64 {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_distance_from_origin(self.raw) }
}
/// The next active COPX point name along the extracted route. May be empty.
pub fn next_copx_point_name(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe { cstr(euroscope_sys::es_flightplan_next_copx_point_name(self.raw)) }
}
/// The next active FIR COPX point name along the extracted route. May be
/// empty.
pub fn next_fir_copx_point_name(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe {
cstr(euroscope_sys::es_flightplan_next_fir_copx_point_name(
self.raw,
))
}
}
/// Minutes until the plane enters my sectors: `0` if already inside, `-1`
/// if it never will.
pub fn sector_entry_minutes(&self) -> i32 {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_sector_entry_minutes(self.raw) }
}
/// Minutes until the plane leaves my sectors, or `-1` if it never enters.
pub fn sector_exit_minutes(&self) -> i32 {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_sector_exit_minutes(self.raw) }
}
/// The RAM flag, set when a RAM warning is to be displayed.
pub fn ram_flag(&self) -> bool {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_ram_flag(self.raw) }
}
/// The CLAM flag, set when a CLAM warning is to be displayed.
pub fn clam_flag(&self) -> bool {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_clam_flag(self.raw) }
}
/// The ground state (`STUP`, `PUSH`, `TAXI`, `DEPA`, `ARR`, `TAXIIN`,
/// `PARK`, or empty).
pub fn ground_state(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe { cstr(euroscope_sys::es_flightplan_ground_state(self.raw)) }
}
/// Whether the clearance has been received.
pub fn clearence_flag(&self) -> bool {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_clearence_flag(self.raw) }
}
/// Whether the pilot or a controller indicated text communication.
pub fn is_text_communication(&self) -> bool {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_is_text_communication(self.raw) }
}
/// The final altitude from the FP or from controller override (no ALT/FL
/// change).
pub fn final_altitude(&self) -> i32 {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_final_altitude(self.raw) }
}
/// The cleared altitude, or the final one if missing (no ALT/FL change).
pub fn cleared_altitude(&self) -> i32 {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_cleared_altitude(self.raw) }
}
/// The entry point coordination state (`COORDINATION_STATE_...`).
pub fn entry_coordination_point_state(&self) -> crate::CoordinationState {
// SAFETY: `raw` is valid for this callback.
crate::CoordinationState::from_raw(unsafe {
euroscope_sys::es_flightplan_entry_coordination_point_state(self.raw)
})
}
/// The coordination point name for the entry point.
pub fn entry_coordination_point_name(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe {
cstr(euroscope_sys::es_flightplan_entry_coordination_point_name(
self.raw,
))
}
}
/// The entry point altitude coordination state (`COORDINATION_STATE_...`).
pub fn entry_coordination_altitude_state(&self) -> crate::CoordinationState {
// SAFETY: `raw` is valid for this callback.
crate::CoordinationState::from_raw(unsafe {
euroscope_sys::es_flightplan_entry_coordination_altitude_state(self.raw)
})
}
/// The coordination point altitude for the entry point.
pub fn entry_coordination_altitude(&self) -> i32 {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_entry_coordination_altitude(self.raw) }
}
/// The exit point name coordination state (`COORDINATION_STATE_...`).
pub fn exit_coordination_name_state(&self) -> crate::CoordinationState {
// SAFETY: `raw` is valid for this callback.
crate::CoordinationState::from_raw(unsafe {
euroscope_sys::es_flightplan_exit_coordination_name_state(self.raw)
})
}
/// The coordination point name for the exit point.
pub fn exit_coordination_point_name(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe {
cstr(euroscope_sys::es_flightplan_exit_coordination_point_name(
self.raw,
))
}
}
/// The exit point altitude coordination state (`COORDINATION_STATE_...`).
pub fn exit_coordination_altitude_state(&self) -> crate::CoordinationState {
// SAFETY: `raw` is valid for this callback.
crate::CoordinationState::from_raw(unsafe {
euroscope_sys::es_flightplan_exit_coordination_altitude_state(self.raw)
})
}
/// The coordination point altitude for the exit point.
pub fn exit_coordination_altitude(&self) -> i32 {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_exit_coordination_altitude(self.raw) }
}
/// The callsign of the coordinated next controller, or empty if none.
pub fn coordinated_next_controller(&self) -> &str {
// SAFETY: borrowed NUL-terminated ANSI string, or null (guarded).
unsafe {
cstr(euroscope_sys::es_flightplan_coordinated_next_controller(
self.raw,
))
}
}
/// The next controller coordination state (`COORDINATION_STATE_...`).
pub fn coordinated_next_controller_state(&self) -> crate::CoordinationState {
// SAFETY: `raw` is valid for this callback.
crate::CoordinationState::from_raw(unsafe {
euroscope_sys::es_flightplan_coordinated_next_controller_state(self.raw)
})
}
// --- Navigators to sibling handles (share this flight plan's pointer) ---
/// The flight-plan data sub-object. Shares this handle's pointer.
pub fn flight_plan_data(&self) -> FlightPlanData<'cb> {
FlightPlanData::from_raw(self.as_ptr())
}
/// The controller-assigned data sub-object. Shares this handle's pointer.
pub fn controller_assigned_data(&self) -> ControllerAssignedData<'cb> {
ControllerAssignedData::from_raw(self.as_ptr())
}
/// The extracted route sub-object. Shares this handle's pointer.
pub fn extracted_route(&self) -> ExtractedRoute<'cb> {
ExtractedRoute::from_raw(self.as_ptr())
}
/// The position predictions sub-object. Shares this handle's pointer.
pub fn position_predictions(&self) -> PositionPredictions<'cb> {
PositionPredictions::from_raw(self.as_ptr())
}
// --- Actions (mutate EuroScope state) ---
/// Uncorrelate the flight plan from its current radar target. Mutates
/// EuroScope state.
pub fn uncorrelate(&self) {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_uncorrelate(self.raw) }
}
/// Start tracking the aircraft if it was untracked. Returns whether it
/// succeeded. Mutates EuroScope state.
pub fn start_tracking(&self) -> bool {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_start_tracking(self.raw) }
}
/// Release (drop) the target. Returns whether it succeeded. Mutates
/// EuroScope state.
pub fn end_tracking(&self) -> bool {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_end_tracking(self.raw) }
}
/// Initiate a handoff to the specified target controller. Returns whether
/// it succeeded. Mutates EuroScope state.
pub fn initiate_handoff(&self, target_controller: &str) -> bool {
// SAFETY: `raw` is valid; the CString outlives the call.
unsafe {
euroscope_sys::es_flightplan_initiate_handoff(
self.raw,
cstr_lossy(target_controller).as_ptr(),
)
}
}
/// Accept a handoff initiated to me. Mutates EuroScope state.
pub fn accept_handoff(&self) {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_accept_handoff(self.raw) }
}
/// Refuse a handoff initiated to me. Mutates EuroScope state.
pub fn refuse_handoff(&self) {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_refuse_handoff(self.raw) }
}
/// Initiate a COPN/COPX request. `point_name` must not be empty; pass
/// `altitude` of `0` to request only the point. Returns whether it
/// succeeded. Mutates EuroScope state.
pub fn initiate_coordination(
&self,
target_controller: &str,
point_name: &str,
altitude: i32,
) -> bool {
// SAFETY: `raw` is valid; the CStrings outlive the call.
unsafe {
euroscope_sys::es_flightplan_initiate_coordination(
self.raw,
cstr_lossy(target_controller).as_ptr(),
cstr_lossy(point_name).as_ptr(),
altitude,
)
}
}
/// Accept a coordination initiated to me. Mutates EuroScope state.
pub fn accept_coordination(&self) {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_accept_coordination(self.raw) }
}
/// Refuse a coordination initiated to me. Mutates EuroScope state.
pub fn refuse_coordination(&self) {
// SAFETY: `raw` is valid for this callback.
unsafe { euroscope_sys::es_flightplan_refuse_coordination(self.raw) }
}
/// Send the flight strip to a controller. Mutates EuroScope state.
pub fn push_flight_strip(&self, target_controller: &str) {
// SAFETY: `raw` is valid; the CString outlives the call.
unsafe {
euroscope_sys::es_flightplan_push_flight_strip(
self.raw,
cstr_lossy(target_controller).as_ptr(),
);
}
}
/// Set the estimated arrival time (`HHMM` zulu) at a point along the route.
/// Mutates EuroScope state.
pub fn set_estimation(&self, point_name: &str, time: &str) {
// SAFETY: `raw` is valid; the CStrings outlive the call.
unsafe {
euroscope_sys::es_flightplan_set_estimation(
self.raw,
cstr_lossy(point_name).as_ptr(),
cstr_lossy(time).as_ptr(),
);
}
}
/// The FP-track position snapshot for this flight plan (owned). `None` if
/// unavailable.
pub fn track_position(&self) -> Option<crate::OwnedRadarTargetPosition> {
// SAFETY: `raw` valid for this callback; shim returns a heap copy or null.
crate::OwnedRadarTargetPosition::from_owned(unsafe {
euroscope_sys::es_flightplan_track_position(self.as_ptr())
})
}
/// The radar target correlated with this flight plan, if any (owned).
pub fn correlated_radar_target(&self) -> Option<crate::OwnedRadarTarget> {
// SAFETY: `raw` is valid for this callback; shim returns a heap copy or null.
crate::OwnedRadarTarget::from_owned(unsafe {
euroscope_sys::es_flightplan_correlated_radar_target(self.as_ptr())
})
}
/// Clear the flight plan's estimations.
///
/// **Always panics.** `CFlightPlan::ClearEstimation` is declared in
/// `EuroScopePlugIn.h` but is *not exported* by `EuroScopePlugInDll.lib`, so
/// there is no symbol to link against — wrapping it for real would make the
/// whole plugin fail to link. It is kept here (panicking) to document the
/// gap and preserve API shape; if a future SDK exports it, replace the body
/// with a `euroscope-sys` call.
#[expect(
clippy::unimplemented,
reason = "Declared in the header but not exported in the lib."
)]
pub fn clear_estimation(&self) {
unimplemented!(
"CFlightPlan::ClearEstimation is not exported by EuroScopePlugInDll.lib, so it cannot \
be called"
)
}
}