在项目上经常要用到身份证阅读器、护照阅读仪、指纹仪等各种品牌硬件,假如每套系统的都做集成开发那代码的维护成本将变得很高,为此采用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包的更多相关文章

  1. Python语言、编译解释、动态库静态库、编译过程、头文件

    学习Python这门语言首先要了解 什么是编译与解释,什么是连接,什么是动态库与静态库, 什么是编译: 编译就是先把高级语言设计的程序翻译成二进制的机器语言,然后CPU直接执行机器码就可以了.一把翻译 ...

  2. Android导入第三方静态库.a编译成动态库.so

    http://ikinglai.blog.51cto.com/6220785/1324985 在Android开发的时候,经常会使用到用c或c++编写的第三方的静态库.如果有源码的话,可以直接跟你自己 ...

  3. 使用ar命令删除iOS静态库重复编译的.o文件

    关于 xcode引入第三方静态类库 duplicate symbol _OBJC_XXX 重复编译错误 看这里 http://www.cnblogs.com/cocoajin/p/3917709.ht ...

  4. 6.关于QT中的内存管理,动态的制作,动态库的调用,静态库的制作

     一  QT的内存管理 1  QT中的内存管理是QObject来管理的 2  QT中的内存管理没有cocos2dx中的引用计数 3  组件能够指定父对象 QTimer *timer = QTime ...

  5. linux C 刚初始化后的一个变量在调用一个静态库中函数后被异常修改为乱码

    linux C 中声明并初始化一个变量const char a[512]="test";后,接着调用了一个静态库中的函数函数test(b);,a并没有传入test函数,但在调用这个 ...

  6. iOS导入c++语言的静态库之后编译不过,先要检查是否导入了libstdc++

    iOS项目中引入c++库,编译链接时报如下错: "std::string::_Rep::_M_destroy(std::allocator<char> const&)&q ...

  7. C/C++ 跨平台交叉编译、静态库/动态库编译、MinGW、Cygwin、CodeBlocks使用原理及链接参数选项

    目录 . 引言 . 交叉编译 . Cygwin简介 . 静态库编译及使用 . 动态库编译及使用 . MinGW简介 . CodeBlocks简介 0. 引言 UNIX是一个注册商标,是要满足一大堆条件 ...

  8. Linux上静态库和动态库的编译和使用

    linux上静态库和动态库的编译和使用(附外部符号错误浅谈) 这就是静态库和动态库的显著区别,静态库是编译期间由链接器通过include目录找到并链接到到可执行文件中,而动态库则是运行期间动态调用,只 ...

  9. linux上静态库和动态库的编译和使用(附外部符号错误浅谈)

    主要参考博客gcc创建和使用静态库和动态库 对于熟悉windows的同学,linux上的静态库.a相当于win的.lib,动态库.so相当于win的.dll. 首先简要地解释下这两种函数库的区别,参考 ...

随机推荐

  1. windows获取高精度时间戳 精度100ns

    #include <stdio.h> #include <Windows.h> int main(void){ LARGE_INTEGER ticks,Frequency; Q ...

  2. Fail2ban 配置详解 过滤器配置

    Fail2ban自带了很多相关服务日志的过滤器. ### # 包含配置 ### [INCLUDES] before = common.conf # 还包含其他文件中的配置,在加载本配置文件中配置之前先 ...

  3. STM32启动文件

    一.复位电路 在了解启动文件之前需要明白STM32的复位中断流程,STM32的复位分为上电复位和手动复位,复位的电路图如下所示: 注意: 图中的复位电路是低电平复位,有的MCU是高电平复位. 上电复位 ...

  4. 可靠的分布式KV存储产品-ETCD-初见

    目录 Paxos Raft(Understandable Distributed Consensus) 名词介绍 Leader Election Log Replication 请求完整流程 etcd ...

  5. 前端4BOM与DOM

    内容概要 BOM操作(了解) DOM操作 DOM操作标签 获取值操作 属性操作 事件 -事件案例 内容详情 BOM操作(了解)

  6. Keytool配置 Tomcat的HTTPS双向认证

    Keytool配置 Tomcat的HTTPS双向认证 证书生成 keytool 简介 Keytool是一个Java数据证书的管理工具, Keytool将密钥(key)和证书(certificates) ...

  7. mac M1 php扩展 xlswriter 编译安装爬坑记录

    电脑配置 MacBook Pro(14英寸,2021年) 系统版本 macOS Monterey 12.4 芯片 Apple M1 Pro PHP环境 MAMP Pro Version 6.6.1 ( ...

  8. 讲一个linux服务启动报错问题排查

    例子 首先我们在/usr/lib/systemd/system目录下创建一个服务文件,写下服务启动任务配置.下面我以prometheus的node_exporter为例 vim /usr/lib/sy ...

  9. 一篇文章带你使用Typescript封装一个Vue组件

    一.搭建项目以及初始化配置 vue create ts_vue_btn 这里使用了vue CLI3自定义选择的服务,我选择了ts.stylus等工具.然后创建完项目之后,进入项目.使用快捷命令code ...

  10. CVPR2022 | 可精简域适应

    前言 在本文中,作者引入了一个简单的框架,即Slimmable Domain Adaptation,以通过权重共享模型库改进跨域泛化,从中可以对不同容量的模型进行采样,以适应不同的精度效率权衡.此外, ...