arfur_wpilib/
robot.rs

1//! Robot marker types.
2
3use tracing::trace;
4
5/// A robot marker type.
6///
7/// In short, a [`Robot`] is simply a marker that you have initialized the HAL.
8/// This is necessary because running HAL methods without initialization is UD.
9/// **Owning a `Robot` proves that the HAL is in working order**.
10///
11/// You can get your hands on a [`Robot`] in one of two ways:
12///
13///  * Construct a [`RobotBuilder`], and run its `initialize` method, or
14///  * Unsafely construct it without initializing the HAL with [`Self::unsafe_new`].
15///
16/// ```
17/// # fn main() {
18/// let robot: Robot = RobotBuilder::new().initialize();
19/// # }
20/// ```
21///
22/// Keep in mind that [`Self::unsafe_new`] defeats the point of the builder's
23/// `initialize` method. It does not run the necessary HAL initialization code.
24/// Only use this method if you are absolutely sure you need it. Using the
25/// builder pattern more than once is perfectly fine - WPILib gaurds for double
26/// initialization of the HAL internally.
27///
28/// See: [`RobotBuilder`]
29#[derive(derive_builder::Builder, Clone, Copy, Debug, PartialEq, Eq)]
30#[builder(
31    pattern = "owned",
32    build_fn(
33        name = "initialize",
34        error = "InitializationError",
35        validate = "Self::validate"
36    ),
37    derive(PartialEq, Eq)
38)]
39pub struct Robot {
40    /// The timeout argument provided to the HAL. The HAL will try to initialize
41    /// for `hal_timeout` ms. The default value is 500.
42    #[builder(default = "500", field(type = "i32"))]
43    hal_timeout: i32,
44    /// The mode argument provided to the HAL. See [`HALMode`].
45    #[builder(default, field(type = "HALMode"))]
46    hal_mode: HALMode,
47    #[builder(default, setter(skip))]
48    pub(self) _private: (),
49}
50
51impl Robot {
52    /// Unsafely construct a [`Robot`] type.
53    ///
54    /// You most probably don't want to use this, because creating a Robot
55    /// without initializing the HAL is undefined behaviour. Unless strictly
56    /// necessary, try using [`RobotBuilder`] instead.
57    pub unsafe fn unsafe_new(hal_timeout: i32, hal_mode: HALMode) -> Self {
58        Self {
59            hal_timeout,
60            hal_mode,
61            _private: (),
62        }
63    }
64}
65
66impl RobotBuilder {
67    /// Validates the builder by initializing the HAL and observing user program
68    /// start.
69    fn validate(&self) -> Result<(), InitializationError> {
70        unsafe {
71            use crate::ffi::root::{HAL_Initialize, HAL_ObserveUserProgramStarting};
72
73            // Initialize the HAL.
74            let status = HAL_Initialize(self.hal_timeout, self.hal_mode as i32);
75            if status != 1 {
76                return Err(InitializationError::HALInitializationError);
77            }
78
79            // Observe the start to the driver station, or else it will
80            // disable automatically.
81            //
82            // This itself is actually a wrapper around NI's NetComm library's
83            // report() interface.
84            HAL_ObserveUserProgramStarting();
85        }
86
87        trace!("Successfully instantiated robot!");
88
89        Ok(())
90    }
91}
92
93/// Error type for initialization.
94#[derive(thiserror::Error, Clone, Debug, PartialEq, Eq)]
95pub enum InitializationError {
96    #[error("tried to set the robot instance twice")]
97    DoubleInitialization,
98    #[error("failed to initilize HAL (for an unknown reason)")]
99    HALInitializationError,
100    #[error("failed to create a c-based string, this is probably a bug in Arfur itself and should be reported immediately")]
101    CStringConversionError(#[from] std::ffi::NulError),
102    #[error("uninitialized field found while building")]
103    UninitializedFieldError(String),
104}
105
106impl From<derive_builder::UninitializedFieldError> for InitializationError {
107    fn from(e: derive_builder::UninitializedFieldError) -> Self {
108        Self::UninitializedFieldError(e.field_name().to_string())
109    }
110}
111
112/// A mode for the HAL to start up in.
113#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
114pub enum HALMode {
115    /// Try to kill an existing HAL from another program, if not successful, error.
116    #[default]
117    Kill = 0,
118    /// Force kill a HAL from another program.
119    ForceKill = 1,
120    /// Just warn if another hal exists and cannot be killed. Will likely result in undefined behavior.
121    Warn = 2,
122}