Simplify rust usage in EPICS
The full example can be found on rust-in-epics
Step 0: Initialize project
Create new crate
cargo new
...
Update Cargo.toml
[lib]
crate-type = ["dylib", "staticlib"]
[dependencies]
epics-sys = "0.0.1"
[build-dependencies]
bindgen = "0.32.1"
Step 1: Generate bindings for the record
Switch EPICS to clang
Go into configure/os/CONFIG_SITE.Common.linux-x86_64
and uncomment
GNU = NO
CMPLR_CLASS = clang
CC = clang
CCC = clang++
If compilerSpecific.h
is missing you might want to add the following line into your RULES
file:
USR_CPPFLAGS = -I${EPICS_BASE}/include/compiler/clang
Create build.rs script
extern crate bindgen;
use std::env;
use std::path::PathBuf;
use std::string::String;
fn main() {
let epics_base = PathBuf::from(env::var("EPICS_BASE").unwrap_or("/home/niklas/git/epics-base".into()));
let mut epics_include = epics_base.clone();
epics_include.push("include");
let mut epics_include_comp = epics_include.clone();
epics_include_comp.push("compiler");
epics_include_comp.push("clang");
let mut epics_include_os = epics_include.clone();
epics_include_os.push("os");
epics_include_os.push("Linux");
let mut sub_record = epics_include.clone();
sub_record.push("subRecord.h");
let mut registry_function = epics_include.clone();
registry_function.push("registryFunction.h");
let bindings = bindgen::Builder::default()
.header(sub_record.to_str().unwrap())
.header(registry_function.to_str().unwrap())
.clang_arg(epics_include.to_str().unwrap())
.clang_arg(String::from("-I") + epics_include_comp.to_str().unwrap())
.clang_arg(String::from("-I") + epics_include_os.to_str().unwrap())
.blacklist_type("max_align_t")
.trust_clang_mangling(false)
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}
Add the following to the top of your lib.rs
file.
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
Step 2: Create subRecord function and register it with EPICS
Add the following to your lib.rs
file.
#[macro_use]
extern crate epics_sys;
use epics_sys::str_from_epics;
epics_register_function!(
mySubProcess,
mySubProcess_priv,
subRecord,
register_func_mySubProcess,
pvar_func_register_func_mySubProcess
);
fn mySubProcess_priv(record: &mut subRecord) -> Result<(), ()> {
match str_from_epics(record.name.as_ref()) {
Ok(name) => println!("Hello from rust! name={:?}", name),
_ => println!("Invalid UTF8 in name"),
}
println!("A={:.2}", record.a);
record.val = quad(record.a);
Ok(())
}
Step 3: Configure EPICS application
Modify Makefile
to link to crate. In this example I've put the rust crate in the Application src folder.
<APPName>_LDFLAGS += -pthread
<APPName>_SYS_LIBS += dl
<APPName>_LIBS += <crate-name>
<crate-name>_DIR = ${TOP}/<APPName>/src/<crate-name>/target/debug
Add dbd file with content:
function(mySubProcess)
Add db file using your function
record(sub, "HELLO") {
field(SNAM, "mySubProcess")
}