#linux-cnc #unsafe-bindings #cnc #generated-bindings #ffi #high-level #input-pin

sys linuxcnc-hal-sys

为LinuxCNC HAL子模块生成的不可安全Rust绑定

10个版本

0.3.0 2022年11月15日
0.2.0 2021年1月6日
0.1.7 2020年1月29日

机器人 类别中排名 109

每月下载量 48
用于 2 crates

MIT/Apache

170KB
4K SLoC

生成LinuxCNC HAL Rust绑定

CircleCI Crates.io Docs.rs Liberapay

请考虑 成为赞助者,这样我可以在业余时间继续维护这个crate!

文档

此crate为LinuxCNC的HAL提供了使用 bindgen 生成的绑定。

建议用户代码使用 linuxcnc-hal 中的高级、安全接口。

开发设置

bindgen 必须设置正确。遵循其文档中的 要求部分

要运行和调试任何HAL组件,可以设置LinuxCNC模拟器。有关Linux Mint(以及其他Debian衍生版)的指南 在此

项目设置

构建此crate需要设置 LINUXCNC_SRC 环境变量。其值必须是LinuxCNC源代码根目录的绝对路径。

LinuxCNC源代码的版本必须与机器控制中使用的LinuxCNC版本相匹配。

# Clone LinuxCNC source code into linuxcnc/
git clone https://github.com/LinuxCNC/linuxcnc.git

# Check out a specific version tag. This may also be a commit, but must match the version in use by the machine control.
cd linuxcnc && git checkout v2.8.0 && cd ..

# Create your component lib
cargo new --lib my_comp

cd my_comp

# Add LinuxCNC HAL bindings as a Cargo dependency with cargo-edit
cargo add linuxcnc-hal-sys

LINUXCNC_SRC=/path/to/linuxcnc/source/code cargo build

示例

在LinuxCNC模拟器中运行示例

确保您已克隆了 LinuxCNC源代码仓库,检出到所需的版本,并按照 构建说明 进行构建。

请注意,LinuxCNC源代码位于以下示例路径中 linuxcnc-hal-rs 的相同父目录中。

LINUXCNC_SRC=$(realpath ../linuxcnc) cargo build --examples

# Define the correct path to the LinuxCNC source
. ../linuxcnc/scripts/rip-environment

linuxcnc ./linuxcnc-hal-sys/examples/<example>.ini

此crate导出的所有函数都是 unsafe,因此每个示例都包含在一个大的 unsafe 块中,以保持清晰。

LinuxCNC HAL需要一定的设置程序才能正确运行。基本程序结构应大致如下

  1. 调用 hal_init 创建新的HAL组件
  2. 注册 SIGTERMSIGINT 信号,可能使用 signal_hook crate。如果这些信号未注册,LinuxCNC 将挂起。
  3. 使用 hal_pin_float_newhal_pin_u32_new 等注册引脚
  4. 调用 hal_ready 通知 LinuxCNC 组件已准备好
  5. 进入无限循环以持续更新输入/输出引脚值并执行组件逻辑

这些示例可以通过类似此处的 HAL 文件加载到 LinuxCNC 中

loadusr -W /path/to/your/component/target/debug/comp_bin_name
net input-1 spindle.0.speed-out pins.input-1

如果 LinuxCNC 配置为就地运行,则启动时可能找不到 liblinuxcnchal.so.0。要修复,请尝试使用例如 export LD_LIBRARY_PATH=~/Repositories/linuxcnc/lib

创建输入引脚

此示例创建了一个名为 pins 的组件,并使用 hal_pin_float_new 向其注册了一个接受浮点值的输入引脚。每个 HAL 引脚都需要一些内存来存储其值,这通过 hal_malloc 完成。

可以使用类似此处的 HAL 文件将示例加载到 LinuxCNC 中

请注意,出于简洁起见,此示例中没有错误处理。

use linuxcnc_hal_sys::*;
use signal_hook::iterator::Signals;
use std::ffi::CString;
use std::mem;
use std::thread;
use std::time::Duration;

unsafe {
    let id = hal_init(CString::new("pins").unwrap().as_ptr() as *const i8);

    println!("ID {}", id);

    let signals = Signals::new(&[signal_hook::SIGTERM, signal_hook::SIGINT]).unwrap();

    let storage = hal_malloc(mem::size_of::<*mut f64>() as i64) as *mut *mut f64;

    println!("Storage {:?}", storage);

    let pin_name = CString::new("pins.input-1").unwrap();

    let ret = hal_pin_float_new(
        pin_name.as_ptr() as *const i8,
        hal_pin_dir_t_HAL_IN,
        storage,
        id,
    );

    println!("Pin init {}", ret);

    let ret = hal_ready(id);

    println!("Ready {}", ret);

    while !signals.pending().any(|signal| match signal {
        signal_hook::SIGTERM | signal_hook::SIGINT | signal_hook::SIGKILL => true,
        _ => false,
    }) {
        println!("Input {:?}", **storage);

        thread::sleep(Duration::from_millis(500));
    }
}

错误处理

此 crate 中的错误处理方式与 C 代码中的方式相同。导出了一些常量,如 EINVALEPERM,以便匹配返回的错误代码。

use linuxcnc_hal_sys::*;
use signal_hook::iterator::Signals;
use std::ffi::CString;
use std::mem;
use std::thread;
use std::time::Duration;

unsafe {
    let ret = hal_init(CString::new("pins").unwrap().as_ptr() as *const i8);

    // Check that component was created successfully
    let component_id = match ret {
        x if x == -(EINVAL as i32) => panic!("Failed to initialise component"),
        x if x == -(ENOMEM as i32) => panic!("Not enough memory to initialise component"),
        id if id > 0 => id,
        code => unreachable!("Hit unreachable error code {}", code),
    };

    println!("Component registered with ID {}", component_id);

    let signals = Signals::new(&[signal_hook::SIGTERM, signal_hook::SIGINT]).unwrap();

    let storage = hal_malloc(mem::size_of::<*mut f64>() as i64) as *mut *mut f64;

    if storage.is_null() {
        panic!("Failed to allocate storage");
    }

    let pin_name = CString::new("pins.input-1").unwrap();

    let ret = hal_pin_float_new(
        pin_name.as_ptr() as *const i8,
        hal_pin_dir_t_HAL_IN,
        storage,
        component_id,
    );

    // Check that pin was registered successfully
    match ret {
        0 => println!("Pin registered successfully"),
        x if x == -(EINVAL as i32) => panic!("Failed to register pin"),
        x if x == -(EPERM as i32) => {
            panic!("HAL is locked. Register pins before calling hal_ready()`")
        }
        x if x == -(ENOMEM as i32) => panic!("Failed to register pin"),
        code => unreachable!("Hit unreachable error code {}", code),
    }

    let ret = hal_ready(component_id);

    // Check that component is ready
    match ret {
        0 => println!("Component is ready"),
        x if x == -(EINVAL as i32) => panic!("HAL component was not found or is already ready"),
        code => unreachable!("Hit unreachable error code {}", code),
    }

    while !signals.pending().any(|signal| match signal {
        signal_hook::SIGTERM | signal_hook::SIGINT | signal_hook::SIGKILL => true,
        _ => false,
    }) {
        println!("Input {:?}", **storage);

        thread::sleep(Duration::from_millis(500));
    }
}

许可证

根据您的要求,许可为以下之一

任选。

贡献

除非您明确声明,否则任何有意提交以包含在您的工作中的贡献,根据 Apache-2.0 许可证定义,将按上述方式双重许可,不附加任何额外条款或条件。

依赖关系

~0–2MB
~39K SLoC