linuxcnc_hal_sys/
lib.rs

1//! This crate provides generated bindings for LinuxCNC's HAL using [`bindgen`].
2//!
3//! The high level, safe interface at [`linuxcnc-hal`] is recommended for user code.
4//!
5//! # Development setup
6//!
7//! [`bindgen`](https://github.com/rust-lang/rust-bindgen) must be set up correctly. Follow the
8//! [requirements section of its docs](https://rust-lang.github.io/rust-bindgen/requirements.html).
9//!
10//! To run and debug any HAL components, the LinuxCNC simulator can be set up. There's a guide
11//! [here](https://wapl.es/cnc/2020/01/25/linuxcnc-simulator-build-linux-mint.html) for Linux Mint
12//! (and other Debian derivatives).
13//!
14//! # Project setup
15//!
16//! The `LINUXCNC_SRC` environment variable is required to build this crate. The value must be the
17//! absolute path to the root of the LinuxCNC source code.
18//!
19//! **The version of the LinuxCNC sources must match the LinuxCNC version used in the machine
20//! control.**
21//!
22//! ```bash
23//! # Clone LinuxCNC source code into linuxcnc/
24//! git clone https://github.com/LinuxCNC/linuxcnc.git
25//!
26//! # Check out a specific version tag. This may also be a commit, but must match the version in use by the machine control.
27//! cd linuxcnc && git checkout v2.8.0 && cd ..
28//!
29//! # Create your component lib
30//! cargo new --lib my_comp
31//!
32//! cd my_comp
33//!
34//! # Add LinuxCNC HAL bindings as a Cargo dependency with cargo-edit
35//! cargo add linuxcnc-hal-sys
36//!
37//! LINUXCNC_SRC=/path/to/linuxcnc/source/code cargo build
38//! ```
39//!
40//! # Examples
41//!
42//! ## Running the examples in the LinuxCNC simulator
43//!
44//! Ensure you have the [LinuxCNC source repository](https://github.com/linuxcnc/linuxcnc) cloned,
45//! checked out to the desired version and built with the [build
46//! instructions](http://linuxcnc.org/docs/devel/html/code/building-linuxcnc.html).
47//!
48//! Note that the LinuxCNC source is located in the same parent directory as `linuxcnc-hal-rs` in
49//! the example paths below.
50//!
51//! ```bash
52//! LINUXCNC_SRC=$(realpath ../linuxcnc) cargo build --examples
53//!
54//! # Define the correct path to the LinuxCNC source
55//! . ../linuxcnc/scripts/rip-environment
56//!
57//! linuxcnc ./linuxcnc-hal-sys/examples/<example>.ini
58//! ```
59//! All functions exported from this crate are `unsafe`, hence each example is wrapped in a big
60//! `unsafe` block for clarity.
61//!
62//! The LinuxCNC HAL requires a certain setup procedure to operate correctly. The basic program
63//! structure should be roughly as follows:
64//!
65//! 1. Call [`hal_init`] to create a new HAL component
66//! 1. Register `SIGTERM` and `SIGINT` signals, likely with the [`signal_hook`] crate. LinuxCNC will
67//! hang if these signals are not registered.
68//! 1. Register pins with [`hal_pin_float_new`], [`hal_pin_u32_new`], etc
69//! 1. Call [`hal_ready`] to signal to LinuxCNC that the component is ready
70//! 1. Enter an infinite loop to continuously update input/output pin values and perform component
71//! logic
72//!
73//! These examples can be loaded into LinuxCNC using a HAL file similar to this:
74//!
75//! ```hal
76//! loadusr -W /path/to/your/component/target/debug/comp_bin_name
77//! net input-1 spindle.0.speed-out pins.input-1
78//! ```
79//!
80//! If LinuxCNC is configured to run in place, `liblinuxcnchal.so.0` may not be found on startup. To
81//! fix, try setting the library path with e.g. `export LD_LIBRARY_PATH=~/Repositories/linuxcnc/lib`
82//!
83//! ## Create an input pin
84//!
85//! This example creates a component called `pins` and registers an input pin to it that accepts a
86//! floating point value using [`hal_pin_float_new`]. Each HAL pin requires some memory allocated to
87//! store its value which is performed with [`hal_malloc`].
88//!
89//! The example can be loaded into LinuxCNC using a HAL file similar to this:
90//!
91//! **Note that there is no error handling in this example for brevity.**
92//!
93//! ```rust,no_run
94//! use linuxcnc_hal_sys::*;
95//! use signal_hook::iterator::Signals;
96//! use std::ffi::CString;
97//! use std::mem;
98//! use std::thread;
99//! use std::time::Duration;
100//!
101//! unsafe {
102//!     let id = hal_init(CString::new("pins").unwrap().as_ptr() as *const u8);
103//!
104//!     println!("ID {}", id);
105//!
106//!     let mut signals = Signals::new(&[signal_hook::consts::SIGTERM, signal_hook::consts::SIGINT]).unwrap();
107//!
108//!     let storage = hal_malloc(mem::size_of::<*mut f64>() as i64) as *mut *mut f64;
109//!
110//!     println!("Storage {:?}", storage);
111//!
112//!     let pin_name = CString::new("pins.input-1").unwrap();
113//!
114//!     let ret = hal_pin_float_new(
115//!         pin_name.as_ptr() as *const u8,
116//!         hal_pin_dir_t_HAL_IN,
117//!         storage,
118//!         id,
119//!     );
120//!
121//!     println!("Pin init {}", ret);
122//!
123//!     let ret = hal_ready(id);
124//!
125//!     println!("Ready {}", ret);
126//!
127//!     while !signals.pending().any(|signal| match signal {
128//!         signal_hook::consts::SIGTERM | signal_hook::consts::SIGINT | signal_hook::consts::SIGKILL => true,
129//!         _ => false,
130//!     }) {
131//!         println!("Input {:?}", **storage);
132//!
133//!         thread::sleep(Duration::from_millis(500));
134//!     }
135//! }
136//! ```
137//!
138//! ## Error handling
139//!
140//! Errors are handled in this crate the same way as in the C code. Some consts are exported like
141//! [`EINVAL`] and [`EPERM`] to allow matching of returned error codes.
142//!
143//! ```rust,no_run
144//! use linuxcnc_hal_sys::*;
145//! use signal_hook::iterator::Signals;
146//! use std::ffi::CString;
147//! use std::mem;
148//! use std::thread;
149//! use std::time::Duration;
150//!
151//! unsafe {
152//!     let ret = hal_init(CString::new("pins").unwrap().as_ptr() as *const u8);
153//!
154//!     // Check that component was created successfully
155//!     let component_id = match ret {
156//!         x if x == -(EINVAL as i32) => panic!("Failed to initialise component"),
157//!         x if x == -(ENOMEM as i32) => panic!("Not enough memory to initialise component"),
158//!         id if id > 0 => id,
159//!         code => unreachable!("Hit unreachable error code {}", code),
160//!     };
161//!
162//!     println!("Component registered with ID {}", component_id);
163//!
164//!     let mut signals = Signals::new(&[signal_hook::consts::SIGTERM, signal_hook::consts::SIGINT]).unwrap();
165//!
166//!     let storage = hal_malloc(mem::size_of::<*mut f64>() as i64) as *mut *mut f64;
167//!
168//!     if storage.is_null() {
169//!         panic!("Failed to allocate storage");
170//!     }
171//!
172//!     let pin_name = CString::new("pins.input-1").unwrap();
173//!
174//!     let ret = hal_pin_float_new(
175//!         pin_name.as_ptr() as *const u8,
176//!         hal_pin_dir_t_HAL_IN,
177//!         storage,
178//!         component_id,
179//!     );
180//!
181//!     // Check that pin was registered successfully
182//!     match ret {
183//!         0 => println!("Pin registered successfully"),
184//!         x if x == -(EINVAL as i32) => panic!("Failed to register pin"),
185//!         x if x == -(EPERM as i32) => {
186//!             panic!("HAL is locked. Register pins before calling hal_ready()`")
187//!         }
188//!         x if x == -(ENOMEM as i32) => panic!("Failed to register pin"),
189//!         code => unreachable!("Hit unreachable error code {}", code),
190//!     }
191//!
192//!     let ret = hal_ready(component_id);
193//!
194//!     // Check that component is ready
195//!     match ret {
196//!         0 => println!("Component is ready"),
197//!         x if x == -(EINVAL as i32) => panic!("HAL component was not found or is already ready"),
198//!         code => unreachable!("Hit unreachable error code {}", code),
199//!     }
200//!
201//!     while !signals.pending().any(|signal| match signal {
202//!         signal_hook::consts::SIGTERM | signal_hook::consts::SIGINT | signal_hook::consts::SIGKILL => true,
203//!         _ => false,
204//!     }) {
205//!         println!("Input {:?}", **storage);
206//!
207//!         thread::sleep(Duration::from_millis(500));
208//!     }
209//! }
210//! ```
211//! [`linuxcnc-hal`]: https://docs.rs/linuxcnc-hal
212//! [`bindgen`]: https://docs.rs/bindgen
213//! [`signal_hook`]: https://docs.rs/signal_hook
214
215#![deny(rustdoc::broken_intra_doc_links)]
216#![allow(non_upper_case_globals)]
217#![allow(non_camel_case_types)]
218#![allow(non_snake_case)]
219
220include!("generated.rs");