🚀 优质资源分享 🚀
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
💛Python量化交易实战💛 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
在项目上经常要用到身份证阅读器、护照阅读仪、指纹仪等各种品牌硬件,假如每套系统的都做集成开发那代码的维护成本将变得很高,为此采用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(ante