linuxcnc_hal/
component.rs1use crate::{error::ComponentInitError, RegisterResources, Resources};
2use linuxcnc_hal_sys::{hal_exit, hal_init, hal_ready, EINVAL, ENOMEM, HAL_NAME_LEN};
3use signal_hook::iterator::Signals;
4use std::{cell::RefCell, ffi::CString};
5
6#[derive(Debug)]
18pub struct HalComponent<R> {
19 name: &'static str,
21
22 id: i32,
24
25 signals: RefCell<Signals>,
27
28 resources: Option<R>,
34}
35
36impl<R> HalComponent<R>
37where
38 R: Resources,
39{
40 pub fn new(name: &'static str) -> Result<Self, ComponentInitError> {
45 let id = Self::create_component(name)?;
46
47 let resources = R::register_resources(&RegisterResources { id, name })
48 .map_err(|e| ComponentInitError::ResourceRegistration(e.into()))?;
49
50 let signals = Self::register_signals()?;
51
52 let comp = Self {
53 name,
54 id,
55 resources: Some(resources),
56 signals: RefCell::new(signals),
57 };
58
59 comp.ready()
60 }
61
62 fn register_signals() -> Result<Signals, ComponentInitError> {
68 let signals = Signals::new(&[signal_hook::consts::SIGTERM, signal_hook::consts::SIGINT])
69 .map_err(ComponentInitError::Signals)?;
70
71 debug!("Signals registered");
72
73 Ok(signals)
74 }
75
76 fn create_component(name: &'static str) -> Result<i32, ComponentInitError> {
86 if name.len() > HAL_NAME_LEN as usize {
87 error!(
88 "Component name must be no longer than {} bytes",
89 HAL_NAME_LEN
90 );
91
92 Err(ComponentInitError::NameLength)
93 } else {
94 let name_c = CString::new(name).map_err(|_| ComponentInitError::InvalidName)?;
95
96 let id = unsafe { hal_init(name_c.as_ptr().cast()) };
97
98 match id {
99 x if x == -(EINVAL as i32) => Err(ComponentInitError::Init),
100 x if x == -(ENOMEM as i32) => Err(ComponentInitError::Memory),
101 id if id > 0 => {
102 debug!("Init component {} with ID {}", name, id);
103
104 Ok(id)
105 }
106 code => unreachable!("Hit unreachable error code {}", code),
107 }
108 }
109 }
110
111 fn ready(self) -> Result<Self, ComponentInitError> {
113 let ret = unsafe { hal_ready(self.id) };
114
115 match ret {
116 x if x == -(EINVAL as i32) => Err(ComponentInitError::Ready),
117 0 => {
118 debug!("Component is ready");
119
120 Ok(self)
121 }
122 ret => unreachable!("Unknown error status {} returned from hal_ready()", ret),
123 }
124 }
125
126 pub fn id(&self) -> i32 {
128 self.id
129 }
130
131 pub fn name(&self) -> &str {
133 self.name
134 }
135
136 pub fn should_exit(&self) -> bool {
138 self.signals.borrow_mut().pending().any(|signal| {
139 matches!(
140 signal,
141 signal_hook::consts::SIGTERM
142 | signal_hook::consts::SIGINT
143 | signal_hook::consts::SIGKILL
144 )
145 })
146 }
147
148 pub fn resources(&self) -> &R {
150 &self.resources.as_ref().unwrap()
152 }
153}
154
155impl<R> Drop for HalComponent<R> {
156 fn drop(&mut self) {
158 self.resources = None;
160
161 debug!("Closing component ID {}, name {}", self.id, self.name);
162
163 unsafe {
166 hal_exit(self.id);
167 }
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use crate::{
175 error::{ComponentInitError, PinRegisterError},
176 RegisterResources,
177 };
178
179 #[derive(Debug)]
180 struct EmptyResources {}
181 impl Resources for EmptyResources {
182 type RegisterError = PinRegisterError;
183
184 fn register_resources(_comp: &RegisterResources) -> Result<Self, Self::RegisterError> {
185 Ok(Self {})
186 }
187 }
188
189 #[test]
190 fn name_too_long() -> Result<(), ComponentInitError> {
191 let comp = HalComponent::<EmptyResources>::new(
192 "name-thats-way-too-long-for-linuxcnc-to-handle-wow-this-is-ridiculous",
193 );
194
195 println!("{:?}", comp);
196
197 match comp {
198 Err(ComponentInitError::NameLength) => Ok(()),
199 Err(e) => Err(e),
200 Ok(_) => Err(ComponentInitError::Init),
201 }
202 }
203}