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
//! Safe wrappers for LinuxCNC's HAL (Hardware Abstraction Layer) //! //! # Examples //! //! These examples can be loaded into LinuxCNC using a HAL file similar to this: //! //! ```text //! loadusr -W /path/to/your/component/target/debug/comp_bin_name //! net input-1 spindle.0.speed-out pins.input-1 //! net output-1 pins.output-1 //! ``` //! //! ## Create a component with input and output //! //! This example creates a component called `"pins"` with a single input (`"input-1"`) and output //! pin (`"output-1"`). It enters an infinite loop updates the value of `output-1` every second. //! Signals are registered when `builder.ready()` is called which allow LinuxCNC to close the //! component gracefully. //! //! ```rust,no_run //! use linuxcnc_hal::{hal_pin::HalPinF64, HalComponentBuilder}; //! use std::{ //! error::Error, //! thread, //! time::{Duration, Instant}, //! }; //! //! fn main() -> Result<(), Box<dyn Error>> { //! // Create a new HAL component called `empty` //! let mut builder = HalComponentBuilder::new("pins")?; //! //! let input_1 = builder.register_input_pin::<HalPinF64>("input-1")?; //! //! let output_1 = builder.register_output_pin::<HalPinF64>("output-1")?; //! //! // All pins added, component is now ready. This consumes the builder and registers signal //! // handlers. //! let comp = builder.ready()?; //! //! let start = Instant::now(); //! //! // Main control loop //! while !comp.should_exit() { //! let time = start.elapsed().as_secs() as i32; //! //! // Set output pin to elapsed seconds since component started //! output_1.set_value(time.into())?; //! //! // Print the current value of the input pin //! println!("Input: {:?}", input_1.value()); //! //! // Sleep for 1000ms. This should be a lower time if the component needs to update more //! // frequently. //! thread::sleep(Duration::from_millis(1000)); //! } //! //! // The custom implementation of `Drop` for `HalComponent` ensures that `hal_exit()` is //! // called at this point. Registered signal handlers are also deregistered. //! //! Ok(()) //! } //! ``` #![deny(missing_docs)] #![deny(intra_doc_link_resolution_failure)] mod builder; mod check_readme; mod error; pub mod hal_pin; pub use crate::builder::HalComponentBuilder; use linuxcnc_hal_sys::hal_exit; use signal_hook::iterator::Signals; /// HAL component /// /// An initialised HAL component ready for use in the main component loop. /// /// Use [`HalComponentBuilder::new`] to create a new component builder. Once all pins are registered, /// calling `.ready()` on the builder will convert it to a `HalComponent` ready for use in the main /// loop. #[derive(Debug)] pub struct HalComponent { /// Component name name: &'static str, /// Component ID id: i32, /// Handles to Unix exit signals signals: Signals, } impl HalComponent { /// Get the HAL-assigned ID for this component pub fn id(&self) -> i32 { self.id } /// Get the component name pub fn name(&self) -> &str { self.name } /// Check whether the component was signalled to shut down pub fn should_exit(&self) -> bool { self.signals.pending().any(|signal| match signal { signal_hook::SIGTERM | signal_hook::SIGINT | signal_hook::SIGKILL => true, _ => false, }) } } impl Drop for HalComponent { /// Clean up resources /// /// Cleans up HAL resources by calling [`hal_exit`] as required by the docs. /// /// Also deregisters signal handlers fn drop(&mut self) { println!("Closing component ID {}, name {}", self.id, self.name); self.signals.close(); unsafe { hal_exit(self.id); } } } #[cfg(test)] mod tests { use super::*; use crate::error::ComponentInitError; #[test] fn name_too_long() { let comp = HalComponentBuilder::new( "name-thats-way-too-long-for-linuxcnc-to-handle-wow-this-is-ridiculous", ); assert_eq!(comp, Err(ComponentInitError::NameLength)); } }