rust调用c++静态库
天边行云 人气:0在项目上经常要用到SFZ阅读器、护照阅读仪、指纹仪等各种品牌硬件,假如每套系统的都做集成开发那代码的维护成本将变得很高,为此采用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{ //SFZ是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编码的, SFZ不会执行到这里 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(()) }
加载全部内容