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

  1. [package]
  2. name = "reader"
  3. version = "0.1.0"
  4. edition = "2018"
  5. exclude = ["reader.node"]
  6. # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
  7. [dependencies]
  8. libc = "0.2.9"
  9. libloading = "0.7"
  10. once_cell = "1.8"
  11. serde = { version = "1.0", features = ["derive"] }
  12. widestring = "0.5.1"
  13. serde_json = "1.0"
  14. base64 = "0.13"
  15. hex="0.4.2"
  16. encoding = "0.2"
  17. tokio={version="1.18.0",features = ["full"]}
  18. [dependencies.neon]
  19. version = "0.9"
  20. default-features = false
  21. features = ["napi-5", "channel-api"]
  22. [lib]
  23. crate-type = ["cdylib"]

三、package.json

  1. {
  2. "name": "reader",
  3. "version": "0.1.0",
  4. "description": "",
  5. "main": "index.node",
  6. "scripts": {
  7. "build": "cargo-cp-artifact -nc index.node -- cargo build --message-format=json-render-diagnostics",
  8. "build-debug": "npm run build --",
  9. "build-release": "npm run build -- --release",
  10. "build_win32": "npm run build -- --release --target=i686-pc-windows-msvc",
  11. "test": "cargo test",
  12. "run": "cargo run"
  13. },
  14. "author": "",
  15. "license": "ISC",
  16. "devDependencies": {
  17. "cargo-cp-artifact": "^0.1"
  18. },
  19. "dependencies": {
  20. "express": "^4.17.3"
  21. }
  22. }

我们可以打印rust看看编译输出支持哪些架构

rustc --print target-list

//添加 x86编译链接器

rustup target add i686-pc-windows-msvc

四、代码分析

  1. use std::collections::HashMap;
  2. use std::str;
  3. use std::fmt::Write;
  4. use std::io::{Error};
  5. extern crate encoding;
  6. use encoding::all::GB18030;
  7. use encoding::{DecoderTrap,EncoderTrap,Encoding};
  8. use tokio::time::{sleep, Duration,Instant};
  9. use libc::{c_int, c_void};
  10. use libloading::{Library, Symbol};
  11. use neon::prelude::*;
  12. use once_cell::sync::OnceCell;
  13. use serde::{Deserialize, Serialize};
  14. use widestring::{WideCStr, WideCString, WideChar};
  15. // 编码转换 utf8 -> utf16le
  16. fn encode(source: &str) -> WideCString {
  17. let string_source = source.to_string() + "\0";
  18. WideCString::from_str(&string_source).unwrap()
  19. }
  20. // 解码转换 utf16le -> utf8
  21. fn decode(source: &[WideChar]) -> String {
  22. WideCStr::from_slice_truncate(source)
  23. .unwrap()
  24. .to_string()
  25. .unwrap()
  26. }
  27. // 加载 dll
  28. static LIBRARY: OnceCell<Library> = OnceCell::new();
  29. //指定编译架构
  30. static MACHINE_KIND: &str = if cfg!(target_os = "windows") {
  31. if cfg!(target_arch = "x86") {
  32. "win32"
  33. } else if cfg!(target_arch = "x86_x64") {
  34. "win64"
  35. } else {
  36. "other"
  37. }
  38. } else if cfg!(target_os = "linux") {
  39. if cfg!(target_arch = "x86") {
  40. "linux32"
  41. } else if cfg!(target_arch = "x86_64") {
  42. "linux64"
  43. } else if cfg!(target_arch = "aarch64") {
  44. "aarch64"
  45. } else if cfg!(target_arch = "arm") {
  46. "arm"
  47. } else {
  48. "other"
  49. }
  50. } else {
  51. "other"
  52. };
  1. //定义函数方法名,这里要根据c++库的函数名和参数来定义,函数名和参数类型务必要一致。
  2. type LPCTSTR = *const WideChar;
  3. type BOOL = c_int;
  4. type INITPTR = *const i8;
  5. type CANRST = *mut WideChar;
  6. // 打开设备
  7. type S2V7_open = unsafe extern "system" fn() -> c_int;
  8. // 关闭设备
  9. type S2V7_close = unsafe extern "system" fn() -> c_int;
  10. //【set mode 设置读证功能】
  11. type S2V7_set_mode =
  12. unsafe extern "system" fn(flg_takeColor: c_int, flg_takeUV: c_int, flg_readChipInfo: c_int, flg_readChipFace: c_int) -> c_int; // Type = 0 即可
  13. //【wait Doc. in 等待放卡】
  14. type S2V7_wait_DocIn =
  15. unsafe extern "system" fn(timeout: f64, flg_in: INITPTR) -> c_int; // Type = 0 即可
  16. //【wait Doc. out 等待拿卡】
  17. type S2V7_wait_DocOut =
  18. unsafe extern "system" fn(timeout: f64, flg_out: INITPTR) -> c_int; // Type = 0 即可
  19. //【process 执行读卡过程】
  20. type S2V7_process = unsafe extern "system" fn() -> c_int;
  21. //读取卡类型
  22. type S2V7_get_cardType = unsafe extern "system" fn() -> c_int;
  23. //保存彩照
  24. type S2V7_VIS_saveColor = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;
  25. //保存红外照
  26. type S2V7_VIS_saveIR = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;
  27. //【get MRZ text 获取OCR文字信息】
  28. type S2V7_VIS_getMRZtext = unsafe extern "system" fn(text: LPCTSTR) -> c_int;
  29. //show text information 文字信息
  30. type S2V7_RDO_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;
  31. type S2V7_VIS_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;
  32. type S2V7_RF_active = unsafe extern "system" fn(antenna: c_int,atr: LPCTSTR, atr_len: c_int) -> c_int;
  33. //构建函数实例
  34. static V7_OPEN: OnceCell<Symbol<S2V7_open>> = OnceCell::new();
  35. static V7_CLOSE: OnceCell<Symbol<S2V7_close>> = OnceCell::new();
  36. static V7_SET_MODE: OnceCell<Symbol<S2V7_set_mode>> = OnceCell::new();
  37. static V7_WAIT_DOCINT: OnceCell<Symbol<S2V7_wait_DocIn>> = OnceCell::new();
  38. static V7_WAIT_DOCOUT: OnceCell<Symbol<S2V7_wait_DocOut>> = OnceCell::new();
  39. static V7_PROCESS: OnceCell<Symbol<S2V7_process>> = OnceCell::new();
  40. static V7_GET_CARDTYPE: OnceCell<Symbol<S2V7_get_cardType>> = OnceCell::new();
  41. static V7_VIS_SAVECOLOR: OnceCell<Symbol<S2V7_VIS_saveColor>> = OnceCell::new();
  42. static V7_VIS_SAVEIR: OnceCell<Symbol<S2V7_VIS_saveIR>> = OnceCell::new();
  43. static V7_VIS_GETMRZTEXT: OnceCell<Symbol<S2V7_VIS_getMRZtext>> = OnceCell::new();
  44. static V7_RDO_getBytesByIndex: OnceCell<Symbol<S2V7_RDO_getBytesByIndex>> = OnceCell::new();
  45. static V7_VIS_getBytesByIndex: OnceCell<Symbol<S2V7_VIS_getBytesByIndex>> = OnceCell::new();
  46. static V7_RF_active: OnceCell<Symbol<S2V7_RF_active>> = OnceCell::new();
  1. // 对外导出函数方法
  2. #[neon::main]
  3. fn main(mut cx: ModuleContext) -> NeonResult<()> {
  4. cx.export_function("init", init_by_node)?;
  5. cx.export_function("start", start)?;
  6. }
  7. //加载dll并对函数进行初始化操作
  8. pub fn init_by_node(mut cx: FunctionContext) -> JsResult<JsNumber> {
  9. //外部传进来的参数(根据自己的需要来定义)
  10. let directory = cx.argument::<JsString>(0)?.value(&mut cx);
  11. let userid = cx.argument::<JsString>(1)?.value(&mut cx);
  12. unsafe {
  13. DIRECTORY_PATH.take();
  14. DIRECTORY_PATH.set(directory).unwrap();
  15. USER_ID.take();
  16. USER_ID.set(userid).unwrap();
  17. };
  18. let result = init() as f64;
  19. Ok(cx.number(result))
  20. }
  21. //核心代码,加载dll函数并映射
  22. fn init() -> c_int {
  23. let directory = unsafe { DIRECTORY_PATH.get().unwrap() };
  24. let userid = unsafe { USER_ID.get().unwrap() };
  25. let directory_path = std::path::Path::new(directory).join(MACHINE_KIND);
  26. if directory_path.exists() {
  27. let dll_path = directory_path.join(libloading::library_filename("STAR200_V7_DRV"));
  28. println!("dll_path: {:?}", dll_path);
  29. if dll_path.exists() {
  30. match init_dll(dll_path.to_str().unwrap()).is_ok() {
  31. true => {
  32. // 打开设备
  33. let init_result = unsafe {V7_OPEN.get_unchecked()()};
  34. if init_result == 0 {
  35. println!("设备打开成功");
  36. return ResultType::Success as c_int;
  37. } else {
  38. println!("设备打开失败,代码:{:?}",init_result);
  39. return ResultType::DeviceNotFound as c_int;
  40. }
  41. }
  42. false => {
  43. return ResultType::INITDLLFail as c_int;
  44. }
  45. }
  46. } else {
  47. return ResultType::DllPathNotExist as c_int;
  48. }
  49. } else {
  50. println!("{:?}", directory_path);
  51. return ResultType::DirectoryPathNotExist as c_int;
  52. }
  53. }
  54. // 加载dll
  55. fn init_dll(dll_path: &str) -> Result<bool, Box<dyn std::error::Error>> {
  56. unsafe {
  57. if INITDLL {
  58. return Ok(true);
  59. }
  60. }
  61. println!("加载dll");
  62. println!("dll_path");
  63. let library = LIBRARY.get_or_init(|| unsafe { Library::new(dll_path).unwrap() });
  64. println!("S2V7_open");
  65. V7_OPEN.get_or_init(|| unsafe { library.get::<S2V7_open>(b"S2V7_open").unwrap() });
  66. println!("S2V7_close");
  67. V7_CLOSE.get_or_init(|| unsafe { library.get::<S2V7_close>(b"S2V7_close").unwrap() });
  68. println!("S2V7_set_mode");
  69. V7_SET_MODE.get_or_init(|| unsafe {library.get::<S2V7_set_mode>(b"S2V7_set_mode").unwrap()});
  70. println!("S2V7_wait_DocIn");
  71. V7_WAIT_DOCINT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocIn>(b"S2V7_wait_DocIn").unwrap() });
  72. println!("S2V7_wait_DocOut");
  73. V7_WAIT_DOCOUT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocOut>(b"S2V7_wait_DocOut").unwrap() });
  74. V7_PROCESS.get_or_init(|| unsafe { library.get::<S2V7_process>(b"S2V7_process").unwrap() });
  75. V7_GET_CARDTYPE.get_or_init(|| unsafe { library.get::<S2V7_get_cardType>(b"S2V7_get_cardType").unwrap() });
  76. V7_VIS_SAVECOLOR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveColor>(b"S2V7_VIS_saveColor").unwrap() });
  77. V7_VIS_SAVEIR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveIR>(b"S2V7_VIS_saveIR").unwrap() });
  78. V7_VIS_GETMRZTEXT.get_or_init(|| unsafe { library.get::<S2V7_VIS_getMRZtext>(b"S2V7_VIS_getMRZtext").unwrap() });
  79. V7_RDO_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_RDO_getBytesByIndex>(b"S2V7_RDO_getBytesByIndex").unwrap() });
  80. V7_VIS_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_VIS_getBytesByIndex>(b"S2V7_VIS_getBytesByIndex").unwrap() });
  81. V7_RF_active.get_or_init(|| unsafe { library.get::<S2V7_RF_active>(b"S2V7_RF_active").unwrap() });
  82. unsafe {
  83. INITDLL = true;
  84. }
  85. Ok(true)
  86. }
  1. //创建新线程来监测设备读证操作
  2. fn start(mut cx: FunctionContext) -> JsResult<JsUndefined> {
  3. let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
  4. let mut channel = cx.channel();
  5. channel.reference(&mut cx);
  6. println!("start {}", channel.has_ref());
  7. let index = unsafe {
  8. DEVICE_START_INDEX += 1;
  9. DEVICE_START_INDEX
  10. };
  11. std::thread::spawn(move || {
  12. // Do the heavy lifting inside the background thread.
  13. device_start(callback, channel, index);
  14. });
  15. Ok(cx.undefined())
  16. }
  17. use std::sync::{Arc, Mutex};
  18. fn device_start(callback: Root<JsFunction>, channel: Channel, index: u64) {
  19. let index = index;
  20. let callback = Arc::new(Mutex::new(callback));
  21. //设置读证功能
  22. unsafe { V7_SET_MODE.get_unchecked()(1,1,1,1) };
  23. loop {
  24. if index != unsafe { DEVICE_START_INDEX } {
  25. break;
  26. };
  27. let callback_clone = Arc::clone(&callback);
  28. let mut result = RecogIDCardEXResult::default();
  29. let mut flg_in:i8=0;
  30. match unsafe { V7_WAIT_DOCINT.get_unchecked()(5.0,&mut flg_in) } {
  31. // 设备正常 检测是否有放入证件
  32. 0 => {
  33. if flg_in==0{
  34. //检查是否放入超时
  35. result.record_type = ResultType::CheckCardNotInOrOut as i32;
  36. break;
  37. }
  38. result.device_online = true;
  39. result.device_name =unsafe { DEVCIE_NAME.get_or_init(|| "".to_string()).to_string() };
  40. match unsafe { V7_PROCESS.get_unchecked()() } {
  41. // 证件有放入
  42. 0 => {
  43. result.record_type = ResultType::CheckCardInput as i32;
  44. }
  45. // 未检测到OCR区域
  46. -1 => {
  47. result.record_type = ResultType::OCRFail as i32;
  48. }
  49. // 设备离线
  50. -3 => {
  51. result.device_online = false;
  52. result.record_type = init();
  53. }
  54. _ => {
  55. result.record_type = ResultType::Unknown as i32;
  56. }
  57. }
  58. }
  59. -3 => {
  60. //设备离线
  61. let init = init();
  62. result.device_online = false;
  63. result.record_type = init;
  64. }
  65. _ => {
  66. //未知错误
  67. result.record_type = ResultType::Unknown as i32;
  68. }
  69. };
  70. if unsafe { *NEED_RECORD.get_or_init(|| false) } {
  71. println!("手工点击识别+1");
  72. result.record_type = ResultType::CheckCardInput as i32;
  73. }
  74. // let time_now = std::time::Instant::now();
  75. if result.record_type == ResultType::CheckCardInput as i32 {
  76. let _result = recog_card();
  77. result.success = _result.success;
  78. result.img_base64 = _result.img_base64;
  79. result.reg_info = _result.reg_info;
  80. result.card_type = _result.card_type;
  81. result.card_name = _result.card_name;
  82. }
  83. let neet_sendinfo = if Some(true) == unsafe { NEED_RECORD.take() } {
  84. true
  85. } else {
  86. false
  87. };
  88. // let elapsed = time_now.elapsed();
  89. // println!("识别时间结束时间 {:.6?}", elapsed);
  90. if result.record_type != ResultType::CheckCardNotInOrOut as i32
  91. && (*unsafe { RESULT_TYPE.get_or_init(|| -10000) } != result.record_type
  92. || result.record_type == ResultType::CheckCardInput as i32
  93. || neet_sendinfo)
  94. {
  95. unsafe {
  96. RESULT_TYPE.take();
  97. RESULT_TYPE.set(result.record_type).unwrap();
  98. }
  99. channel.send(move |mut cx| {
  100. let result_json = serde_json::to_string(&result).unwrap();
  101. let callback = callback_clone.lock().unwrap().to_inner(&mut cx);
  102. let this = cx.undefined();
  103. let args = vec![cx.string(&result_json)];
  104. callback.call(&mut cx, this, args)?;
  105. Ok(())
  106. });
  107. }
  108. std::thread::sleep(std::time::Duration::from_millis(20));
  109. }
  110. }

完整源码

点击查看代码
  1. use std::collections::HashMap;
  2. use libc::{c_int, c_void};
  3. use libloading::{Library, Symbol};
  4. use neon::prelude::*;
  5. use once_cell::sync::OnceCell;
  6. use serde::Serialize;
  7. extern crate encoding;
  8. use encoding::all::GB18030;
  9. use encoding::{DecoderTrap,EncoderTrap,Encoding};
  10. use widestring::{WideCStr, WideCString, WideChar};
  11. // 编码转换 utf8 -> utf16le
  12. fn encode(source: &str) -> WideCString {
  13. let string_source = source.to_string() + "\0";
  14. WideCString::from_str(&string_source).unwrap()
  15. }
  16. // 解码转换 utf16le -> utf8
  17. fn decode(source: &[WideChar]) -> String {
  18. WideCStr::from_slice_truncate(source)
  19. .unwrap()
  20. .to_string()
  21. .unwrap()
  22. }
  23. // 加载 dll
  24. static LIBRARY: OnceCell<Library> = OnceCell::new();
  25. static MACHINE_KIND: &str = if cfg!(target_os = "windows") {
  26. if cfg!(target_arch = "x86") {
  27. "win32"
  28. } else if cfg!(target_arch = "x86_x64") {
  29. "win64"
  30. } else {
  31. "other"
  32. }
  33. } else if cfg!(target_os = "linux") {
  34. if cfg!(target_arch = "x86") {
  35. "linux32"
  36. } else if cfg!(target_arch = "x86_64") {
  37. "linux64"
  38. } else if cfg!(target_arch = "aarch64") {
  39. "aarch64"
  40. } else if cfg!(target_arch = "arm") {
  41. "arm"
  42. } else {
  43. "other"
  44. }
  45. } else {
  46. "other"
  47. };
  48. //设置识别的证件 ID
  49. // 设置当前要识别的证件类型,并将
  50. // 之前已经设置的证件类型清除。
  51. // nMainID 主要识别类型,nSubID 子类型
  52. // nSubID 头指针,默认将数组
  53. // nSubID 第 一 个 元 素 赋 值 为 0 即
  54. // nSubID[0]=0
  55. // type S = c_int[];
  56. type LPCTSTR = *const WideChar;
  57. type BOOL = c_int;
  58. type INITPTR = *const i8;
  59. type CANRST = *mut WideChar;
  60. // 打开设备
  61. type S2V7_open = unsafe extern "system" fn() -> c_int;
  62. // 关闭设备
  63. type S2V7_close = unsafe extern "system" fn() -> c_int;
  64. //【set mode 设置读证功能】
  65. type S2V7_set_mode =
  66. unsafe extern "system" fn(flg_takeColor: c_int, flg_takeUV: c_int, flg_readChipInfo: c_int, flg_readChipFace: c_int) -> c_int; // Type = 0 即可
  67. //【wait Doc. in 等待放卡】
  68. type S2V7_wait_DocIn =
  69. unsafe extern "system" fn(timeout: f64, flg_in: INITPTR) -> c_int; // Type = 0 即可
  70. //【wait Doc. out 等待拿卡】
  71. type S2V7_wait_DocOut =
  72. unsafe extern "system" fn(timeout: f64, flg_out: INITPTR) -> c_int; // Type = 0 即可
  73. //【process 执行读卡过程】
  74. type S2V7_process = unsafe extern "system" fn() -> c_int;
  75. //读取卡类型
  76. type S2V7_get_cardType = unsafe extern "system" fn() -> c_int;
  77. //保存彩照
  78. type S2V7_VIS_saveColor = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;
  79. //保存红外照
  80. type S2V7_VIS_saveIR = unsafe extern "system" fn(imgPath: LPCTSTR) -> c_int;
  81. //【get MRZ text 获取OCR文字信息】
  82. type S2V7_VIS_getMRZtext = unsafe extern "system" fn(text: LPCTSTR) -> c_int;
  83. //show text information 文字信息
  84. type S2V7_RDO_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;
  85. type S2V7_VIS_getBytesByIndex = unsafe extern "system" fn(index: c_int,data: LPCTSTR) -> c_int;
  86. type S2V7_RF_active = unsafe extern "system" fn(antenna: c_int,atr: LPCTSTR, atr_len: c_int) -> c_int;
  87. static V7_OPEN: OnceCell<Symbol<S2V7_open>> = OnceCell::new();
  88. static V7_CLOSE: OnceCell<Symbol<S2V7_close>> = OnceCell::new();
  89. static V7_SET_MODE: OnceCell<Symbol<S2V7_set_mode>> = OnceCell::new();
  90. static V7_WAIT_DOCINT: OnceCell<Symbol<S2V7_wait_DocIn>> = OnceCell::new();
  91. static V7_WAIT_DOCOUT: OnceCell<Symbol<S2V7_wait_DocOut>> = OnceCell::new();
  92. static V7_PROCESS: OnceCell<Symbol<S2V7_process>> = OnceCell::new();
  93. static V7_GET_CARDTYPE: OnceCell<Symbol<S2V7_get_cardType>> = OnceCell::new();
  94. static V7_VIS_SAVECOLOR: OnceCell<Symbol<S2V7_VIS_saveColor>> = OnceCell::new();
  95. static V7_VIS_SAVEIR: OnceCell<Symbol<S2V7_VIS_saveIR>> = OnceCell::new();
  96. static V7_VIS_GETMRZTEXT: OnceCell<Symbol<S2V7_VIS_getMRZtext>> = OnceCell::new();
  97. static V7_RDO_getBytesByIndex: OnceCell<Symbol<S2V7_RDO_getBytesByIndex>> = OnceCell::new();
  98. static V7_VIS_getBytesByIndex: OnceCell<Symbol<S2V7_VIS_getBytesByIndex>> = OnceCell::new();
  99. static V7_RF_active: OnceCell<Symbol<S2V7_RF_active>> = OnceCell::new();
  100. // static
  101. static mut INITDLL: bool = false;
  102. static mut DEVICE_START_INDEX: u64 = 0;
  103. static mut DIRECTORY_PATH: OnceCell<String> = OnceCell::new();
  104. static mut USER_ID: OnceCell<String> = OnceCell::new();
  105. static mut DEVCIE_NAME: OnceCell<String> = OnceCell::new();
  106. static mut RESULT_TYPE: OnceCell<i32> = OnceCell::new();
  107. static mut NEED_RECORD: OnceCell<bool> = OnceCell::new();
  108. // 初始化dll
  109. fn init_dll(dll_path: &str) -> Result<bool, Box<dyn std::error::Error>> {
  110. unsafe {
  111. if INITDLL {
  112. return Ok(true);
  113. }
  114. }
  115. println!("加载dll");
  116. println!("dll_path");
  117. let library = LIBRARY.get_or_init(|| unsafe { Library::new(dll_path).unwrap() });
  118. println!("S2V7_open");
  119. V7_OPEN.get_or_init(|| unsafe { library.get::<S2V7_open>(b"S2V7_open").unwrap() });
  120. println!("S2V7_close");
  121. V7_CLOSE.get_or_init(|| unsafe { library.get::<S2V7_close>(b"S2V7_close").unwrap() });
  122. println!("S2V7_set_mode");
  123. V7_SET_MODE.get_or_init(|| unsafe {library.get::<S2V7_set_mode>(b"S2V7_set_mode").unwrap()});
  124. println!("S2V7_wait_DocIn");
  125. V7_WAIT_DOCINT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocIn>(b"S2V7_wait_DocIn").unwrap() });
  126. println!("S2V7_wait_DocOut");
  127. V7_WAIT_DOCOUT.get_or_init(|| unsafe { library.get::<S2V7_wait_DocOut>(b"S2V7_wait_DocOut").unwrap() });
  128. V7_PROCESS.get_or_init(|| unsafe { library.get::<S2V7_process>(b"S2V7_process").unwrap() });
  129. V7_GET_CARDTYPE.get_or_init(|| unsafe { library.get::<S2V7_get_cardType>(b"S2V7_get_cardType").unwrap() });
  130. V7_VIS_SAVECOLOR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveColor>(b"S2V7_VIS_saveColor").unwrap() });
  131. V7_VIS_SAVEIR.get_or_init(|| unsafe { library.get::<S2V7_VIS_saveIR>(b"S2V7_VIS_saveIR").unwrap() });
  132. V7_VIS_GETMRZTEXT.get_or_init(|| unsafe { library.get::<S2V7_VIS_getMRZtext>(b"S2V7_VIS_getMRZtext").unwrap() });
  133. V7_RDO_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_RDO_getBytesByIndex>(b"S2V7_RDO_getBytesByIndex").unwrap() });
  134. V7_VIS_getBytesByIndex.get_or_init(|| unsafe { library.get::<S2V7_VIS_getBytesByIndex>(b"S2V7_VIS_getBytesByIndex").unwrap() });
  135. V7_RF_active.get_or_init(|| unsafe { library.get::<S2V7_RF_active>(b"S2V7_RF_active").unwrap() });
  136. unsafe {
  137. INITDLL = true;
  138. }
  139. Ok(true)
  140. }
  141. fn init() -> c_int {
  142. let directory = unsafe { DIRECTORY_PATH.get().unwrap() };
  143. let userid = unsafe { USER_ID.get().unwrap() };
  144. let directory_path = std::path::Path::new(directory).join(MACHINE_KIND);
  145. if directory_path.exists() {
  146. let dll_path = directory_path.join(libloading::library_filename("STAR200_V7_DRV"));
  147. println!("dll_path: {:?}", dll_path);
  148. if dll_path.exists() {
  149. match init_dll(dll_path.to_str().unwrap()).is_ok() {
  150. true => {
  151. // 打开设备
  152. let init_result = unsafe {V7_OPEN.get_unchecked()()};
  153. if init_result == 0 {
  154. println!("设备打开成功");
  155. return ResultType::Success as c_int;
  156. } else {
  157. println!("设备打开失败,代码:{:?}",init_result);
  158. return ResultType::DeviceNotFound as c_int;
  159. }
  160. }
  161. false => {
  162. return ResultType::INITDLLFail as c_int;
  163. }
  164. }
  165. } else {
  166. return ResultType::DllPathNotExist as c_int;
  167. }
  168. } else {
  169. println!("{:?}", directory_path);
  170. return ResultType::DirectoryPathNotExist as c_int;
  171. }
  172. }
  173. pub fn init_by_node(mut cx: FunctionContext) -> JsResult<JsNumber> {
  174. let directory = cx.argument::<JsString>(0)?.value(&mut cx);
  175. let userid = cx.argument::<JsString>(1)?.value(&mut cx);
  176. unsafe {
  177. DIRECTORY_PATH.take();
  178. DIRECTORY_PATH.set(directory).unwrap();
  179. USER_ID.take();
  180. USER_ID.set(userid).unwrap();
  181. };
  182. let result = init() as f64;
  183. Ok(cx.number(result))
  184. }
  185. #[allow(dead_code)] // 允许dead_code
  186. enum ResultType {
  187. DirectoryPathNotExist = -2003, // 找不到运行目录
  188. DllPathNotExist = -2001, // 找不到dll文件
  189. INITDLLFail = -2000, // 初始化dll
  190. Success = 0, // 成功
  191. UserIdFail = 2001, //用户 ID 错误
  192. DeviceInitFail = 2002, // 设备初始化失败
  193. DeviceKernelInitFail = 2003, // 初始化核心失败
  194. DeviceDatInitFail = 2004, //未找到授权文件
  195. DeviceNotInit = 2101, // 设备未初始化
  196. DeviceNotFound = 2102, // 没有找到设备
  197. DeviceReConnect = 2103, // 重新连接设备
  198. Unknown = -100, // 未知错误
  199. CheckCardInput = 3001, // 证件放入设备
  200. CheckCardOut = 3002, // 证件移出设备
  201. CheckCardNotInOrOut = 3000, // 证件无放入或拿出
  202. CheckCardBarCode = 3003, // 检测到手机条码
  203. OCRFail=-1, // 未检测到OCR区域
  204. }
  205. type RecogIDCardEXResultItem = HashMap<i32, [String; 2]>;
  206. #[derive(Default, Serialize)]
  207. pub struct RecogIDCardEXResultObject {
  208. pub viz_result: RecogIDCardEXResultItem,
  209. pub viz_orc_result: RecogIDCardEXResultItem,
  210. pub mrz_result: RecogIDCardEXResultItem,
  211. pub mrz_ocr_result: RecogIDCardEXResultItem,
  212. pub chip_result: RecogIDCardEXResultItem,
  213. }
  214. #[derive(Default, Serialize)]
  215. pub struct RecogIDCardEXResult {
  216. pub device_name: String,
  217. pub device_online: bool,
  218. pub reg_info: RecogIDCardEXResultObject,
  219. pub img_base64: HashMap<String, String>,
  220. pub card_type: i32,
  221. pub record_type: i32,
  222. pub card_name: String,
  223. pub success: bool, // 识别是否成功
  224. }
  225. static SAVE_IMAGE_REUSLT_NAME: [&str; 5] = [
  226. "tempHeadEC.jpg",
  227. "tempHead.jpg",
  228. "tempUV.jpg",
  229. "tempIR.jpg",
  230. "temp.jpg",
  231. ];
  232. fn start(mut cx: FunctionContext) -> JsResult<JsUndefined> {
  233. let callback = cx.argument::<JsFunction>(0)?.root(&mut cx);
  234. let mut channel = cx.channel();
  235. channel.reference(&mut cx);
  236. println!("start {}", channel.has_ref());
  237. let index = unsafe {
  238. DEVICE_START_INDEX += 1;
  239. DEVICE_START_INDEX
  240. };
  241. std::thread::spawn(move || {
  242. // Do the heavy lifting inside the background thread.
  243. device_start(callback, channel, index);
  244. });
  245. Ok(cx.undefined())
  246. }
  247. use std::sync::{Arc, Mutex};
  248. fn device_start(callback: Root<JsFunction>, channel: Channel, index: u64) {
  249. let index = index;
  250. let callback = Arc::new(Mutex::new(callback));
  251. //设置读证功能
  252. unsafe { V7_SET_MODE.get_unchecked()(1,1,1,1) };
  253. loop {
  254. if index != unsafe { DEVICE_START_INDEX } {
  255. break;
  256. };
  257. let callback_clone = Arc::clone(&callback);
  258. let mut result = RecogIDCardEXResult::default();
  259. let mut flg_in:i8=0;
  260. match unsafe { V7_WAIT_DOCINT.get_unchecked()(5.0,&mut flg_in) } {
  261. // 设备正常 检测是否有放入证件
  262. 0 => {
  263. if flg_in==0{
  264. //检查是否放入超时
  265. result.record_type = ResultType::CheckCardNotInOrOut as i32;
  266. break;
  267. }
  268. result.device_online = true;
  269. result.device_name =unsafe { DEVCIE_NAME.get_or_init(|| "".to_string()).to_string() };
  270. match unsafe { V7_PROCESS.get_unchecked()() } {
  271. // 证件有放入
  272. 0 => {
  273. result.record_type = ResultType::CheckCardInput as i32;
  274. }
  275. // 未检测到OCR区域
  276. -1 => {
  277. result.record_type = ResultType::OCRFail as i32;
  278. }
  279. // 未找到非接卡
  280. // v if v/10 == -21 => {
  281. // result.record_type = ResultType::OCRFail as i32;
  282. // }
  283. // 设备离线
  284. -3 => {
  285. result.device_online = false;
  286. result.record_type = init();
  287. }
  288. _ => {
  289. result.record_type = ResultType::Unknown as i32;
  290. }
  291. }
  292. }
  293. -3 => {
  294. //设备离线
  295. let init = init();
  296. result.device_online = false;
  297. result.record_type = init;
  298. }
  299. _ => {
  300. //未知错误
  301. result.record_type = ResultType::Unknown as i32;
  302. }
  303. };
  304. if unsafe { *NEED_RECORD.get_or_init(|| false) } {
  305. println!("手工点击识别+1");
  306. result.record_type = ResultType::CheckCardInput as i32;
  307. }
  308. // let time_now = std::time::Instant::now();
  309. if result.record_type == ResultType::CheckCardInput as i32 {
  310. let _result = recog_card();
  311. result.success = _result.success;
  312. result.img_base64 = _result.img_base64;
  313. result.reg_info = _result.reg_info;
  314. result.card_type = _result.card_type;
  315. result.card_name = _result.card_name;
  316. }
  317. let neet_sendinfo = if Some(true) == unsafe { NEED_RECORD.take() } {
  318. true
  319. } else {
  320. false
  321. };
  322. // let elapsed = time_now.elapsed();
  323. // println!("识别时间结束时间 {:.6?}", elapsed);
  324. if result.record_type != ResultType::CheckCardNotInOrOut as i32
  325. && (*unsafe { RESULT_TYPE.get_or_init(|| -10000) } != result.record_type
  326. || result.record_type == ResultType::CheckCardInput as i32
  327. || neet_sendinfo)
  328. {
  329. unsafe {
  330. RESULT_TYPE.take();
  331. RESULT_TYPE.set(result.record_type).unwrap();
  332. }
  333. channel.send(move |mut cx| {
  334. let result_json = serde_json::to_string(&result).unwrap();
  335. let callback = callback_clone.lock().unwrap().to_inner(&mut cx);
  336. let this = cx.undefined();
  337. let args = vec![cx.string(&result_json)];
  338. callback.call(&mut cx, this, args)?;
  339. Ok(())
  340. });
  341. }
  342. std::thread::sleep(std::time::Duration::from_millis(20));
  343. }
  344. }
  345. // 白光图、红外
  346. // 图、紫外图、版面头像和芯片头像
  347. pub fn recog_card() -> RecogIDCardEXResult {
  348. let time_now = std::time::Instant::now();
  349. let mut result = RecogIDCardEXResult::default();
  350. result.device_online = true;
  351. let img_path_directory = std::path::Path::new(unsafe { DIRECTORY_PATH.get().unwrap() });
  352. let ir_img_path = img_path_directory.join("ir.jpg");
  353. let color_img_path = img_path_directory.join("color.jpg");
  354. //显示红外照
  355. let irResult = unsafe {V7_VIS_SAVEIR.get_unchecked()(encode(ir_img_path.to_str().unwrap()).as_ptr() as LPCTSTR)};
  356. //显示彩照
  357. let colorResult = unsafe {V7_VIS_SAVECOLOR.get_unchecked()(encode(color_img_path.to_str().unwrap()).as_ptr() as LPCTSTR)};
  358. if irResult==0{
  359. if ir_img_path.exists() {
  360. match std::fs::read(&ir_img_path) {
  361. Ok(image_data) => {
  362. println!("读取照片成功");
  363. let image_data = base64::encode(&image_data);
  364. let base64_string = String::from("data:image/jpg;base64,");
  365. std::fs::remove_file(ir_img_path).unwrap();
  366. result.img_base64.insert("0".to_string(), base64_string + &image_data);
  367. }
  368. Err(e) => {
  369. println!("读取照片抛异常");
  370. println!(
  371. "{:?} {:?}",
  372. e,
  373. "ir.jpg",
  374. );
  375. }
  376. };
  377. }
  378. }
  379. if colorResult==0{
  380. if color_img_path.exists() {
  381. match std::fs::read(&color_img_path) {
  382. Ok(image_data) => {
  383. println!("读取照片成功");
  384. let image_data = base64::encode(&image_data);
  385. let base64_string = String::from("data:image/jpg;base64,");
  386. std::fs::remove_file(color_img_path).unwrap();
  387. result.img_base64.insert("1".to_string(), base64_string + &image_data);
  388. }
  389. Err(e) => {
  390. println!("读取照片抛异常");
  391. println!(
  392. "{:?} {:?}",
  393. e,
  394. "color.jpg",
  395. );
  396. }
  397. };
  398. }
  399. }
  400. let mut ocritem = RecogIDCardEXResultObject::default();
  401. let mut index: c_int = 0;
  402. //orc识别文字
  403. let mut mrztext = [0; 1024];
  404. let x = unsafe {V7_VIS_GETMRZTEXT.get_unchecked()(mrztext.as_mut_ptr())};
  405. if x==0{
  406. let result_item = ["MRZ".to_string(), decode(&mrztext)];
  407. ocritem.mrz_result.insert(index, result_item);
  408. index+=1;
  409. }
  410. let mut data:[u16; 256] = [0; 256];
  411. let mut len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(0,data.as_mut_ptr())};
  412. if len>0{
  413. ocritem.mrz_result.insert(index, ["编号".to_string(), decode(&data)]);
  414. index+=1;
  415. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(1,data.as_mut_ptr())};
  416. ocritem.mrz_result.insert(index, ["国籍".to_string(), decode(&data)]);
  417. index+=1;
  418. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(2,data.as_mut_ptr())};
  419. ocritem.mrz_result.insert(index, ["民族".to_string(), decode(&data)]);
  420. index+=1;
  421. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(3,data.as_mut_ptr())};
  422. ocritem.mrz_result.insert(index, ["姓名".to_string(), decode(&data)]);
  423. index+=1;
  424. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(4,data.as_mut_ptr())};
  425. let cardType= unsafe {V7_GET_CARDTYPE.get_unchecked()()};
  426. if cardType==101{
  427. //身份证是UTF8格式
  428. ocritem.mrz_result.insert(index, ["中文名".to_string(), decode(&data)]);
  429. }else{
  430. ocritem.mrz_result.insert(index, ["中文名".to_string(), decode(&data)]);
  431. // //中国护照的中文姓名 是GBK编码的
  432. // let name=GB18030.decode(&data, DecoderTrap::Strict).unwrap();
  433. // ocritem.mrz_result.insert(index, ["中文名".to_string(), name.replace("\u{0}","")]);
  434. }
  435. index+=1;
  436. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(5,data.as_mut_ptr())};
  437. ocritem.mrz_result.insert(index, ["性别".to_string(), decode(&data)]);
  438. index+=1;
  439. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(6,data.as_mut_ptr())};
  440. ocritem.mrz_result.insert(index, ["出生日期".to_string(), decode(&data)]);
  441. index+=1;
  442. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(7,data.as_mut_ptr())};
  443. ocritem.mrz_result.insert(index, ["地址".to_string(), decode(&data)]);
  444. index+=1;
  445. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(8,data.as_mut_ptr())};
  446. ocritem.mrz_result.insert(index, ["签发机关".to_string(), decode(&data)]);
  447. index+=1;
  448. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(9,data.as_mut_ptr())};
  449. ocritem.mrz_result.insert(index, ["有效期始".to_string(), decode(&data)]);
  450. index+=1;
  451. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(10,data.as_mut_ptr())};
  452. ocritem.mrz_result.insert(index, ["有效期止".to_string(), decode(&data)]);
  453. index+=1;
  454. len = unsafe {V7_RDO_getBytesByIndex.get_unchecked()(20,data.as_mut_ptr())};
  455. ocritem.mrz_result.insert(index, ["港澳台ID".to_string(), decode(&data)]);
  456. index+=1;
  457. }
  458. else{
  459. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(0,data.as_mut_ptr())};
  460. ocritem.mrz_result.insert(index, ["编号".to_string(), decode(&data)]);
  461. index+=1;
  462. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(1,data.as_mut_ptr())};
  463. ocritem.mrz_result.insert(index, ["国籍".to_string(), decode(&data)]);
  464. index+=1;
  465. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(2,data.as_mut_ptr())};
  466. ocritem.mrz_result.insert(index, ["民族".to_string(), decode(&data)]);
  467. index+=1;
  468. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(3,data.as_mut_ptr())};
  469. ocritem.mrz_result.insert(index, ["姓名".to_string(), decode(&data)]);
  470. index+=1;
  471. //中国护照的中文姓名 是GBK编码的, 身份证不会执行到这里
  472. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(4,data.as_mut_ptr())};
  473. ocritem.mrz_result.insert(index, ["中文名".to_string(), decode(&data)]);
  474. // let name=GB18030.decode(&data, DecoderTrap::Strict).unwrap();
  475. // ocritem.mrz_result.insert(index, ["中文名".to_string(), name.replace("\u{0}","")]);
  476. index+=1;
  477. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(5,data.as_mut_ptr())};
  478. ocritem.mrz_result.insert(index, ["性别".to_string(), decode(&data)]);
  479. index+=1;
  480. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(6,data.as_mut_ptr())};
  481. ocritem.mrz_result.insert(index, ["出生日期".to_string(), decode(&data)]);
  482. index+=1;
  483. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(7,data.as_mut_ptr())};
  484. ocritem.mrz_result.insert(index, ["地址".to_string(), decode(&data)]);
  485. index+=1;
  486. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(8,data.as_mut_ptr())};
  487. ocritem.mrz_result.insert(index, ["签发机关".to_string(), decode(&data)]);
  488. index+=1;
  489. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(9,data.as_mut_ptr())};
  490. ocritem.mrz_result.insert(index, ["有效期始".to_string(), decode(&data)]);
  491. index+=1;
  492. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(10,data.as_mut_ptr())};
  493. ocritem.mrz_result.insert(index, ["有效期止".to_string(), decode(&data)]);
  494. index+=1;
  495. len = unsafe {V7_VIS_getBytesByIndex.get_unchecked()(20,data.as_mut_ptr())};
  496. ocritem.mrz_result.insert(index, ["港澳台ID".to_string(), decode(&data)]);
  497. index+=1;
  498. }
  499. result.reg_info=ocritem;
  500. result.success = true;
  501. result.card_type = unsafe {V7_GET_CARDTYPE.get_unchecked()()};
  502. let elapsed = time_now.elapsed();
  503. println!("{:.6?}", elapsed);
  504. return result;
  505. }
  506. pub fn regcord_by_node(mut cx: FunctionContext) -> JsResult<JsNull> {
  507. println!("regcord_by_node");
  508. unsafe {
  509. NEED_RECORD.take();
  510. NEED_RECORD.set(true).unwrap();
  511. };
  512. Ok(cx.null())
  513. }
  514. #[neon::main]
  515. fn main(mut cx: ModuleContext) -> NeonResult<()> {
  516. cx.export_function("init", init_by_node)?;
  517. cx.export_function("start", start)?;
  518. cx.export_function("regcord_by_node", regcord_by_node)?;
  519. Ok(())
  520. }

使用rust调用c++静态库并编译nodejs包的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 五、C++运算符重载,使面向对象编程更方便

    复数类CComplex 编译器做对象运算的时候,会调用对象的运算符重载函数(优先调用成员方法):如果没有成员方法,就砸全局作用域找合适的运算符重载函数 ++和--运算符是单目运算符,在参数列表里放上一 ...

  2. 论文阅读 DyREP:Learning Representations Over Dynamic Graphs

    5 DyREP:Learning Representations Over Dynamic Graphs link:https://scholar.google.com/scholar_url?url ...

  3. HttpContext.TraceIdentifier那严谨的设计

    前言 Asp.Net Core中有一个不受人重视的属性HttpContext.TraceIdentifier,它在链路追踪中非常有用,下面是官方的定义: 在项目中一般会将该字段输出到每一条日志中,也可 ...

  4. linux篇-centos7 安装cacti

    1 cacti运行环境准备 cacti需要php+apache+mysql+snmp+RRDTool,以及cacti本身.cacti本体是用php开发的网站,通过snmp对远端设备信息进行采集.apa ...

  5. 137_Power BI 自定义矩阵复刻Beyondsoft Calendar

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 前两天我们用PBI原生的视觉制作了自定义的热力图,今天我们来复刻一个Beyondsoft Calendar 1. ...

  6. 121_Power Query之R.Execute的read.xlsx&ODBC

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.问题 pq在用 Excel.Workbook 读取一些Excel早期版本(.xls后缀)的文件时候,报错:DataFo ...

  7. P4169 [Violet]天使玩偶

    两种操作:1.加入点(x,y); 2.查询距(x,y)最近的点的曼哈顿距离距离 思路:绝对值拆开通常可以取max,不过这里直接分类讨论4种情况,我们发现如果找\(i\)点左下点\(j\)\((x_j& ...

  8. Eureka属性配置

    一:Eureka Instance实例信息配置   里面的配置以"-"隔开 其实也支持驼峰命名代替"-" 首先是入门时的配置: server: port: 80 ...

  9. while循环、do..while循环

    While循环 While循环呢它是更具条件来判断是否执行大括号里的内容 ,只要条件成立就会一值执行直到不满足条件它的语法格式: while(循环条件){ 执行语句 }那么我们来做一个小测试看看: p ...

  10. 开发工具-PowerShell下载地址

    更新日志 2022年6月10日 初始化链接. https://github.com/PowerShell/PowerShell/releases/