關於使用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包的文章就介紹到這瞭,更多相關rust調用c++靜態庫內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 關於Rust 使用 dotenv 來設置環境變量的問題
- 在 TypeScript 中使用泛型的方法
- Golang標準庫unsafe源碼解讀
- Golang的strings.Split()踩坑記錄
- Go 語言 json解析框架與 gjson 詳解