• 使用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 = 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> = OnceCell::new();
    static V7_CLOSE: OnceCell> = OnceCell::new();
    static V7_SET_MODE: OnceCell> = OnceCell::new();
    static V7_WAIT_DOCINT: OnceCell> = OnceCell::new();
    static V7_WAIT_DOCOUT: OnceCell> = OnceCell::new();
    static V7_PROCESS: OnceCell> = OnceCell::new();
    static V7_GET_CARDTYPE: OnceCell> = OnceCell::new();
    static V7_VIS_SAVECOLOR: OnceCell> = OnceCell::new();
    static V7_VIS_SAVEIR: OnceCell> = OnceCell::new();
    static V7_VIS_GETMRZTEXT: OnceCell> = OnceCell::new();
    static V7_RDO_getBytesByIndex: OnceCell> = OnceCell::new();
    static V7_VIS_getBytesByIndex: OnceCell> = OnceCell::new();
    static V7_RF_active: OnceCell> = 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 {
    	//外部传进来的参数(根据自己的需要来定义)
        let directory = cx.argument::(0)?.value(&mut cx);
        let userid = cx.argument::(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::(b"S2V7_open").unwrap() });
        println!("S2V7_close");
        V7_CLOSE.get_or_init(|| unsafe { library.get::(b"S2V7_close").unwrap() });
        println!("S2V7_set_mode");
        V7_SET_MODE.get_or_init(|| unsafe {library.get::(b"S2V7_set_mode").unwrap()});
        println!("S2V7_wait_DocIn");
        V7_WAIT_DOCINT.get_or_init(|| unsafe { library.get::(b"S2V7_wait_DocIn").unwrap() });
        println!("S2V7_wait_DocOut");
        V7_WAIT_DOCOUT.get_or_init(|| unsafe { library.get::(b"S2V7_wait_DocOut").unwrap() });
        V7_PROCESS.get_or_init(|| unsafe { library.get::(b"S2V7_process").unwrap() });
        V7_GET_CARDTYPE.get_or_init(|| unsafe { library.get::(b"S2V7_get_cardType").unwrap() });
        V7_VIS_SAVECOLOR.get_or_init(|| unsafe { library.get::(b"S2V7_VIS_saveColor").unwrap() });
        V7_VIS_SAVEIR.get_or_init(|| unsafe { library.get::(b"S2V7_VIS_saveIR").unwrap() });
        V7_VIS_GETMRZTEXT.get_or_init(|| unsafe { library.get::(b"S2V7_VIS_getMRZtext").unwrap() });
        V7_RDO_getBytesByIndex.get_or_init(|| unsafe { library.get::(b"S2V7_RDO_getBytesByIndex").unwrap() });
        V7_VIS_getBytesByIndex.get_or_init(|| unsafe { library.get::(b"S2V7_VIS_getBytesByIndex").unwrap() });
        V7_RF_active.get_or_init(|| unsafe { library.get::(b"S2V7_RF_active").unwrap() });
    
        unsafe {
            INITDLL = true;
        }
        Ok(true)
    }
    
    折叠
    //创建新线程来监测设备读证操作
    fn start(mut cx: FunctionContext) -> JsResult {
        let callback = cx.argument::(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, 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 = 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> = OnceCell::new();
    static V7_CLOSE: OnceCell> = OnceCell::new();
    static V7_SET_MODE: OnceCell> = OnceCell::new();
    static V7_WAIT_DOCINT: OnceCell> = OnceCell::new();
    static V7_WAIT_DOCOUT: OnceCell> = OnceCell::new();
    static V7_PROCESS: OnceCell> = OnceCell::new();
    static V7_GET_CARDTYPE: OnceCell> = OnceCell::new();
    static V7_VIS_SAVECOLOR: OnceCell> = OnceCell::new();
    static V7_VIS_SAVEIR: OnceCell> = OnceCell::new();
    static V7_VIS_GETMRZTEXT: OnceCell> = OnceCell::new();
    static V7_RDO_getBytesByIndex: OnceCell> = OnceCell::new();
    static V7_VIS_getBytesByIndex: OnceCell> = OnceCell::new();
    static V7_RF_active: OnceCell> = 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::(b"S2V7_open").unwrap() });
        println!("S2V7_close");
        V7_CLOSE.get_or_init(|| unsafe { library.get::(b"S2V7_close").unwrap() });
        println!("S2V7_set_mode");
        V7_SET_MODE.get_or_init(|| unsafe {library.get::(b"S2V7_set_mode").unwrap()});
        println!("S2V7_wait_DocIn");
        V7_WAIT_DOCINT.get_or_init(|| unsafe { library.get::(b"S2V7_wait_DocIn").unwrap() });
        println!("S2V7_wait_DocOut");
        V7_WAIT_DOCOUT.get_or_init(|| unsafe { library.get::(b"S2V7_wait_DocOut").unwrap() });
        V7_PROCESS.get_or_init(|| unsafe { library.get::(b"S2V7_process").unwrap() });
        V7_GET_CARDTYPE.get_or_init(|| unsafe { library.get::(b"S2V7_get_cardType").unwrap() });
        V7_VIS_SAVECOLOR.get_or_init(|| unsafe { library.get::(b"S2V7_VIS_saveColor").unwrap() });
        V7_VIS_SAVEIR.get_or_init(|| unsafe { library.get::(b"S2V7_VIS_saveIR").unwrap() });
        V7_VIS_GETMRZTEXT.get_or_init(|| unsafe { library.get::(b"S2V7_VIS_getMRZtext").unwrap() });
        V7_RDO_getBytesByIndex.get_or_init(|| unsafe { library.get::(b"S2V7_RDO_getBytesByIndex").unwrap() });
        V7_VIS_getBytesByIndex.get_or_init(|| unsafe { library.get::(b"S2V7_VIS_getBytesByIndex").unwrap() });
        V7_RF_active.get_or_init(|| unsafe { library.get::(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 {
        let directory = cx.argument::(0)?.value(&mut cx);
        let userid = cx.argument::(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 {
        let callback = cx.argument::(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, 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 {
        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(())
    }
    
    
  • 相关阅读:
    leetcode面试题之栈与队列
    Unity3D教程:实现房产项目中的材质动态切换
    redis 重建主从记录
    武汉新时标文化传媒有限公司抖音电商的算法逻辑
    虹科分享 | 如何测试与验证复杂的FPGA设计(3)——硬件测试
    赞评论收藏分享测评补单对速卖通、Lazada、阿里国际卖家有哪些好处和硬件要求?
    三电系统集成技术杂谈
    HTML小游戏10 —— 休闲类游戏《解救海盗船长》(附完整源码)
    .Net FramWork 和Net Core3.1配置Log4.net
    python创建与分发自己的包
  • 原文地址:https://www.cnblogs.com/beekit/p/16543154.html