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
#![deny(missing_docs)]

//! # Lib Rail Driver
//!
//! Rust FFI bindings to the `RailDriver.dll` library.
//!
//! These allow you to read and write data to or from Train Simulator 2020. Note
//! that this doesn't work with Train Sim World.
//!
//! ## Quick example
//!
//! ```rust
//! extern crate libraildriver;
//!
//! fn main() {
//!     let context = libraildriver::Context::new();
//!     let speed = context.get_value(libraildriver::Value::Speedometer,
//!                   libraildriver::Kind::Current).expect("Failed to get value.");
//!     println!("The train's current speed is: {}", speed);
//! }
//! ```

extern crate libraildriver_sys as libraildriver;
extern crate libc;

#[derive(Debug)]
/// The value you wish to target for an operation.
pub enum Value {
    /// The current reverser setting (F/N/R).
    /// Usually:
    /// - `1`: F
    /// - `0`: N
    /// - `-1`: R
    Reverser,
    /// The current throttle setting, usually between `0` and `100`. For locomotives and units with
    /// a separate throttle and brake control.
    Throttle,
    /// The current combined throttle/brake setting, usually between `-100` and `100`. For
    /// locomotives and units with a combined throttle and brake.
    CombinedThrottle,
    /// The current gear, for trains fitted.
    GearLever,
    /// The train brake lever. Usually between `0` and `100`.
    TrainBrake,
    /// The locomotive brake lever. Usually between `0` and `100`.
    LocomotiveBrake,
    /// The dynamic brake lever. Usually between `0` and `100`.
    DynamicBrake,
    /// The emergency brake button. Usually operated by setting to `1`.
    EmergencyBrake,
    /// The hand brake. Usually operated by setting to `1`.
    HandBrake,
    /// The warning system reset button. Usually operated by setting to `1`.
    WarningSystemReset,
    /// The engine start/stop button. Usually operated by setting to `1`.
    StartStopEngine,
    /// The horn lever. Usually operated by setting to `1`.
    Horn,
    /// The wipers switch. Usually operated by setting to `1`.
    Wipers,
    /// The sander. Usually operated by setting to `1`.
    Sander,
    /// The headlights. Often operated by setting to `1`.
    Headlights,
    /// The pantograph switch. Usually raised by setting to `1`.
    Pantograph,
    /// The firebox door. Usually opened by setting to `1`.
    FireboxDoor,
    /// The exhaust-based steam injector. Usually between `0` and `100`.
    ExhaustInjectorSteam,
    /// The exhaust-based water injector. Usually between `0` and `100`.
    ExhaustInjectorWater,
    /// The live steam injector. Usually between `0` and `100`.
    LiveInjectorSteam,
    /// The live water injector. Usually between `0` and `100`.
    LiveInjectorWater,
    /// The damper. Usually actuated between `0` and `100`.
    Damper,
    /// The blower valve. Usually opened between `0` and `100`.
    Blower,
    /// Stoking. Usually stoking when set to `1`.
    Stoking,
    /// The cylinder cocks. Usually opened when set to `1`.
    CylinderCock,
    /// The waterscoop. Usually operated when set to `1`.
    Waterscoop,
    /// Currently undocumented.
    SmallCompressor,
    /// Get only: The state of the AWS.
    AWS,
    /// Set only: The AWS reset button, depressed when set to `1`.
    AWSReset,
    /// Whether the unit is in startup
    // TODO: Verify this
    Startup,
    /// Get only: The current speed of the unit.
    Speedometer,
    // Events
    /// The save event, usually triggered by `F2`, triggered when set to `1`.
    PromptSave,
    /// Toggle labels, triggered when set to `1`.
    // TODO: Check if this is toggled or enabled.
    ToggleLabels,
    /// The 2D Map, usually triggered by `9`, triggered when set to `1`.
    Toggle2DMap,
    /// Toggle the HUD visibilty, usually triggered by `F3` or `F4`, triggered when set to `1`.
    ToggleHud,
    /// Currently undocumented.
    ToggleQut,
    /// Pause the game, triggered when set to `1`.
    Pause,
    /// Currently undocumented.
    DriverGuide,
    /// Show the rail vehicle number, enabled when set to `1`.
    // TODO: Check whether this is enabled or toggled.
    ToggleRvNumber,
    /// Close the dialog given to you by an assignment in a scenario.
    DialogAssignment,
    /// Switch the set of points to the front of the train, triggered when set to `1`.
    SwitchJunctionAhead,
    /// Switch the set of points to the rear of the train, triggered when set to `1`.
    SwitchJunctionBehind,
    /// The load cargo event, triggered when set to `1`.
    LoadCargo,
    /// The unload cargo action, triggered when set to `1`.
    UnloadCargo,
    /// Request to pass a signal at danger to the front of the train, usually triggered by `Tab`,
    /// triggered when set to `1`.
    PassAtDangerAhead,
    /// Request to pass a signal at danger to the rear of the train, usually triggered by
    /// `Shift Tab`, triggered when set to `1`.
    PassAtDangerBehind,
    /// Manual coupling, triggered when set to `1`.
    ManualCouple,
    // Camera
    /// The cab camera, usually operated by pressing `1`, switched to by setting to `1`.
    CabCamera,
    /// The follow camera, usually operated by pressing `2`, switched to by setting to `1`.
    FollowCamera,
    /// The head-out-window camera, usually operated by pressing `Shift 2`, switched to by setting
    /// to `1`.
    HeadOutCamera,
    /// The rear camera, usually operated by pressing `3`, switched to by setting to `1`.
    RearCamera,
    /// The track-side camera, usually operated by pressing `4`, switched to by setting to `1`.
    TrackSideCamera,
    /// The passenger-view (carriage) camera, usually operated by pressing `5`, switched to by
    /// setting to `1`.
    CarriageCamera,
    /// The coupling camera, usually operated by pressing `6`, switched to by setting to `1`.
    CouplingCamera,
    /// The yard camera, usually operated by pressing `7`, switched to by setting to `1`.
    YardCamera,
    /// Cab camera switch, usually operated by pressing `Ctrl +`, switched to by setting to `1`.
    SwitchToNextFrontCab,
    /// Cab camera switch, usually operated by pressing `Ctrl -`, switched to by setting to `1`.
    SwitchToNextRearCab,
    /// The free camera, usually operated by pressing `8`, switched to by setting to `1`.
    FreeCamera,
}

/// The value of data supposedly from the controller, although RailDriver does offer some
/// 'virtual' values.
#[derive(Debug)]
pub enum ControllerValue {
    /// Latitude of the player
    Latitude = 400,
    /// Longitude of the player
    Longitude,
    /// Fuel level of the train
    FuelLevel,
    /// Is the train in a tunnel
    InTunnel,
    /// Gradient of the track
    Gradient,
    /// Heading of the player
    Heading,
    /// Time of day, hour component
    TimeHours,
    /// Time of day, minute component
    TimeMinutes,
    /// Time of day, second component
    TimeSeconds,
}

#[derive(Debug)]
/// The kind of value that is required
pub enum Kind {
    /// The current value
    Current,
    /// The minimum value
    Min,
    /// The maximum value
    Max,
}

/// A custom `Result` type to simplify dealing with errors.
pub type Result<T> = std::result::Result<T, RailDriverError>;

#[derive(Debug)]
/// Errors returned by the operation being performed.
pub enum RailDriverError {
    /// The simulator doens't think this context is connected, therefore the operation cannot
    /// continue.
    NotConnected,
}

/// A controller context with the simulator.
pub struct Context {
    connected: bool,
}

impl Drop for Context {
    fn drop(&mut self) {
        self.disconnect();
    }
}

impl Context {

    /// Creates a new controller context with the simulator.
    pub fn new() -> Self {
        let mut val = Self {
            connected: false,
        };
        val.connect();
        val
    }

    fn connect(&mut self) {
        unsafe { libraildriver::SetRailDriverConnected(true); }
        self.connected = true;
    }

    fn disconnect(&mut self) {
        unsafe { libraildriver::SetRailDriverConnected(false); }
        self.connected = false;
    }

    /// Return the value of `of`. Depending on `kind`, this may return the current value, a
    /// minimum, or a maximum.
    pub fn get_value(&self, of: Value, kind: Kind) -> Result<f32> {
        if !self.connected {
            return Err(RailDriverError::NotConnected);
        }
        let of = of as libc::c_int;
        let kind = kind as libc::c_int;
        unsafe { Ok(libraildriver::GetRailSimValue(of, kind)) }
    }

    /// Set the value of `of` within the simulator to `value`.
    pub fn set_value(&self, of: Value, value: i32) -> Result<()> {
        if !self.connected {
            return Err(RailDriverError::NotConnected);
        }
        let of = of as libc::c_int;
        let value = value as libc::c_int;
        unsafe { libraildriver::SetRailSimValue(of, value); }
        Ok(())
    }

    /// Return the value of `of` from the virtual controller. Depending on `kind`, this may
    /// return the current value, a minimum, or a maximum.
    pub fn get_controller_value(&self, of: ControllerValue, kind: Kind) -> Result<f32> {
        if !self.connected {
            return Err(RailDriverError::NotConnected);
        }
        let of = of as libc::c_int;
        let kind = kind as libc::c_int;
        unsafe { Ok(libraildriver::GetControllerValue(of, kind)) }
    }
}