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}