
代码
use std::collections::HashMap;
use std::ffi::{CString, CStr};
use std::fs::{self, File};
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::ptr;
use std::time::{SystemTime, UNIX_EPOCH};
use chrono::{Local, DateTime, TimeZone};
use winapi::um::winuser::{MessageBoxA, MB_OK, MB_ICONERROR, MB_ICONINFORMATION};
use libc::{c_char, c_int, c_uint, c_uchar, size_t};
// 定义DLL函数绑定
#[link(name = "proRFLV102024", kind = "dylib")]
extern "stdcall" {
// proRFLV102024.dll
fn GetDLLVersion(sDllVer: *mut c_char) -> c_int;
fn CloseUSB();
fn Buzzer(fUSB: c_uchar, t: c_int) -> c_int;
fn ReadCard(fUSB: c_uchar, Buffer: *mut c_uchar) -> c_int;
#[link_name = "ReadCard"]
fn ReadCard_v10(fUSB: c_uchar, Buffer: *mut c_uchar) -> c_int;
fn ReadCardID_T5557(fUSB: c_uchar, Buffer: *mut c_uchar) -> c_int;
fn GuestCard(
fUSB: c_uchar,
dlsCoID: c_int,
CardNo: c_uchar,
dai: c_uchar,
llock: c_uchar,
pdoors: c_uchar,
BDate: *const c_char,
EDate: *const c_char,
RoomNo: *const c_char,
CardHexStr: *mut c_uchar,
) -> c_int;
#[link_name = "GuestCard"]
fn GuestCard_原始(
d12: c_int,
dlsCoID: c_int,
CardNo: c_int,
dai: c_int,
LLock: c_int,
pdoors: c_int,
BDate: *const c_char,
EDate: *const c_char,
RoomNo: *const c_char,
cardHexStr: *mut c_char,
) -> c_int;
fn LimitCard(
fUSB: c_uchar,
dlsCoID: c_int,
CardNo: c_uchar,
dai: c_uchar,
BDate: *const c_char,
LCardNo: *const c_char,
CardHexStr: *const c_char,
) -> c_int;
fn CardErase(fUSB: c_uchar, dlsCoID: c_int, cardHexStr: *mut c_uchar) -> c_int;
#[link_name = "CardErase"]
fn CardErase_V10(d12: c_int, dlsCoID: c_int, CardNo: *mut c_char) -> c_int;
fn hex_a(hex: *const c_char, asc: *mut c_char, hLen: c_int) -> c_int;
fn a_hex(asc: *const c_char, hex: *mut c_char, aLen: c_int) -> c_int;
fn GetCardTypeByCardDataStr(cardHexStr: *const c_char, CardType: *mut c_char) -> c_int;
fn GetGuestLockNoByCardDataStr(dlsCoID: c_int, cardHexStr: *const c_char, LockNo: *mut c_char) -> c_int;
fn GetGuestETimeByCardDataStr(dlsCoID: c_int, cardHexStr: *mut c_uchar, eTime: *mut c_uchar) -> c_int;
fn ReadRecord(fUSB: c_uchar, bufData: *mut c_char) -> c_int;
fn GetOpenRecordByDataStr(DataStr: *const c_char, sOpen: *mut c_char) -> c_int;
}
#[link(name = "proRFLP50202501", kind = "dylib")]
extern "stdcall" {
// proRFLP50202501.dll
#[link_name = "GetDLLVersion"]
fn GetDLLVersion_P50(sDllVer: *mut c_char) -> c_int;
#[link_name = "initializeUSB"]
fn initializeUSB_P50(d12: c_int) -> c_int;
#[link_name = "CloseUSB"]
fn CloseUSB_P50(d12: c_int);
#[link_name = "Buzzer"]
fn Buzzer_P50(fUSB: c_uchar, t: c_int) -> c_int;
#[link_name = "CardErase"]
fn CardErase_P50(d12: c_int, dlsCoID: c_int, CardNo: *mut c_char) -> c_int;
#[link_name = "GuestCard"]
fn GuestCard_P50(
fUSB: c_uchar,
dlsCoID: c_int,
CardNo: c_uchar,
dai: c_uchar,
llock: c_uchar,
pdoors: c_uchar,
BDate: *const c_char,
EDate: *const c_char,
RoomNo: *const c_char,
CardHexStr: *mut c_uchar,
) -> c_int;
#[link_name = "GuestCard"]
fn GuestCard_原始_P50(
d12: c_int,
dlsCoID: c_int,
CardNo: c_int,
dai: c_int,
LLock: c_int,
pdoors: c_int,
BDate: *const c_char,
EDate: *const c_char,
RoomNo: *const c_char,
cardHexStr: *mut c_char,
) -> c_int;
#[link_name = "GetGuestLockNoByCardDataStr"]
fn GetGuestLockNoByCardDataStr_P50(dlsCoID: c_int, cardHexStr: *const c_char, LockNo: *mut c_char) -> c_int;
}
// 全局常量
const BUF_CARD_SIZE: usize = 129; // 128 + 1
const BUF_CARD_V10_SIZE: usize = 201; // 200 + 1
// 全局变量(使用Mutex保证线程安全)
lazy_static::lazy_static! {
static ref CARD_DATA: std::sync::Mutex<[u8; 128]> = std::sync::Mutex::new([0u8; 128]);
static ref ID_PHOTO_SAVE_PATH: std::sync::Mutex<String> = std::sync::Mutex::new(String::new());
static ref BUF_CARD: std::sync::Mutex<[u8; BUF_CARD_SIZE]> = std::sync::Mutex::new([0u8; BUF_CARD_SIZE]);
static ref BUF_CARD_V10: std::sync::Mutex<[u8; BUF_CARD_V10_SIZE]> = std::sync::Mutex::new([0u8; BUF_CARD_V10_SIZE]);
}
// 模拟C#的NameValueCollection
type NameValueCollection = HashMap<String, String>;
// 协议解析结构体
#[derive(Default)]
struct ClCyberWinAPPProtocolPackage {
data: HashMap<String, String>,
}
impl ClCyberWinAPPProtocolPackage {
fn new() -> Self {
ClCyberWinAPPProtocolPackage {
data: HashMap::new(),
}
}
// 解析协议字符串(支持 "key=value&key2=value2" 格式)
fn format_string(&mut self, param: &str) {
self.data.clear();
for pair in param.split('&') {
let parts: Vec<&str> = pair.splitn(2, '=').collect();
if parts.len() == 2 {
self.data.insert(parts[0].to_string(), parts[1].to_string());
}
}
}
fn get(&self, key: &str) -> String {
self.data.get(key).cloned().unwrap_or_default()
}
}
// 弹窗函数(模拟MessageBox)
fn show_message_box(title: &str, message: &str, icon: u32) {
let title_c = CString::new(title).unwrap();
let message_c = CString::new(message).unwrap();
unsafe {
MessageBoxA(
ptr::null_mut(),
message_c.as_ptr(),
title_c.as_ptr(),
MB_OK | icon,
);
}
}
// 信息弹窗
fn info_box(title: &str, message: &str) {
show_message_box(title, message, MB_ICONINFORMATION);
}
// 错误弹窗
fn error_box(title: &str, message: &str) {
show_message_box(title, message, MB_ICONERROR);
}
// 日志写入函数
fn write_log(capture_type: &str, log_type: &str, content: &str) -> io::Result<()> {
// 获取可执行文件路径
let exe_path = std::env::current_exe()?;
let exe_dir = exe_path.parent().unwrap_or(Path::new("."));
// 构建日志路径
let now = Local::now();
let date_str = now.format("%Y-%m-%d").to_string();
let log_dir = exe_dir.join("log").join(capture_type).join(date_str);
// 创建目录
fs::create_dir_all(&log_dir)?;
// 日志文件路径
let log_path = log_dir.join(format!("{}_log.log", log_type));
// 写入日志内容
let mut file = File::options().append(true).create(true).open(log_path)?;
let log_time = now.format("%Y-%m-%d %H:%M:%S").to_string();
writeln!(file, "==============================")?;
writeln!(file, "{}<<<<<<<<<<<<<<<<<<<<<<<<<<", log_time)?;
writeln!(file, "{}", content)?;
writeln!(file)?;
Ok(())
}
// 模拟C#的Copy函数(从字节数组截取字符串)
fn copy_bytes(data: &[u8], start: usize, length: usize) -> String {
// C#是1-based索引,转换为0-based
let start_idx = if start > 0 { start - 1 } else { 0 };
let end_idx = start_idx + length;
// 确保不越界
let end_idx = end_idx.min(data.len());
if start_idx >= end_idx {
return String::new();
}
// 转换为字符串(ASCII编码)
String::from_utf8_lossy(&data[start_idx..end_idx]).into_owned()
}
// 读卡函数(旧版本)
fn rd_card() -> bool {
let mut buf_card = BUF_CARD.lock().unwrap();
let st = unsafe { ReadCard(1, buf_card.as_mut_ptr()) };
if st != 0 {
if st == 1 {
error_box(
"读卡失败(返回值=1)",
"请放一张卡在发卡器上面,\n确保 门锁软件 可以正常发卡,然后调试接口",
);
} else {
error_box("提示", &format!("读卡失败\n错误码: {}", st));
}
return false;
}
// 验证卡数据
let card_data_str = copy_bytes(&buf_card, 5, 2);
if card_data_str != "01" {
error_box("提示", "发卡器的感应区无卡");
return false;
}
true
}
// V10版本读卡函数
fn rd_card_v10() -> bool {
let mut buf_card_v10 = BUF_CARD_V10.lock().unwrap();
let st = unsafe { ReadCard_v10(1, buf_card_v10.as_mut_ptr()) };
if st != 0 {
error_box("提示", &format!("读卡失败\n错误码: {}", st));
return false;
}
true
}
// 获取卡片标识
fn cyber_win_locak_app_get_sign(buf_card: &[u8]) -> String {
// 检查是否为空白卡
if copy_bytes(buf_card, 25, 8) == "FFFFFFFF" {
error_box("提示", "此卡是空白卡,请换一张能开门的卡");
return "此卡是空白卡,请换一张能开门的卡".to_string();
}
// 计算酒店标识
let s = copy_bytes(buf_card, 11, 4);
let i = u64::from_str_radix(&s, 16).unwrap_or(0) % 16384;
let s2 = copy_bytes(buf_card, 9, 2);
let i2 = u64::from_str_radix(&s2, 16).unwrap_or(0);
let i = i + i2 * 65536;
// 最终计算
let i_final = i2 * 65536 + (i % 16383);
i_final.to_string()
}
// 构建卡片信息JSON
fn build_card_info_json(
status: &str,
hotel_sign: &str,
message: &str,
lock_no: &str,
physical_no: &str,
check_in_time: &str,
check_out_time: &str,
llock: &str,
) -> String {
format!(
r#"{{
"status": "{}",
"hotelsign": "{}",
"message": "{}",
"lockno": "{}",
"physical_no": "{}",
"checkingintime": "{}",
"checkingouttime": "{}",
"llock": "{}"
}}"#,
status, hotel_sign, message, lock_no, physical_no, check_in_time, check_out_time, llock
)
}
// ------------------------------ 业务函数 ------------------------------
// 启动函数
pub fn start(obj: &NameValueCollection) -> String {
let _param1 = obj.get("param1").cloned().unwrap_or_default();
"随机预安装插件".to_string()
}
// 状态函数
pub fn status(obj: &NameValueCollection) -> String {
// 调用蜂鸣器(fUSB=1, 时长50ms)
unsafe { Buzzer(1, 50) };
"当你听到设备蜂鸣器,说明设备已经连接".to_string()
}
// 退房(注销卡片)
pub fn checking_out(obj: &NameValueCollection) -> String {
let mut result = "注销卡片".to_string();
let param = obj.get("param").cloned().unwrap_or_default();
// 解析协议
let mut cl_app = ClCyberWinAPPProtocolPackage::new();
cl_app.format_string(¶m);
let _ = write_log("酒店智能门锁", "P50", &format!("注销,{}", param));
let hotel_sign_str = cl_app.get("hotelsign");
let hotel_sign = match hotel_sign_str.parse::<i32>() {
Ok(v) => v,
Err(_) => {
return format!("{}:酒店标识格式错误", result);
}
};
// 初始化USB设备(1=proUSB)
let st = unsafe { initializeUSB_P50(1) };
if st != 0 {
error_box("错误", "设备打开失败");
return "打开端口失败".to_string();
}
// 确保退出时关闭设备
struct CloseUsbGuard;
impl Drop for CloseUsbGuard {
fn drop(&mut self) {
unsafe { CloseUSB_P50(1) };
}
}
let _guard = CloseUsbGuard;
// 注销卡片
let mut card_no_buf = [0u8; 100];
let st = unsafe {
CardErase_P50(
1,
hotel_sign,
card_no_buf.as_mut_ptr() as *mut c_char,
)
};
if st != 0 {
let msg = format!("注销失败\n错误码: {}", st);
info_box("提示", &msg);
result = format!("{}:注销失败{}", result, st);
} else {
result = format!("{}:成功", result);
}
result
}
// 入住(发卡)
pub fn checking_in(obj: &NameValueCollection) -> String {
let mut result = "酒店入住发卡".to_string();
let param = obj.get("param").cloned().unwrap_or_default();
// 解析协议
let mut cl_app = ClCyberWinAPPProtocolPackage::new();
cl_app.format_string(¶m);
let _ = write_log("酒店智能门锁", "P50", &format!("入住,{}", param));
let lock_no = cl_app.get("lockno");
let hotel_sign_str = cl_app.get("hotelsign");
let check_out_time = cl_app.get("checkingouttime");
// 验证锁号长度
if lock_no.len() < 6 {
error_box("提示", &format!("锁号长度错误={}", lock_no));
return String::new();
}
let hotel_sign = match hotel_sign_str.parse::<i32>() {
Ok(v) => v,
Err(_) => {
return format!("{}:酒店标识格式错误", result);
}
};
// 初始化USB设备
let st = unsafe { initializeUSB_P50(1) };
if st != 0 {
error_box("错误", "设备打开失败");
return "打开端口失败".to_string();
}
// 确保退出时关闭设备
struct CloseUsbGuard;
impl Drop for CloseUsbGuard {
fn drop(&mut self) {
unsafe { CloseUSB_P50(1) };
}
}
let _guard = CloseUsbGuard;
// 生成时间字符串(yyMMddHHmmss)
let check_in_time = Local::now().format("%y%m%d%H%M%S").to_string();
// 发卡参数
let dai = 1; // DAI值
let llock = 1; // 反锁标志
let mut card_hex_str = [0u8; 500];
// 转换字符串为CString
let check_in_time_c = CString::new(check_in_time).unwrap();
let check_out_time_c = CString::new(check_out_time).unwrap();
let lock_no_c = CString::new(lock_no).unwrap();
// 调用发卡函数
let st = unsafe {
GuestCard_原始_P50(
1,
hotel_sign,
0,
dai,
llock,
0,
check_in_time_c.as_ptr(),
check_out_time_c.as_ptr(),
lock_no_c.as_ptr(),
card_hex_str.as_mut_ptr() as *mut c_char,
)
};
if st != 0 {
let msg = format!("调用发卡函数失败\n错误码: {}", st);
info_box("提示", &msg);
result = format!("{}调用发卡函数失败", result);
} else {
result = format!("{}制卡成功V2024{}", result, lock_no);
}
result
}
// 获取卡片标识
pub fn get_sign(obj: &NameValueCollection) -> String {
if !rd_card_v10() {
return "读卡失败".to_string();
}
let buf_card_v10 = BUF_CARD_V10.lock().unwrap();
cyber_win_locak_app_get_sign(&buf_card_v10)
}
// 读取房卡信息
pub fn read_card_info(obj: &NameValueCollection) -> String {
let param = obj.get("param").cloned().unwrap_or_default();
// 解析协议
let mut cl_app = ClCyberWinAPPProtocolPackage::new();
cl_app.format_string(¶m);
let _ = write_log("酒店智能门锁", "P50", &format!("读卡,{}", param));
let hotel_sign_str = cl_app.get("hotelsign");
let hotel_sign = match hotel_sign_str.parse::<i32>() {
Ok(v) => v,
Err(_) => {
return build_card_info_json(
"3",
&hotel_sign_str,
"酒店标识格式错误",
"",
"",
"",
"",
"",
);
}
};
// 模拟卡数据(实际应从读卡获取)
let card_data_hex = "551501C1011B4D9D1B0601036707CB2C07D30000000000000000000000000000000000325CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00";
let mut lock_no_buf = [0u8; 50];
// 转换为CString
let card_data_hex_c = CString::new(card_data_hex).unwrap();
// 调用DLL获取卡信息
let st = unsafe {
GetGuestLockNoByCardDataStr_P50(
hotel_sign,
card_data_hex_c.as_ptr(),
lock_no_buf.as_mut_ptr() as *mut c_char,
)
};
let (status, message, lock_no, physical_no, check_in_time, check_out_time, llock) = match st {
-4 => (
"4",
&format!("空白卡或者已经注销的卡片,返回值:{}", st),
"", "", "", "", "",
),
-3 => (
"3",
&format!("非本酒店卡,酒店标识不匹配,返回值:{}", st),
"", "", "", "", "",
),
-2 => (
"3",
&format!("没有有效卡片,返回值:{}", st),
"", "", "", "", "",
),
0 => {
// 解析返回数据
let lock_no = copy_bytes(&lock_no_buf, 1, 6);
let check_in_time = copy_bytes(&lock_no_buf, 7, 12);
let check_out_time = copy_bytes(&lock_no_buf, 19, 12);
let physical_no = copy_bytes(&lock_no_buf, 33, 8);
let llock = copy_bytes(&lock_no_buf, 31, 1);
(
"9",
"读取成功",
&lock_no,
&physical_no,
&check_in_time,
&check_out_time,
&llock,
)
}
1 => (
"1",
&format!("连接发卡器失败,返回值:{}", st),
"", "", "", "", "",
),
_ => (
"4",
&format!("未知返回值:{}", st),
"", "", "", "", "",
),
};
// 显示弹窗
if st != 0 {
info_box("提示", message);
}
build_card_info_json(
status,
&hotel_sign_str,
message,
lock_no,
physical_no,
check_in_time,
check_out_time,
llock,
)
}
// 初始化函数(可选)
pub fn init() {
// 读取DLL版本信息
let mut ver_buf = [0u8; 256];
unsafe {
GetDLLVersion(ver_buf.as_mut_ptr() as *mut c_char);
}
let version = CStr::from_bytes_until_nul(&ver_buf)
.unwrap()
.to_str()
.unwrap_or("未知");
println!("DLL版本: {}", version);
}
阿雪技术观
在科技发展浪潮中,我们不妨积极投身技术共享。不满足于做受益者,更要主动担当贡献者。无论是分享代码、撰写技术博客,还是参与开源项目维护改进,每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地,我们携手在此探索硅基生命,为科技进步添砖加瓦。
Hey folks, in this wild tech - driven world, why not dive headfirst into the whole tech - sharing scene? Don't just be the one reaping all the benefits; step up and be a contributor too. Whether you're tossing out your code snippets, hammering out some tech blogs, or getting your hands dirty with maintaining and sprucing up open - source projects, every little thing you do might just end up being a massive force that pushes tech forward. And guess what? The Eastern FairyAlliance is this awesome place where we all come together. We're gonna team up and explore the whole silicon - based life thing, and in the process, we'll be fueling the growth of technology.