iracing_telem/
flags.rs

1//! flags contains definitions for the Bitfield and Enum values that are returned
2//! as variable values.
3//!
4//! e.g. the variable "SessionState" has an VarType::Int which can be mapped into
5//! the SessionState enum. variables with VarType::Bitfield can be mapped into
6//! the relevant bitfield struct.
7//!
8
9use super::makelong;
10use bitflags::bitflags;
11use num::ToPrimitive;
12use num_derive::{FromPrimitive, ToPrimitive};
13
14bitflags! {
15    pub struct StatusField:i32 {
16        const CONNECTED = 1;
17    }
18}
19
20// BITFIELD & ENUMs that can appear as telemetry values
21
22bitflags! {
23    pub struct EngineWarnings:i32 {
24        const WATER_TEMP_WARNING    = 0x01;
25        const FUEL_PRESSURE_WARNING = 0x02;
26        const OIL_PRESSURE_WARNING  = 0x04;
27        const ENGINE_STALLED        = 0x08;
28        const PIT_SPEED_LIMITER     = 0x10;
29        const REV_LIMITER_ACTIVE    = 0x20;
30        const OIL_TEMP_WARNING      = 0x40;
31    }
32}
33
34bitflags! {
35    /// The state of the flags being waved on the track, as well as the
36    /// state of the start lights.
37    pub struct Flags:u32 {
38        // global flags
39        const CHECKERED = 0x00000001;
40        const WHITE     = 0x00000002;
41        const GREEN     = 0x00000004;
42        const YELLOW    = 0x00000008;
43        const RED       = 0x00000010;
44        const BLUE      = 0x00000020;
45        const DEBRIS    = 0x00000040;
46        const CROSSED   = 0x00000080;
47        const YELLOW_WAVING = 0x00000100;
48        const ONE_TO_GREEN  = 0x00000200;
49        const GREEN_HELD    = 0x00000400;
50        const LAPS_10_TO_GO = 0x00000800;
51        const LAPS_5_TO_GO  = 0x00001000;
52        const RANDOM_WAVING = 0x00002000;
53        const CAUTION       = 0x00004000;
54        const CAUTION_WAVING= 0x00008000;
55
56        // driver black flags
57        const BLACK         = 0x00010000;
58        const DISQUALIFY    = 0x00020000;
59        const SERVICABLE    = 0x00040000;   // aka can pit
60        const FURLED        = 0x00080000;
61        const REPAIR        = 0x00100000;
62
63        // start lights
64        const START_HIDDEN  = 0x10000000;
65        const START_READY   = 0x20000000;
66        const START_SET     = 0x40000000;
67        const START_GO      = 0x80000000;
68    }
69}
70
71#[derive(Clone, Copy, Debug, PartialEq, FromPrimitive)]
72pub enum TrackLocation {
73    NotInWorld = -1,
74    OffTrack,
75    InPitStall,
76    ApproachingPits,
77    OnTrack,
78}
79
80#[derive(Clone, Copy, Debug, PartialEq, FromPrimitive)]
81pub enum TrackSurface {
82    SurfaceNotInWorld = -1,
83    UndefinedMaterial = 0,
84
85    Asphalt1Material,
86    Asphalt2Material,
87    Asphalt3Material,
88    Asphalt4Material,
89    Concrete1Material,
90    Concrete2Material,
91    RacingDirt1Material,
92    RacingDirt2Material,
93    Paint1Material,
94    Paint2Material,
95    Rumble1Material,
96    Rumble2Material,
97    Rumble3Material,
98    Rumble4Material,
99
100    Grass1Material,
101    Grass2Material,
102    Grass3Material,
103    Grass4Material,
104    Dirt1Material,
105    Dirt2Material,
106    Dirt3Material,
107    Dirt4Material,
108    SandMaterial,
109    Gravel1Material,
110    Gravel2Material,
111    GrasscreteMaterial,
112    AstroturfMaterial,
113}
114
115#[derive(Clone, Copy, Debug, PartialEq, FromPrimitive)]
116pub enum SessionState {
117    Invalid,
118    GetInCar,
119    Warmup,
120    ParadeLaps,
121    Racing,
122    Checkered,
123    CoolDown,
124}
125
126#[allow(clippy::enum_variant_names)]
127#[derive(Clone, Copy, Debug, PartialEq, FromPrimitive)]
128pub enum CarLeftRight {
129    Off,
130    /// no cars around us.
131    Clear,
132    /// there is a car to our left.
133    CarLeft,
134    /// there is a car to our right.    
135    CarRight,
136    /// there are cars on each side.
137    CarLeftRight,
138    /// there are two cars to our left.
139    TwoCarsLeft,
140    /// there are two cars to our right.
141    TwoCarsRight,
142}
143
144bitflags! {
145    pub struct CameraState:i32 {
146        /// The camera tool can only be activated if viewing the session screen (out of car)
147        const IS_SESSION_SCREEN     = 0x0001;
148        /// the scenic camera is active (no focus car)
149        const IS_SCENIC_ACTIVE      = 0x0002;
150
151        //these can be changed with a broadcast message
152        const CAM_TOOL_ACTIVE           = 0x0004;
153        const UI_HIDDEN                 = 0x0008;
154        const USE_AUTO_SHOT_SELECTION   = 0x0010;
155        const USE_TEMPORARY_EDITS       = 0x0020;
156        const USE_KEY_ACCELERATION      = 0x0040;
157        const USE_KEY_10X_ACCELERATION  = 0x0080;
158        const USE_MOUSE_AIM_MODE        = 0x0100;
159    }
160}
161impl CameraState {
162    pub fn params(&self) -> i32 {
163        self.bits()
164    }
165}
166bitflags! {
167    pub struct PitSvcFlags:i32 {
168        const LF_TIRE_CHANGE	= 0x0001;
169        const RF_TIRE_CHANGE	= 0x0002;
170        const LR_TIRE_CHANGE    = 0x0004;
171        const RR_TIRE_CHANGE	= 0x0008;
172
173        const FUEL_FILL			= 0x0010;
174        const WINDSHIELD_TEAROFF= 0x0020;
175        const FAST_REPAIR		= 0x0040;
176    }
177}
178
179#[derive(Clone, Copy, Debug, PartialEq, FromPrimitive)]
180pub enum PitSvcStatus {
181    // status
182    None = 0,
183    InProgress,
184    Complete,
185
186    // errors
187    TooFarLeft = 100,
188    TooFarRight,
189    TooFarForward,
190    TooFarBack,
191    BadAngle,
192    CantFixThat,
193}
194
195#[derive(Clone, Copy, Debug, PartialEq, FromPrimitive)]
196pub enum PaceMode {
197    SingleFileStart = 0,
198    DoubleFileStart,
199    SingleFileRestart,
200    DoubleFileRestart,
201    NotPacing,
202}
203
204bitflags! {
205    pub struct PaceFlags:i32 {
206        const END_OF_LINE   = 0x01;
207        const FREE_PASS     = 0x02;
208        const WAVED_AROUND  = 0x04;
209    }
210}
211
212/// Enums for broadcast msg
213//----
214
215/// Remote control the sim by sending these messages.
216/// camera and replay commands only work when you are out of your car,
217/// pit commands only work when in your car.
218#[derive(Debug)]
219pub enum BroadcastMsg {
220    /// car position, group, camera (this is per the SDK docs, but seems like in practice group & camera are revesed)
221    CamSwitchPos(CameraFocus, i16, i16),
222    /// driver #, group, camera (this is per the SDK docs, but seems like in practice group & camera are revesed)
223    CamSwitchNum(CameraFocus, i16, i16),
224    // irsdk_CameraState, unused, unused
225    CamSetState(CameraState),
226    /// speed, slowMotion, unused
227    ReplaySetPlaySpeed(i16, bool),
228    /// irsdk_RpyPosMode, Frame Number (60 frames a second)
229    ReplaySetPlayPosition(ReplayPos, i32),
230    // irsdk_RpySrchMode, unused, unused
231    ReplaySearch(ReplaySearch),
232    // irsdk_RpyStateMode, unused, unused
233    ReplaySetState(ReplayState),
234    /// irsdk_ReloadTexturesMode, carIdx, unused
235    ReloadTextures(ReloadTextures),
236    /// irsdk_ChatCommandMode, subCommand, unused
237    ChatComand(ChatCommand),
238    /// irsdk_PitCommandMode, parameter   
239    PitCommand(PitCommand),
240    /// irsdk_TelemCommandMode, unused, unused        
241    TelemCommand(TelemCommand),
242    /// irsdk_FFBCommandMode, value (float, high, low)       
243    FFBCommand(FFBCommand),
244    /// sessionNum, sessionTimeMS (high, low)    
245    ReplaySearchSessionTime(i16, std::time::Duration),
246    /// irsdk_VideoCaptureMode, unused, unused
247    VideoCapture(VideoCapture),
248}
249impl BroadcastMsg {
250    pub fn params(&self) -> (i16, (i16, isize)) {
251        match self {
252            BroadcastMsg::CamSwitchPos(car, group, cam) => {
253                (0, (car.params(), makelong(*group, *cam)))
254            }
255            BroadcastMsg::CamSwitchNum(car, group, cam) => {
256                (1, (car.params(), makelong(*group, *cam)))
257            }
258            BroadcastMsg::CamSetState(cs) => (2, (cs.params() as i16, 0)), // CameraState would appear to overflow an i16
259            BroadcastMsg::ReplaySetPlaySpeed(speed, slow) => {
260                (3, (*speed, if *slow { 1 } else { 0 }))
261            }
262            BroadcastMsg::ReplaySetPlayPosition(pos, frame) => (4, (pos.params(), *frame as isize)),
263            BroadcastMsg::ReplaySearch(s) => (5, (s.parms(), 0)),
264            BroadcastMsg::ReplaySetState(s) => (6, (s.params(), 0)),
265            BroadcastMsg::ReloadTextures(r) => (7, (r.params())),
266            BroadcastMsg::ChatComand(c) => (8, (c.params())),
267            BroadcastMsg::PitCommand(c) => (9, c.params()),
268            BroadcastMsg::TelemCommand(t) => (10, (t.params(), 0)),
269            BroadcastMsg::FFBCommand(f) => (11, f.params()),
270            BroadcastMsg::ReplaySearchSessionTime(ses, tm) => (12, (*ses, tm.as_millis() as isize)),
271            BroadcastMsg::VideoCapture(v) => (13, (v.params(), 0)),
272        }
273    }
274}
275
276/// irsdk_BroadcastCamSwitchPos or irsdk_BroadcastCamSwitchNum camera focus defines
277/// pass these in for the first parameter to select the 'focus at' types in the camera system.
278#[derive(Debug)]
279pub enum CameraFocus {
280    Incident,
281    Leader,
282    Exciting,
283    // ctFocusAtDriver + car number...
284    Driver(i16),
285}
286impl CameraFocus {
287    fn params(&self) -> i16 {
288        match self {
289            CameraFocus::Incident => -3,
290            CameraFocus::Leader => -2,
291            CameraFocus::Exciting => -1,
292            CameraFocus::Driver(d) => *d,
293        }
294    }
295}
296
297#[derive(Debug, ToPrimitive)]
298pub enum ReplayPos {
299    Begin = 0,
300    Current,
301    End,
302}
303impl ReplayPos {
304    fn params(&self) -> i16 {
305        self.to_i16().unwrap()
306    }
307}
308
309#[derive(Debug, ToPrimitive)]
310pub enum ReplaySearch {
311    ToStart = 0,
312    ToEnd,
313    PrevSession,
314    NextSession,
315    PrevLap,
316    NextLap,
317    PrevFrame,
318    NextFrame,
319    PrevIncident,
320    NextIncident,
321}
322impl ReplaySearch {
323    fn parms(&self) -> i16 {
324        self.to_i16().unwrap()
325    }
326}
327
328#[derive(Debug, ToPrimitive)]
329pub enum ReplayState {
330    /// clear any data in the replay tape
331    EraseTape = 0,
332}
333impl ReplayState {
334    fn params(&self) -> i16 {
335        self.to_i16().unwrap()
336    }
337}
338
339#[derive(Debug)]
340pub enum ReloadTextures {
341    /// reload all textures
342    All,
343    /// reload only textures for the specific carIdx
344    CarIdx(i16),
345}
346impl ReloadTextures {
347    fn params(&self) -> (i16, isize) {
348        match self {
349            ReloadTextures::All => (0, 0),
350            ReloadTextures::CarIdx(i) => (1, *i as isize),
351        }
352    }
353}
354
355#[derive(Debug)]
356pub enum ChatCommand {
357    /// pass in a number from 1-15 representing the chat macro to launch (actual values 0-14)
358    Macro(u8),
359    /// Open up a new chat window
360    BeginChat,
361    /// Reply to last private chat
362    Reply,
363    /// Close chat window
364    Cancel,
365}
366impl ChatCommand {
367    fn params(&self) -> (i16, isize) {
368        match self {
369            ChatCommand::Macro(m) => (0, *m as isize),
370            ChatCommand::BeginChat => (1, 0),
371            ChatCommand::Reply => (2, 0),
372            ChatCommand::Cancel => (3, 0),
373        }
374    }
375}
376
377// this only works when the driver is in the car
378#[derive(Debug)]
379pub enum PitCommand {
380    /// Clear all pit checkboxes
381    Clear,
382    /// WS: Clean the winshield, using one tear off
383    TearOff,
384    /// Add fuel, optionally specify the amount to add in liters
385    Fuel(Option<i16>),
386    /// Change the left front tire, optionally specifying the pressure in KPa
387    LF(Option<i16>),
388    /// Change the right front tire, optionally specifying the pressure in KPa
389    RF(Option<i16>),
390    /// Change the left rear tire, optionally specifying the pressure in KPa
391    LR(Option<i16>),
392    /// Change the right rear tire, optionally specifying the pressure in KPa
393    RR(Option<i16>),
394    /// Clear tire pit checkboxes
395    ClearTires,
396    /// FR: Request a fast repair   
397    FastRepair,
398    /// Uncheck Clean the winshield checkbox
399    ClearWS,
400    /// Uncheck request a fast repair  
401    ClearFR,
402    /// Uncheck add fuel
403    ClearFuel,
404}
405impl PitCommand {
406    fn params(&self) -> (i16, isize) {
407        match self {
408            PitCommand::Clear => (0, 0),
409            PitCommand::TearOff => (1, 0),
410            PitCommand::Fuel(l) => (2, pit_amt(l)),
411            PitCommand::LF(p) => (3, pit_amt(p)),
412            PitCommand::RF(p) => (4, pit_amt(p)),
413            PitCommand::LR(p) => (5, pit_amt(p)),
414            PitCommand::RR(p) => (6, pit_amt(p)),
415            PitCommand::ClearTires => (7, 0),
416            PitCommand::FastRepair => (8, 0),
417            PitCommand::ClearWS => (9, 0),
418            PitCommand::ClearFR => (10, 0),
419            PitCommand::ClearFuel => (11, 0),
420        }
421    }
422}
423fn pit_amt(a: &Option<i16>) -> isize {
424    match a {
425        Some(l) => *l as isize,
426        None => 0,
427    }
428}
429
430/// You can call this any time, but telemtry only records when driver is in there car
431#[derive(Debug, ToPrimitive)]
432pub enum TelemCommand {
433    /// Turn telemetry recording off
434    Stop = 0,
435    /// Turn telemetry recording on
436    Start,
437    /// Write current file to disk and start a new one
438    Restart,
439}
440impl TelemCommand {
441    fn params(&self) -> i16 {
442        self.to_i16().unwrap()
443    }
444}
445
446/// You can call this any time
447#[derive(Debug)]
448pub enum FFBCommand {
449    /// Set the maximum force when mapping steering torque force to direct input units (float in Nm)
450    MaxForce(f32),
451}
452impl FFBCommand {
453    fn params(&self) -> (i16, isize) {
454        match self {
455            FFBCommand::MaxForce(f) => {
456                // in c assigning an int to an LPARAM (long) doesn't fill any of the new MSB.
457                // int x = 0xFFFFFFFF; LPARAM x2 = x; // x2 is 00000000FFFFFFFF;
458                // in rust casting the sign gets extended so 0xFFFFFFFF becomes 0xFFFFFFFFFFFFFFFF
459                // which is why we have the extra &
460                (0, (((*f * 65536.0) as isize) & 0x00000000FFFFFFFF))
461            }
462        }
463    }
464}
465
466#[derive(Debug, ToPrimitive)]
467pub enum VideoCapture {
468    /// save a screenshot to disk
469    TriggerScreenShot = 0,
470    /// start capturing video
471    Start,
472    /// stop capturing video
473    End,
474    /// toggle video capture on/off
475    Toggle,
476    /// show video timer in upper left corner of display
477    ShowVideoTimer,
478    /// hide video timer
479    HideVideoTimer,
480}
481impl VideoCapture {
482    fn params(&self) -> i16 {
483        self.to_i16().unwrap()
484    }
485}
486
487#[cfg(test)]
488mod tests {
489
490    use super::*;
491
492    #[test]
493    fn test_pitcommand() {
494        assert_eq!(PitCommand::Fuel(Some(12)).params(), (2, 12));
495        assert_eq!(PitCommand::Fuel(None).params(), (2, 0));
496    }
497
498    #[test]
499    fn test_ffb() {
500        assert_eq!(FFBCommand::MaxForce(50.0).params(), (0, 0x320000));
501        assert_eq!(FFBCommand::MaxForce(0.0).params(), (0, 0x00000000));
502        assert_eq!(FFBCommand::MaxForce(42.42).params(), (0, 0x2a6b85));
503        assert_eq!(FFBCommand::MaxForce(-1.0).params(), (0, 0xffff0000));
504    }
505    #[test]
506    fn test_bcast_msg() {
507        assert_eq!(
508            BroadcastMsg::PitCommand(PitCommand::LF(Some(140))).params(),
509            (9, (3, 140))
510        )
511    }
512}