使用rust调用c++静态库并编译nodejs包
在项目上经常要用到身份证阅读器、护照阅读仪、指纹仪等各种品牌硬件,假如每套系统的都做集成开发那代码的维护成本将变得很高,为此采用rust来调用厂家提供的sdk c++开发包并封装成nodejs包,用fastify来开发成web api独立的服务形式。这样我们开发系统时只需调用web接口即可,跨平台又可共用,方便快捷,话不多说来看代码如何实现。
一、创建项目
安装rust后,打开vs新建一个工程目录,我们通过cargo new创建的一个package项目,加上--lib参数后创建的项目就是库项目(library package)。
cargo new --lib reader
package 就是一个项目,因此它包含有独立的 Cargo.toml 文件,用于项目配置。库项目只能作为三方库被其它项目引用,而不能独立运行,即src/lib.rs。
典型的package
如果一个 package 同时拥有 src/main.rs 和 src/lib.rs,那就意味着它包含两个包:库包和二进制包,这两个包名也都是 test_math —— 都与 package 同名。
一个真实项目中典型的 package,会包含多个二进制包,这些包文件被放在 src/bin 目录下,每一个文件都是独立的二进制包,同时也会包含一个库包,该包只能存在一个 src/lib.rs:
.
├── Cargo.toml
├── Cargo.lock
├── src
│ ├── main.rs
│ ├── lib.rs
│ └── bin
│ └── main1.rs
│ └── main2.rs
├── tests
│ └── some_integration_tests.rs
├── benches
│ └── simple_bench.rs
└── examples
└── simple_example.rs
唯一库包:src/lib.rs
默认二进制包:src/main.rs,编译后生成的可执行文件与package同名
其余二进制包:src/bin/main1.rs 和 src/bin/main2.rs,它们会分别生成一个文件同名的二进制可执行文件
集成测试文件:tests 目录下
性能测试benchmark文件:benches 目录下
项目示例:examples 目录下
这种目录结构基本上是 Rust 的标准目录结构,在 github 的大多数项目上,你都将看到它的身影。
运行Cargo build命令,我们在target\debug目录下可以看到编译后的结果。
二、Cargo.toml
[package]
name = "reader"
version = "0.1.0"
edition = "2018"
exclude = ["reader.node"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2.9"
libloading = "0.7"
once_cell = "1.8"
serde = { version = "1.0", features = ["derive"] }
widestring = "0.5.1"
serde_json = "1.0"
base64 = "0.13"
hex="0.4.2"
encoding = "0.2"
tokio={version="1.18.0",features = ["full"]}
[dependencies.neon]
version = "0.9"
default-features = false
features = ["napi-5", "channel-api"]
[lib]
crate-type = ["cdylib"]
三、package.json
{
"name": "reader",
"version": "0.1.0",
"description": "",
"main": "index.node",
"scripts": {
"build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
"build-debug": "npm run build --",
"build-release": "npm run build -- --release",
"build_win32": "npm run build -- --release --target=i686-pc-windows-msvc",
"test": "cargo test",
"run": "cargo run"
},
"author": "",
"license": "ISC",
"devDependencies": {
"cargo-cp-artifact": "^0.1"
},
"dependencies": {
"express": "^4.17.3"
}
}
我们可以打印rust看看编译输出支持哪些架构
rustc --print target-list
//添加 x86编译链接器
rustup target add i686-pc-windows-msvc
四、代码分析
use std::collections::HashMap;
use std::str;
use std::fmt::Write;
use std::io::{Error};
extern crate encoding;
use encoding::all::GB18030;
use encoding::{DecoderTrap,EncoderTrap,Encoding};
use tokio::time::{sleep, Duration,Instant};
use libc::{c_int, c_void};
use libloading::{Library, Symbol};
use neon::prelude::*;
use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};
use widestring::{WideCStr, WideCString, WideChar};
// 编码转换 utf8 -> utf16le
fn encode(source: &str) -> WideCString {
let string_source = source.to_string() + "\0";
WideCString::from_str(&string_source).unwrap()
}
// 解码转换 utf16le -> utf8
fn decode(source: &[WideChar]) -> String {
WideCStr::from_slice_truncate(source)
.unwrap()
.to_string()
.unwrap()
}
// 加载 dll
static LIBRARY: OnceCell<Library> = OnceCell::new();
//指定编译架构
static MACHINE_KIND: &str = if cfg!(target_os = "windows") {
if cfg!(target_arch = "x86") {
"win32"
} else if cfg!(target_arch = "x86_x64") {
"win64"
} else {
"other"
}
} else if cfg!(target_os = "linux") {
if cfg!(target_arch = "x86") {
"linux32"
} else if cfg!(target_arch = "x86_64") {
"linux64"
} else if cfg!(target_arch = "aarch64") {
"aarch64"
} else if cfg!(target_arch = "arm") {
"arm"
} else {
"other"
}
} else {
"other"
};
//定义函数方法名,这里要根据c++库的函数名和参数来定义,函数名和参数类型务必要一致。
type LPCTSTR = *const WideChar;
type BOOL = c_int;
type INITPTR = *const i8;
type CANRST = *mut WideChar;
// 打开设备
type S2V7_open = unsafe extern "system" fn() -> c_int;
// 关闭设备
type S2V7_close = unsafe extern "system" fn() -> c_int;
//【set mode 设置读证功能】
type S2V7_set_mode =
unsafe extern "system" fn(flg_takeColor: c_int, flg_takeUV: c_int, flg_readChipInfo: c_int, flg_readChipFace: c_int) -> c_int; // Type = 0 即可
//【wait Doc. in 等待放卡】
type S2V7_wait_DocIn =
unsafe extern "system" fn(timeout: f64, flg_in: INITPTR) -> c_int; // Type = 0 即可
//【wait Doc. out 等待拿卡】
type S2V7_wait_DocOut =
unsafe extern "system" fn(timeout: f64, flg_out: INITPTR) -> c_int; // Type = 0 即可
//【process 执行读卡过程】
type S2V7_process = unsafe extern "system" fn() -> c_int;
//读取卡类型
type S2V7_get_cardType = unsafe extern "system" fn() -> c_int;
//保存彩照
type S2V7_VIS_saveColor = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;
//保存红外照
type S2V7_VIS_saveIR = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;
//【get MRZ text 获取OCR文字信息】
type S2V7_VIS_getMRZtext = unsafe extern "system" fn(text: LPCTSTR) -> c_int;
//show text information 文字信息
type S2V7_RDO_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;
type S2V7_VIS_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;
type S2V7_RF_active = unsafe extern "system" fn(antenna: c_int,atr: LPCTSTR, atr_len: c_int) -> c_int;
//构建函数实例
static V7_OPEN: OnceCell<Symbol<S2V7_open>> = OnceCell::new();
static V7_CLOSE: OnceCell<Symbol<S2V7_close>> = OnceCell::new();
static V7_SET_MODE: OnceCell<Symbol<S2V7_set_mode>> = OnceCell::new();
static V7_WAIT_DOCINT: OnceCell<Symbol<S2V7_wait_DocIn>> = OnceCell::new();
static V7_WAIT_DOCOUT: OnceCell<Symbol<S2V7_wait_DocOut>> = OnceCell::new();
static V7_PROCESS: OnceCell<Symbol<S2V7_process>> = OnceCell::new();
static V7_GET_CARDTYPE: OnceCell<Symbol<S2V7_get_cardType>> = OnceCell::new();
static V7_VIS_SAVECOLOR: OnceCell<Symbol<S2V7_VIS_saveColor>> = OnceCell::new();
static V7_VIS_SAVEIR: OnceCell<Symbol<S2V7_VIS_saveIR>> = OnceCell::new();
static V7_VIS_GETMRZTEXT: OnceCell<Symbol<S2V7_VIS_getMRZtext>> = OnceCell::new();
static V7_RDO_getBytesByIndex: OnceCell<Symbol<S2V7_RDO_getBytesByIndex>> = OnceCell::new();
static V7_VIS_getBytesByIndex: OnceCell<Symbol<S2V7_VIS_getBytesByIndex>> = OnceCell::new();
static V7_RF_active: OnceCell<Symbol<S2V7_RF_active>> = OnceCell::new();
// 对外导出函数方法
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("init", init_by_node)?;
cx.export_function("start", start)?;
}
//加载dll并对函数进行初始化操作
pub fn init_by_node(mut cx: FunctionContext) -> JsResult<JsNumber> {
//外部传进来的参数(根据自己的需要来定义)
let directory = cx.argument::<JsString>(0)?.value(&mut cx);
let userid = cx.argument::<JsString>(1)?.value(&mut cx);
unsafe {
DIRECTORY_PATH.take();
DIRECTORY_PATH.set(directory).unwrap();
USER_ID.take();
USER_ID.set(userid).unwrap();
};
let result = init() as f64;
Ok(cx.number(result))
}
//核心代码,加载dll函数并映射
fn init() -> c_int {
let directory = unsafe { DIRECTORY_PATH.get().unwrap() };
let userid = unsafe { USER_ID.get().unwrap() };
let directory_path = std::path::Path::new(directory).join(MACHINE_KIND);
if directory_path.exists() {
let dll_path = directory_path.join(libloading::library_filename("STAR200_V7_DRV"));
println!("dll_path: {:?}", dll_path);
if dll_path.exists() {
match init_dll(dll_path.to_str().unwrap()).is_ok() {
true => {
// 打开设备
let init_result = unsafe {V7_OPEN.get_unchecked()()};
if init_result == 0 {
println!("设备打开成功");
return ResultType::Success as c_int;
} else {
println!("设备打开失败,代码:{:?}",init_result);
return ResultType::DeviceNotFound as c_int;
}
}
false => {
return ResultType::INITDLLFail as c_int;
}
}
} else {
return ResultType::DllPathNotExist as c_int;
}
} else {
println!("{:?}", directory_path);
return ResultType::DirectoryPathNotExist as c_int;
}
}
// 加载dll
fn init_dll(dll_path: &str) -> Result<bool, Box<dyn std::error::Error>> {
unsafe {
if INITDLL {
return Ok(true);
}
}
println!("加载dll");
println!("dll_path");
let library = LIBRARY.get_or_init(|| unsafe { Library::new(dll_path).unwrap() });
println!("S2V7_open");
V7_OPEN.get_or_init(|| unsafe { library.get::<S2V7_open>(b"S2V7_open").unwrap() });
println!("S2V7_close");
V7_CLOSE.get_or_init(|| unsafe { library.get::<S2V7_close>(b"S2V7_close").unwrap() });
println!("S2V7_set_mode");
V7_SET_MODE.get_or_init(|| unsafe {library.get::<S2V7_set_mode>(b"S2V7_set_mode").unwrap()});
println!("S2V7_wait_DocIn");
V7_WAIT_DOCINT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocIn>(b"S2V7_wait_DocIn").unwrap() });
println!("S2V7_wait_DocOut");
V7_WAIT_DOCOUT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocOut>(b"S2V7_wait_DocOut").unwrap() });
V7_PROCESS.get_or_init(|| unsafe { library.get::<S2V7_process>(b"S2V7_process").unwrap() });
V7_GET_CARDTYPE.get_or_init(|| unsafe { library.get::<S2V7_get_cardType>(b"S2V7_get_cardType").unwrap() });
V7_VIS_SAVECOLOR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveColor>(b"S2V7_VIS_saveColor").unwrap() });
V7_VIS_SAVEIR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveIR>(b"S2V7_VIS_saveIR").unwrap() });
V7_VIS_GETMRZTEXT.get_or_init(|| unsafe { library.get::<S2V7_VIS_getMRZtext>(b"S2V7_VIS_getMRZtext").unwrap() });
V7_RDO_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_RDO_getBytesByIndex>(b"S2V7_RDO_getBytesByIndex").unwrap() });
V7_VIS_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_VIS_getBytesByIndex>(b"S2V7_VIS_getBytesByIndex").unwrap() });
V7_RF_active.get_or_init(|| unsafe { library.get::<S2V7_RF_active>(b"S2V7_RF_active").unwrap() });
unsafe {
INITDLL = true;
}
Ok(true)
}
//创建新线程来监测设备读证操作
fn start(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
let mut channel = cx.channel();
channel.reference(&mut cx);
println!("start {}", channel.has_ref());
let index = unsafe {
DEVICE_START_INDEX += 1;
DEVICE_START_INDEX
};
std::thread::spawn(move || {
// Do the heavy lifting inside the background thread.
device_start(callback, channel, index);
});
Ok(cx.undefined())
}
use std::sync::{Arc, Mutex};
fn device_start(callback: Root<JsFunction>, channel: Channel, index: u64) {
let index = index;
let callback = Arc::new(Mutex::new(callback));
//设置读证功能
unsafe { V7_SET_MODE.get_unchecked()(1,1,1,1) };
loop {
if index != unsafe { DEVICE_START_INDEX } {
break;
};
let callback_clone = Arc::clone(&callback);
let mut result = RecogIDCardEXResult::default();
let mut flg_in:i8=0;
match unsafe { V7_WAIT_DOCINT.get_unchecked()(5.0,&mut flg_in) } {
// 设备正常 检测是否有放入证件
0 => {
if flg_in==0{
//检查是否放入超时
result.record_type = ResultType::CheckCardNotInOrOut as i32;
break;
}
result.device_online = true;
result.device_name =unsafe { DEVCIE_NAME.get_or_init(|| "".to_string()).to_string() };
match unsafe { V7_PROCESS.get_unchecked()() } {
// 证件有放入
0 => {
result.record_type = ResultType::CheckCardInput as i32;
}
// 未检测到OCR区域
-1 => {
result.record_type = ResultType::OCRFail as i32;
}
// 设备离线
-3 => {
result.device_online = false;
result.record_type = init();
}
_ => {
result.record_type = ResultType::Unknown as i32;
}
}
}
-3 => {
//设备离线
let init = init();
result.device_online = false;
result.record_type = init;
}
_ => {
//未知错误
result.record_type = ResultType::Unknown as i32;
}
};
if unsafe { *NEED_RECORD.get_or_init(|| false) } {
println!("手工点击识别+1");
result.record_type = ResultType::CheckCardInput as i32;
}
// let time_now = std::time::Instant::now();
if result.record_type == ResultType::CheckCardInput as i32 {
let _result = recog_card();
result.success = _result.success;
result.img_base64 = _result.img_base64;
result.reg_info = _result.reg_info;
result.card_type = _result.card_type;
result.card_name = _result.card_name;
}
let neet_sendinfo = if Some(true) == unsafe { NEED_RECORD.take() } {
true
} else {
false
};
// let elapsed = time_now.elapsed();
// println!("识别时间结束时间 {:.6?}", elapsed);
if result.record_type != ResultType::CheckCardNotInOrOut as i32
&& (*unsafe { RESULT_TYPE.get_or_init(|| -10000) } != result.record_type
|| result.record_type == ResultType::CheckCardInput as i32
|| neet_sendinfo)
{
unsafe {
RESULT_TYPE.take();
RESULT_TYPE.set(result.record_type).unwrap();
}
channel.send(move |mut cx| {
let result_json = serde_json::to_string(&result).unwrap();
let callback = callback_clone.lock().unwrap().to_inner(&mut cx);
let this = cx.undefined();
let args = vec![cx.string(&result_json)];
callback.call(&mut cx, this, args)?;
Ok(())
});
}
std::thread::sleep(std::time::Duration::from_millis(20));
}
}
完整源码
点击查看代码
use std::collections::HashMap;
use libc::{c_int, c_void};
use libloading::{Library, Symbol};
use neon::prelude::*;
use once_cell::sync::OnceCell;
use serde::Serialize;
extern crate encoding;
use encoding::all::GB18030;
use encoding::{DecoderTrap,EncoderTrap,Encoding};
use widestring::{WideCStr, WideCString, WideChar};
// 编码转换 utf8 -> utf16le
fn encode(source: &str) -> WideCString {
let string_source = source.to_string() + "\0";
WideCString::from_str(&string_source).unwrap()
}
// 解码转换 utf16le -> utf8
fn decode(source: &[WideChar]) -> String {
WideCStr::from_slice_truncate(source)
.unwrap()
.to_string()
.unwrap()
}
// 加载 dll
static LIBRARY: OnceCell<Library> = OnceCell::new();
static MACHINE_KIND: &str = if cfg!(target_os = "windows") {
if cfg!(target_arch = "x86") {
"win32"
} else if cfg!(target_arch = "x86_x64") {
"win64"
} else {
"other"
}
} else if cfg!(target_os = "linux") {
if cfg!(target_arch = "x86") {
"linux32"
} else if cfg!(target_arch = "x86_64") {
"linux64"
} else if cfg!(target_arch = "aarch64") {
"aarch64"
} else if cfg!(target_arch = "arm") {
"arm"
} else {
"other"
}
} else {
"other"
};
//设置识别的证件 ID
// 设置当前要识别的证件类型,并将
// 之前已经设置的证件类型清除。
// nMainID 主要识别类型,nSubID 子类型
// nSubID 头指针,默认将数组
// nSubID 第 一 个 元 素 赋 值 为 0 即
// nSubID[0]=0
// type S = c_int[];
type LPCTSTR = *const WideChar;
type BOOL = c_int;
type INITPTR = *const i8;
type CANRST = *mut WideChar;
// 打开设备
type S2V7_open = unsafe extern "system" fn() -> c_int;
// 关闭设备
type S2V7_close = unsafe extern "system" fn() -> c_int;
//【set mode 设置读证功能】
type S2V7_set_mode =
unsafe extern "system" fn(flg_takeColor: c_int, flg_takeUV: c_int, flg_readChipInfo: c_int, flg_readChipFace: c_int) -> c_int; // Type = 0 即可
//【wait Doc. in 等待放卡】
type S2V7_wait_DocIn =
unsafe extern "system" fn(timeout: f64, flg_in: INITPTR) -> c_int; // Type = 0 即可
//【wait Doc. out 等待拿卡】
type S2V7_wait_DocOut =
unsafe extern "system" fn(timeout: f64, flg_out: INITPTR) -> c_int; // Type = 0 即可
//【process 执行读卡过程】
type S2V7_process = unsafe extern "system" fn() -> c_int;
//读取卡类型
type S2V7_get_cardType = unsafe extern "system" fn() -> c_int;
//保存彩照
type S2V7_VIS_saveColor = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;
//保存红外照
type S2V7_VIS_saveIR = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;
//【get MRZ text 获取OCR文字信息】
type S2V7_VIS_getMRZtext = unsafe extern "system" fn(text: LPCTSTR) -> c_int;
//show text information 文字信息
type S2V7_RDO_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;
type S2V7_VIS_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;
type S2V7_RF_active = unsafe extern "system" fn(antenna: c_int,atr: LPCTSTR, atr_len: c_int) -> c_int;
static V7_OPEN: OnceCell<Symbol<S2V7_open>> = OnceCell::new();
static V7_CLOSE: OnceCell<Symbol<S2V7_close>> = OnceCell::new();
static V7_SET_MODE: OnceCell<Symbol<S2V7_set_mode>> = OnceCell::new();
static V7_WAIT_DOCINT: OnceCell<Symbol<S2V7_wait_DocIn>> = OnceCell::new();
static V7_WAIT_DOCOUT: OnceCell<Symbol<S2V7_wait_DocOut>> = OnceCell::new();
static V7_PROCESS: OnceCell<Symbol<S2V7_process>> = OnceCell::new();
static V7_GET_CARDTYPE: OnceCell<Symbol<S2V7_get_cardType>> = OnceCell::new();
static V7_VIS_SAVECOLOR: OnceCell<Symbol<S2V7_VIS_saveColor>> = OnceCell::new();
static V7_VIS_SAVEIR: OnceCell<Symbol<S2V7_VIS_saveIR>> = OnceCell::new();
static V7_VIS_GETMRZTEXT: OnceCell<Symbol<S2V7_VIS_getMRZtext>> = OnceCell::new();
static V7_RDO_getBytesByIndex: OnceCell<Symbol<S2V7_RDO_getBytesByIndex>> = OnceCell::new();
static V7_VIS_getBytesByIndex: OnceCell<Symbol<S2V7_VIS_getBytesByIndex>> = OnceCell::new();
static V7_RF_active: OnceCell<Symbol<S2V7_RF_active>> = OnceCell::new();
// static
static mut INITDLL: bool = false;
static mut DEVICE_START_INDEX: u64 = 0;
static mut DIRECTORY_PATH: OnceCell<String> = OnceCell::new();
static mut USER_ID: OnceCell<String> = OnceCell::new();
static mut DEVCIE_NAME: OnceCell<String> = OnceCell::new();
static mut RESULT_TYPE: OnceCell<i32> = OnceCell::new();
static mut NEED_RECORD: OnceCell<bool> = OnceCell::new();
// 初始化dll
fn init_dll(dll_path: &str) -> Result<bool, Box<dyn std::error::Error>> {
unsafe {
if INITDLL {
return Ok(true);
}
}
println!("加载dll");
println!("dll_path");
let library = LIBRARY.get_or_init(|| unsafe { Library::new(dll_path).unwrap() });
println!("S2V7_open");
V7_OPEN.get_or_init(|| unsafe { library.get::<S2V7_open>(b"S2V7_open").unwrap() });
println!("S2V7_close");
V7_CLOSE.get_or_init(|| unsafe { library.get::<S2V7_close>(b"S2V7_close").unwrap() });
println!("S2V7_set_mode");
V7_SET_MODE.get_or_init(|| unsafe {library.get::<S2V7_set_mode>(b"S2V7_set_mode").unwrap()});
println!("S2V7_wait_DocIn");
V7_WAIT_DOCINT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocIn>(b"S2V7_wait_DocIn").unwrap() });
println!("S2V7_wait_DocOut");
V7_WAIT_DOCOUT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocOut>(b"S2V7_wait_DocOut").unwrap() });
V7_PROCESS.get_or_init(|| unsafe { library.get::<S2V7_process>(b"S2V7_process").unwrap() });
V7_GET_CARDTYPE.get_or_init(|| unsafe { library.get::<S2V7_get_cardType>(b"S2V7_get_cardType").unwrap() });
V7_VIS_SAVECOLOR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveColor>(b"S2V7_VIS_saveColor").unwrap() });
V7_VIS_SAVEIR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveIR>(b"S2V7_VIS_saveIR").unwrap() });
V7_VIS_GETMRZTEXT.get_or_init(|| unsafe { library.get::<S2V7_VIS_getMRZtext>(b"S2V7_VIS_getMRZtext").unwrap() });
V7_RDO_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_RDO_getBytesByIndex>(b"S2V7_RDO_getBytesByIndex").unwrap() });
V7_VIS_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_VIS_getBytesByIndex>(b"S2V7_VIS_getBytesByIndex").unwrap() });
V7_RF_active.get_or_init(|| unsafe { library.get::<S2V7_RF_active>(b"S2V7_RF_active").unwrap() });
unsafe {
INITDLL = true;
}
Ok(true)
}
fn init() -> c_int {
let directory = unsafe { DIRECTORY_PATH.get().unwrap() };
let userid = unsafe { USER_ID.get().unwrap() };
let directory_path = std::path::Path::new(directory).join(MACHINE_KIND);
if directory_path.exists() {
let dll_path = directory_path.join(libloading::library_filename("STAR200_V7_DRV"));
println!("dll_path: {:?}", dll_path);
if dll_path.exists() {
match init_dll(dll_path.to_str().unwrap()).is_ok() {
true => {
// 打开设备
let init_result = unsafe {V7_OPEN.get_unchecked()()};
if init_result == 0 {
println!("设备打开成功");
return ResultType::Success as c_int;
} else {
println!("设备打开失败,代码:{:?}",init_result);
return ResultType::DeviceNotFound as c_int;
}
}
false => {
return ResultType::INITDLLFail as c_int;
}
}
} else {
return ResultType::DllPathNotExist as c_int;
}
} else {
println!("{:?}", directory_path);
return ResultType::DirectoryPathNotExist as c_int;
}
}
pub fn init_by_node(mut cx: FunctionContext) -> JsResult<JsNumber> {
let directory = cx.argument::<JsString>(0)?.value(&mut cx);
let userid = cx.argument::<JsString>(1)?.value(&mut cx);
unsafe {
DIRECTORY_PATH.take();
DIRECTORY_PATH.set(directory).unwrap();
USER_ID.take();
USER_ID.set(userid).unwrap();
};
let result = init() as f64;
Ok(cx.number(result))
}
#[allow(dead_code)] // 允许dead_code
enum ResultType {
DirectoryPathNotExist = -2003, // 找不到运行目录
DllPathNotExist = -2001, // 找不到dll文件
INITDLLFail = -2000, // 初始化dll
Success = 0, // 成功
UserIdFail = 2001, //用户 ID 错误
DeviceInitFail = 2002, // 设备初始化失败
DeviceKernelInitFail = 2003, // 初始化核心失败
DeviceDatInitFail = 2004, //未找到授权文件
DeviceNotInit = 2101, // 设备未初始化
DeviceNotFound = 2102, // 没有找到设备
DeviceReConnect = 2103, // 重新连接设备
Unknown = -100, // 未知错误
CheckCardInput = 3001, // 证件放入设备
CheckCardOut = 3002, // 证件移出设备
CheckCardNotInOrOut = 3000, // 证件无放入或拿出
CheckCardBarCode = 3003, // 检测到手机条码
OCRFail=-1, // 未检测到OCR区域
}
type RecogIDCardEXResultItem = HashMap<i32, [String; 2]>;
#[derive(Default, Serialize)]
pub struct RecogIDCardEXResultObject {
pub viz_result: RecogIDCardEXResultItem,
pub viz_orc_result: RecogIDCardEXResultItem,
pub mrz_result: RecogIDCardEXResultItem,
pub mrz_ocr_result: RecogIDCardEXResultItem,
pub chip_result: RecogIDCardEXResultItem,
}
#[derive(Default, Serialize)]
pub struct RecogIDCardEXResult {
pub device_name: String,
pub device_online: bool,
pub reg_info: RecogIDCardEXResultObject,
pub img_base64: HashMap<String, String>,
pub card_type: i32,
pub record_type: i32,
pub card_name: String,
pub success: bool, // 识别是否成功
}
static SAVE_IMAGE_REUSLT_NAME: [&str; 5] = [
"tempHeadEC.jpg",
"tempHead.jpg",
"tempUV.jpg",
"tempIR.jpg",
"temp.jpg",
];
fn start(mut cx: FunctionContext) -> JsResult<JsUndefined> {
let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
let mut channel = cx.channel();
channel.reference(&mut cx);
println!("start {}", channel.has_ref());
let index = unsafe {
DEVICE_START_INDEX += 1;
DEVICE_START_INDEX
};
std::thread::spawn(move || {
// Do the heavy lifting inside the background thread.
device_start(callback, channel, index);
});
Ok(cx.undefined())
}
use std::sync::{Arc, Mutex};
fn device_start(callback: Root<JsFunction>, channel: Channel, index: u64) {
let index = index;
let callback = Arc::new(Mutex::new(callback));
//设置读证功能
unsafe { V7_SET_MODE.get_unchecked()(1,1,1,1) };
loop {
if index != unsafe { DEVICE_START_INDEX } {
break;
};
let callback_clone = Arc::clone(&callback);
let mut result = RecogIDCardEXResult::default();
let mut flg_in:i8=0;
match unsafe { V7_WAIT_DOCINT.get_unchecked()(5.0,&mut flg_in) } {
// 设备正常 检测是否有放入证件
0 => {
if flg_in==0{
//检查是否放入超时
result.record_type = ResultType::CheckCardNotInOrOut as i32;
break;
}
result.device_online = true;
result.device_name =unsafe { DEVCIE_NAME.get_or_init(|| "".to_string()).to_string() };
match unsafe { V7_PROCESS.get_unchecked()() } {
// 证件有放入
0 => {
result.record_type = ResultType::CheckCardInput as i32;
}
// 未检测到OCR区域
-1 => {
result.record_type = ResultType::OCRFail as i32;
}
// 未找到非接卡
// v if v/10 == -21 => {
// result.record_type = ResultType::OCRFail as i32;
// }
// 设备离线
-3 => {
result.device_online = false;
result.record_type = init();
}
_ => {
result.record_type = ResultType::Unknown as i32;
}
}
}
-3 => {
//设备离线
let init = init();
result.device_online = false;
result.record_type = init;
}
_ => {
//未知错误
result.record_type = ResultType::Unknown as i32;
}
};
if unsafe { *NEED_RECORD.get_or_init(|| false) } {
println!("手工点击识别+1");
result.record_type = ResultType::CheckCardInput as i32;
}
// let time_now = std::time::Instant::now();
if result.record_type == ResultType::CheckCardInput as i32 {
let _result = recog_card();
result.success = _result.success;
result.img_base64 = _result.img_base64;
result.reg_info = _result.reg_info;
result.card_type = _result.card_type;
result.card_name = _result.card_name;
}
let neet_sendinfo = if Some(true) == unsafe { NEED_RECORD.take() } {
true
} else {
false
};
// let elapsed = time_now.elapsed();
// println!("识别时间结束时间 {:.6?}", elapsed);
if result.record_type != ResultType::CheckCardNotInOrOut as i32
&& (*unsafe { RESULT_TYPE.get_or_init(|| -10000) } != result.record_type
|| result.record_type == ResultType::CheckCardInput as i32
|| neet_sendinfo)
{
unsafe {
RESULT_TYPE.take();
RESULT_TYPE.set(result.record_type).unwrap();
}
channel.send(move |mut cx| {
let result_json = serde_json::to_string(&result).unwrap();
let callback = callback_clone.lock().unwrap().to_inner(&mut cx);
let this = cx.undefined();
let args = vec![cx.string(&result_json)];
callback.call(&mut cx, this, args)?;
Ok(())
});
}
std::thread::sleep(std::time::Duration::from_millis(20));
}
}
// 白光图、红外
// 图、紫外图、版面头像和芯片头像
pub fn recog_card() -> RecogIDCardEXResult {
let time_now = std::time::Instant::now();
let mut result = RecogIDCardEXResult::default();
result.device_online = true;
let img_path_directory = std::path::Path::new(unsafe { DIRECTORY_PATH.get().unwrap() });
let ir_img_path = img_path_directory.join("ir.jpg");
let color_img_path = img_path_directory.join("color.jpg");
//显示红外照
let irResult = unsafe {V7_VIS_SAVEIR.get_unchecked()(encode(ir_img_path.to_str().unwrap()).as_ptr() as LPCTSTR)};
//显示彩照
let colorResult = unsafe {V7_VIS_SAVECOLOR.get_unchecked()(encode(color_img_path.to_str().unwrap()).as_ptr() as LPCTSTR)};
if irResult==0{
if ir_img_path.exists() {
match std::fs::read(&ir_img_path) {
Ok(image_data) => {
println!("读取照片成功");
let image_data = base64::encode(&image_data);
let base64_string = String::from("data:image/jpg;base64,");
std::fs::remove_file(ir_img_path).unwrap();
result.img_base64.insert("0".to_string(), base64_string + &image_data);
}
Err(e) => {
println!("读取照片抛异常");
println!(
"{:?} {:?}",
e,
"ir.jpg",
);
}
};
}
}
if colorResult==0{
if color_img_path.exists() {
match std::fs::read(&color_img_path) {
Ok(image_data) => {
println!("读取照片成功");
let image_data = base64::encode(&image_data);
let base64_string = String::from("data:image/jpg;base64,");
std::fs::remove_file(color_img_path).unwrap();
result.img_base64.insert("1".to_string(), base64_string + &image_data);
}
Err(e) => {
println!("读取照片抛异常");
println!(
"{:?} {:?}",
e,
"color.jpg",
);
}
};
}
}
let mut ocritem = RecogIDCardEXResultObject::default();
let mut index: c_int = 0;
//orc识别文字
let mut mrztext = [0; 1024];
let x = unsafe {V7_VIS_GETMRZTEXT.get_unchecked()(mrztext.as_mut_ptr())};
if x==0{
let result_item = ["MRZ".to_string(), decode(&mrztext)];
ocritem.mrz_result.insert(index, result_item);
index+=1;
}
let mut data:[u16; 256] = [0; 256];
let mut len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(0,data.as_mut_ptr())};
if len>0{
ocritem.mrz_result.insert(index, ["编号".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(1,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["国籍".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(2,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["民族".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(3,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["姓名".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(4,data.as_mut_ptr())};
let cardType= unsafe {V7_GET_CARDTYPE.get_unchecked()()};
if cardType==101{
//身份证是UTF8格式
ocritem.mrz_result.insert(index, ["中文名".to_string(), decode(&data)]);
}else{
ocritem.mrz_result.insert(index, ["中文名".to_string(), decode(&data)]);
// //中国护照的中文姓名 是GBK编码的
// let name=GB18030.decode(&data, DecoderTrap::Strict).unwrap();
// ocritem.mrz_result.insert(index, ["中文名".to_string(), name.replace("\u{0}","")]);
}
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(5,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["性别".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(6,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["出生日期".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(7,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["地址".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(8,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["签发机关".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(9,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["有效期始".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(10,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["有效期止".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(20,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["港澳台ID".to_string(), decode(&data)]);
index+=1;
}
else{
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(0,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["编号".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(1,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["国籍".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(2,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["民族".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(3,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["姓名".to_string(), decode(&data)]);
index+=1;
//中国护照的中文姓名 是GBK编码的, 身份证不会执行到这里
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(4,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["中文名".to_string(), decode(&data)]);
// let name=GB18030.decode(&data, DecoderTrap::Strict).unwrap();
// ocritem.mrz_result.insert(index, ["中文名".to_string(), name.replace("\u{0}","")]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(5,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["性别".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(6,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["出生日期".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(7,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["地址".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(8,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["签发机关".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(9,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["有效期始".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(10,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["有效期止".to_string(), decode(&data)]);
index+=1;
len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(20,data.as_mut_ptr())};
ocritem.mrz_result.insert(index, ["港澳台ID".to_string(), decode(&data)]);
index+=1;
}
result.reg_info=ocritem;
result.success = true;
result.card_type = unsafe {V7_GET_CARDTYPE.get_unchecked()()};
let elapsed = time_now.elapsed();
println!("{:.6?}", elapsed);
return result;
}
pub fn regcord_by_node(mut cx: FunctionContext) -> JsResult<JsNull> {
println!("regcord_by_node");
unsafe {
NEED_RECORD.take();
NEED_RECORD.set(true).unwrap();
};
Ok(cx.null())
}
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("init", init_by_node)?;
cx.export_function("start", start)?;
cx.export_function("regcord_by_node", regcord_by_node)?;
Ok(())
}
使用rust调用c++静态库并编译nodejs包的更多相关文章
- Python语言、编译解释、动态库静态库、编译过程、头文件
学习Python这门语言首先要了解 什么是编译与解释,什么是连接,什么是动态库与静态库, 什么是编译: 编译就是先把高级语言设计的程序翻译成二进制的机器语言,然后CPU直接执行机器码就可以了.一把翻译 ...
- Android导入第三方静态库.a编译成动态库.so
http://ikinglai.blog.51cto.com/6220785/1324985 在Android开发的时候,经常会使用到用c或c++编写的第三方的静态库.如果有源码的话,可以直接跟你自己 ...
- 使用ar命令删除iOS静态库重复编译的.o文件
关于 xcode引入第三方静态类库 duplicate symbol _OBJC_XXX 重复编译错误 看这里 http://www.cnblogs.com/cocoajin/p/3917709.ht ...
- 6.关于QT中的内存管理,动态的制作,动态库的调用,静态库的制作
一 QT的内存管理 1 QT中的内存管理是QObject来管理的 2 QT中的内存管理没有cocos2dx中的引用计数 3 组件能够指定父对象 QTimer *timer = QTime ...
- linux C 刚初始化后的一个变量在调用一个静态库中函数后被异常修改为乱码
linux C 中声明并初始化一个变量const char a[512]="test";后,接着调用了一个静态库中的函数函数test(b);,a并没有传入test函数,但在调用这个 ...
- iOS导入c++语言的静态库之后编译不过,先要检查是否导入了libstdc++
iOS项目中引入c++库,编译链接时报如下错: "std::string::_Rep::_M_destroy(std::allocator<char> const&)&q ...
- C/C++ 跨平台交叉编译、静态库/动态库编译、MinGW、Cygwin、CodeBlocks使用原理及链接参数选项
目录 . 引言 . 交叉编译 . Cygwin简介 . 静态库编译及使用 . 动态库编译及使用 . MinGW简介 . CodeBlocks简介 0. 引言 UNIX是一个注册商标,是要满足一大堆条件 ...
- Linux上静态库和动态库的编译和使用
linux上静态库和动态库的编译和使用(附外部符号错误浅谈) 这就是静态库和动态库的显著区别,静态库是编译期间由链接器通过include目录找到并链接到到可执行文件中,而动态库则是运行期间动态调用,只 ...
- linux上静态库和动态库的编译和使用(附外部符号错误浅谈)
主要参考博客gcc创建和使用静态库和动态库 对于熟悉windows的同学,linux上的静态库.a相当于win的.lib,动态库.so相当于win的.dll. 首先简要地解释下这两种函数库的区别,参考 ...
随机推荐
- zabbix 线路质量监控自定义python模块,集成ICMP/TCP/UDP探测,批量监控线路质量自定义阈值联动mtr保存线路故障日志并发送至noc邮箱
互联网故障一般表现为丢包和时延增大,持续性故障不难排查,难的是间歇性或凌晨故障,后者往往来不及等我们测试就已经恢复正常,得不到异常时的mtr无法判断故障点在哪里 故此有了根据丢包率和时延变换联动mtr ...
- 566. Reshape the Matrix - LeetCode
Question 566. Reshape the Matrix Solution 题目大意:给一个二维数组,将这个二维数组转换为r行c列 思路:构造一个r行c列的二维数组,遍历给出二给数组nums, ...
- linux篇-基于域名的apache服务器
1承接上个博客说的,咱们继续扩展 Cd /usr/local/apache2/conf /usr/local/apache2/conf/extra扩展文件 Vi httpd-vhosts.conf & ...
- Linux篇-mysql + keepalived高可用
1上次说过了mysql的主从配置 tar zxf keepalived-1.2.7.tar.gz cd keepalived-1.2.7 yum install gcc gcc-c++ yum ins ...
- 使用虚拟机在3台centos7系统安装docker和k8s集群
一.安装docker 环境:准备3台centos7系统,都安装上docker环境,具体安装步骤和流程如下 参考: https://docs.docker.com/install/linux/docke ...
- python初识数据类型(字典、集合、元组、布尔)与运算符
目录 python数据类型(dict.tuple.set.bool) 字典 集合 元组 布尔值 用户交互与输出 获取用户输入 输出信息 格式化输出 基本运算符 算术运算符 比较运算符 逻辑运算符 赋值 ...
- 值得注意的: c++动态库、静态库、弱符号__attribute__((weak))以及extern之间的关系
先说结论: ①:动态库优先级最差,如果同时有静态库和动态库,那么首先使用的是静态库函数. ②:如果只有两个或多个动态库,那么首先使用的是最开始链接的动态库函数: ③:弱符号函数在动态库中是起任何作用的 ...
- 图的连通性--Tarjan算法
一些概念 无向图: 连通图:在无向图中,任意两点都直接或间接连通,则称该图为连通图.(或者说:任意两点之间都存在可到达的路径) 连通分量: G的 最大连通子图 称为G的连通分量. 有向图 (ps.区别 ...
- java-去除html中的标签或者元素属性(正则表达式/jsoup)
业务场景: 如一篇使用富文本编辑器编辑的新闻稿,需要在列表页面截取前200字作为摘要,此时需要去除html标签,截取真正的文本部分. /** * 删除Html标签 */public static St ...
- 全球共有多少MySQL实例在运行?这里有一份数据
摘要 Shadowserver Foundation在5月31日发布了一份全网的MySQL扫描报告,共发现了暴露在公网的360万个MySQL实例.因为这份报告基数够大,而且信息也非常完整,从数据库专业 ...