news 2026/4/3 4:38:42

幽冥大陆(五十二)V10酒店门锁SDK TypeScript——东方仙盟筑基期

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
幽冥大陆(五十二)V10酒店门锁SDK TypeScript——东方仙盟筑基期

实现硬件交互(读卡、发卡、蜂鸣器等),而 TypeScript/Node.js 运行在 Node.js 环境中,无法直接调用 Windows 的 DLL 文件。因此转换方案分为两部分:

  1. 保留核心业务逻辑和数据处理部分,用 TypeScript 实现;
  2. 对于硬件交互的 DLL 调用部分,提供Node.js 调用本地 DLL 的方案(使用ffi-napi库)作为替代,同时标注需要适配的硬件交互逻辑。

一、环境准备

首先需要安装 Node.js 环境,然后安装依赖包:

bash

运行

# 初始化项目(如果没有package.json) npm init -y # 安装TypeScript及类型定义 npm install typescript @types/node --save-dev # 安装调用DLL的依赖(仅Windows平台可用) npm install ffi-napi ref-napi ref-struct-napi --save # 安装处理URL参数的依赖(对应NameValueCollection) npm install querystring --save

二、TypeScript 转换代码

创建文件hotelDoorLock.ts,代码如下:

typescript

运行

// 引入Node.js核心模块 import * as fs from 'fs'; import * as path from 'path'; import * as querystring from 'querystring'; import { promisify } from 'util'; // 引入调用DLL的库(仅Windows平台) import ffi from 'ffi-napi'; import ref from 'ref-napi'; // -------------------------- 类型定义 -------------------------- // 对应C#的NameValueCollection(简化版,用对象模拟) type NameValueCollection = { [key: string]: string | undefined }; // 模拟C#的CyberWinAPPProtocolPackage协议解析类 class Cl_CyberWinAPPProtocolPackage { private data: { [key: string]: string } = {}; // 解析参数字符串(模拟原逻辑,可根据实际协议扩展) formatString(param: string): void { // 假设param是类似"hotelsign=123&lockno=456789&checkingouttime=2501010000"的格式 const parsed = querystring.parse(param); for (const key in parsed) { this.data[key] = parsed[key] as string; } } // 获取解析后的参数 get(key: string): string { return this.data[key] || ''; } } // -------------------------- 硬件交互(DLL调用) -------------------------- // 定义DLL的类型(对应原C#的DllImport) // 注意:需要根据DLL的实际导出函数签名调整类型,这里是模拟 const ProRFLV102024Dll = (() => { try { // 加载DLL文件(路径需与原C#一致,或调整为绝对路径) const dllPath = path.resolve(__dirname, 'CyberWinPHP/CyberPHP_Application/CyberWin_App/CyberWin_Hotdoorlock_proV10D202409/proRFLV102024.dll'); return ffi.Library(dllPath, { // 函数签名:返回值类型,[参数类型列表] 'Buzzer': ['int', ['byte', 'int']], // Buzzer(byte fUSB, int t) 'initializeUSB': ['int', ['int']], // initializeUSB(int d12) 'CloseUSB': ['void', []], // CloseUSB() 'CardErase': ['int', ['int', 'int', 'string']], // CardErase(int d12, int dlsCoID, string CardNo)(模拟,原是StringBuilder) 'GuestCard_原始': ['int', ['int', 'int', 'int', 'int', 'int', 'int', 'string', 'string', 'string', 'string']], // 模拟原函数 'ReadCard_v10': ['int', ['byte', 'ref']], // ReadCard_v10(byte fUSB, byte[] Buffer)(ref表示指针) }); } catch (err) { console.error('加载DLL失败:', err); // 返回空对象,避免程序崩溃(实际使用时需处理) return {} as any; } })(); // -------------------------- 核心业务类 -------------------------- class APP { private carddata: Buffer = Buffer.alloc(128); // 对应原byte[128] private 身份证照片保存路径: string = ''; public static bufCard: Buffer = Buffer.alloc(128 + 1); // 对应原静态变量 public static bufCard_v10: Buffer = Buffer.alloc(200 + 1); // 对应原静态变量 public static st: number = 0; // 对应原静态变量 // 初始化USB设备(对应原initializeUSB) private initializeUSB(fUSB: number): number { if (!ProRFLV102024Dll.initializeUSB) { return -1; // 表示DLL加载失败 } return ProRFLV102024Dll.initializeUSB(fUSB); } // 蜂鸣器(对应原Buzzer) private Buzzer(fUSB: number, t: number): number { if (!ProRFLV102024Dll.Buzzer) { return -1; } return ProRFLV102024Dll.Buzzer(fUSB, t); } // -------------------------- 原方法实现 -------------------------- // 启动方法(对应原start) start(obj: NameValueCollection): string { const 参数1 = obj['param1'] || ''; const s = '随机预安装插件'; return s; } // 设备状态检测(对应原status) status(obj: NameValueCollection): string { this.Buzzer(1, 50); // 蜂鸣器 return '当你听到设备蜂鸣器,说明设备已经连接'; } // 退房注销卡片(对应原checkingout) checkingout(obj: NameValueCollection): string { let s = '注销卡片'; const param = obj['param'] || ''; // 解析协议参数 const clApp = new Cl_CyberWinAPPProtocolPackage(); clApp.formatString(param); const 酒店标识 = clApp.get('hotelsign'); if (!酒店标识) { return s + ':酒店标识为空'; } // 初始化USB设备 const st读卡器 = this.initializeUSB(1); // 1表示proUSB if (st读卡器 !== 0) { console.log('设备打开失败'); return '打开端口失败'; } // 调用注销卡片函数(模拟原CardErase,原是StringBuilder,这里用字符串模拟) const CardNostr = ''; // 实际使用时需传入正确的卡号缓冲区 const st = ProRFLV102024Dll.CardErase ? ProRFLV102024Dll.CardErase(1, parseInt(酒店标识), CardNostr) : -1; if (st !== 0) { s += ':注销失败' + st.toString(); console.log(`注销失败:${st}`); } else { s += ':成功'; } // 关闭USB(原代码未显式关闭,这里补充最佳实践) ProRFLV102024Dll.CloseUSB && ProRFLV102024Dll.CloseUSB(); return s; } // 入住发卡(对应原checkingin) checkingin(obj: NameValueCollection): string { let s = '酒店入住发卡'; const param = obj['param'] || ''; // 解析协议参数 const clApp = new Cl_CyberWinAPPProtocolPackage(); clApp.formatString(param); const 锁号服务器 = clApp.get('lockno'); const 酒店标识 = clApp.get('hotelsign'); const 退房时间服务器 = clApp.get('checkingouttime'); // 校验锁号长度 if (锁号服务器.length < 6) { console.log(`锁号长度错误=${锁号服务器}`); return s + ':锁号长度错误'; } // 初始化USB设备 const st = this.initializeUSB(1); if (st !== 0) { console.log('设备打开失败'); return '打开端口失败'; } // 构造参数 const 开房时间s = new Date().toLocaleString('zh-CN', { year: '2-digit', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', }).replace(/[^\d]/g, ''); // 模拟yyMMddHHmmss格式 const 反锁标志 = 1; const dai = 1; // 对应原dai变量 // 调用发卡函数(模拟原GuestCard_原始) const sa1_V10字符串 = ''; // 实际使用时需传入缓冲区 const stCard = ProRFLV102024Dll.GuestCard_原始 ? ProRFLV102024Dll.GuestCard_原始( 1, parseInt(酒店标识), 0, dai, 反锁标志, 0, 开房时间s, 退房时间服务器, 锁号服务器, sa1_V10字符串 ) : -1; if (stCard !== 0) { s += '调用发卡函数失败'; console.log(`调用发卡函数失败:${stCard}`); } else { s += `制卡成功V2024${锁号服务器}`; } // 关闭USB ProRFLV102024Dll.CloseUSB && ProRFLV102024Dll.CloseUSB(); return s; } // 读取酒店标识(对应原getsign) getsign(obj: NameValueCollection): string { // 调用读卡函数(模拟原rdCard_v10) if (!this.rdCard_v10()) { return '读卡失败'; } // 调用解析函数获取酒店标识 const 酒店标识 = CyberWin_LocakAPP.未来之窗_美萍_getsign(APP.bufCard_v10); return 酒店标识; } // 读卡v10(对应原rdCard_v10) private rdCard_v10(): boolean { if (!ProRFLV102024Dll.ReadCard_v10) { console.log('读卡函数未找到'); return false; } // 调用DLL的ReadCard_v10(ref表示传递缓冲区指针) APP.st = ProRFLV102024Dll.ReadCard_v10(1, ref.ref(APP.bufCard_v10)); if (APP.st !== 0) { console.log(`读卡失败${APP.st}`); return false; } return true; } // 写日志(对应原write_log) public static write_log(capturetype: string, type: string, s: string): void { // 构造日志路径(Node.js中替换Application.StartupPath为__dirname) const dateStr = new Date().toLocaleDateString('zh-CN').replace(/\//g, '-'); const logPath = path.join(__dirname, 'log', capturetype, dateStr); // 创建目录(同步) if (!fs.existsSync(logPath)) { fs.mkdirSync(logPath, { recursive: true }); } // 日志文件路径 const 文件路径 = path.join(logPath, `${type}_log.log`); // 写入日志(追加模式) const logContent = `==============================\n${new Date().toLocaleString()}<<<<<<<<<<<<<<<<<<<<<<<<<<\n${s}\n\n`; fs.appendFileSync(文件路径, logContent, 'utf8'); } } // -------------------------- 辅助类(对应原CyberWin_LocakAPP) -------------------------- class CyberWin_LocakAPP { // 解析酒店标识(对应原未来之窗_美萍_getsign) public static 未来之窗_美萍_getsign(bufCard: Buffer): string { const 未来之窗 = bufCard.toString('ascii'); // 对应Encoding.ASCII.GetString // 检查是否为空白卡 if (CyberWin_LocakAPP.Copy(bufCard, 25, 8) === 'FFFFFFFF') { console.log('此卡是空白卡,请换一张能开门的卡'); return '此卡是空白卡,请换一张能开门的卡'; } // 解析酒店标识(保留原逻辑) const s = CyberWin_LocakAPP.Copy(bufCard, 11, 4); let i = parseInt(s, 16) % 16384; const s2 = CyberWin_LocakAPP.Copy(bufCard, 9, 2); i = i + (parseInt(s, 16) * 65536); const i2 = parseInt(CyberWin_LocakAPP.Copy(bufCard, 9, 2), 16) * 65536 + parseInt(CyberWin_LocakAPP.Copy(bufCard, 11, 4), 16) % 16383; const 酒店标识 = i2.toString(); return 酒店标识; } // 字符串截取(对应原Copy方法) public static Copy(字符串: Buffer, 开始: number, 长度: number): string { const 完整 = 字符串.toString('ascii'); 开始 = 开始 < 1 ? 1 : 开始; return 完整.substring(开始 - 1, 开始 - 1 + 长度); } } // -------------------------- 测试代码(可选) -------------------------- // 模拟调用示例 const app = new APP(); // 测试状态检测 console.log(app.status({})); // 测试退房(模拟参数) console.log(app.checkingout({ param: 'hotelsign=123456' })); // 测试入住(模拟参数) console.log(app.checkingin({ param: 'hotelsign=123456&lockno=123456&checkingouttime=2501010000' })); // 导出类供其他模块使用 export { APP, CyberWin_LocakAPP };

三、关键代码解释

  1. 类型模拟:用NameValueCollection类型模拟 C# 的同名集合,用
  2. DLL 调用:使用ffi-napi库加载 Windows DLL,模拟原 C# 的DllImport,注意函数签名需要与 DLL 实际导出的一致(代码中是模拟,需根据实际 DLL 调整)。
  3. 核心业务逻辑:保留原有的入住发卡、退房注销、读卡、写日志等逻辑,适配 TypeScript 的语法和 Node.js 的 API(如fs模块写日志、path模块处理路径)。
  4. 硬件交互适配:原 C# 的byte[]缓冲区用 Node.js 的Buffer替代,StringBuilder用字符串模拟(实际使用时需用ref库处理指针)。

四、编译与运行

  1. 创建tsconfig.json文件(TypeScript 配置):

json

{ "compilerOptions": { "target": "ES6", "module": "CommonJS", "outDir": "./dist", "strict": true, "esModuleInterop": true }, "include": ["*.ts"] }
  1. 编译 TypeScript 代码:

bash

运行

npx tsc
  1. 运行编译后的 JS 代码:

bash

运行

node dist/hotelDoorLock.js

阿雪技术观

在科技发展浪潮中,我们不妨积极投身技术共享。不满足于做受益者,更要主动担当贡献者。无论是分享代码、撰写技术博客,还是参与开源项目维护改进,每一个微小举动都可能蕴含推动技术进步的巨大能量。东方仙盟是汇聚力量的天地,我们携手在此探索硅基生命,为科技进步添砖加瓦。

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.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/1 11:27:32

.NET进阶——深入理解线程(1)同步异步与单线程多线程的区分

今天我们来详细介绍一下多线程&#xff0c;多线程是.NET中非常重要的知识点&#xff0c;需要完全掌握。 一、什么是多线程&#xff1f; 在了解线程之前&#xff0c;我们需要知道什么是进程&#xff0c;所谓进程&#xff0c;就是指操作系统中运行的程序&#xff0c;比如我们自己…

作者头像 李华
网站建设 2026/4/1 10:05:39

食品工业生产线的安全防线:X光异物检测机的作用与挑选要点

在食品工业的生产线上&#xff0c;食品安全一直都是绝不能突破的红线&#xff0c;在此之中&#xff0c;异物混入是致使产品被召回以及品牌声誉遭受损害的主要风险之一&#xff0c;X光异物检测机作为一种效率高的非破坏性检测技术&#xff0c;已然变成保障最终产品安全的关键防线…

作者头像 李华
网站建设 2026/3/30 4:32:39

LobeChat情人节品牌互动文案

LobeChat&#xff1a;当开源遇见浪漫&#xff0c;技术如何编织情感体验 在生成式 AI 如潮水般涌入日常生活的今天&#xff0c;我们早已不再惊讶于一个模型能写出诗、编出代码&#xff0c;甚至模仿哲学家对话。真正让人驻足的&#xff0c;是那些让技术“有温度”的瞬间——比如&…

作者头像 李华
网站建设 2026/3/30 17:26:45

Motrix浏览器扩展终极指南:三步实现专业下载管理革命

Motrix浏览器扩展终极指南&#xff1a;三步实现专业下载管理革命 【免费下载链接】motrix-webextension A browser extension for the Motrix Download Manager 项目地址: https://gitcode.com/gh_mirrors/mo/motrix-webextension 你是否曾经因为浏览器下载速度慢如蜗牛…

作者头像 李华
网站建设 2026/3/27 14:20:27

JS利用分块技术实现100万文件上传的解决方案?

专业版技术方案&#xff1a;大文件传输系统开发实录 一、需求分析与技术选型 作为内蒙古某软件公司前端负责人&#xff0c;针对20G大文件传输需求&#xff0c;我进行了以下技术评估&#xff1a; 核心痛点&#xff1a; 现有方案&#xff08;WebUploader&#xff09;已停更&…

作者头像 李华
网站建设 2026/3/31 20:27:24

LobeChat SEO元描述自动生成

LobeChat&#xff1a;构建私有化 AI 对话门户的现代技术实践 在大语言模型&#xff08;LLM&#xff09;能力不断突破的今天&#xff0c;我们早已不再质疑“AI 能不能回答问题”&#xff0c;而是更关心&#xff1a;“用户能不能顺畅地用上它&#xff1f;” 尽管 OpenAI、Anthr…

作者头像 李华