酒店外设驱动的一致性门禁:每个驱动自带 simulator 与 CTS¶
酒店 PMS 要直控的外设五花八门:身份证读卡器、发卡器、门锁网关、小票打印机、RF 灯光控制器、UPS。每家厂商的 SDK/协议都不一样,而研发团队手里往往只有一两台真机。如果驱动的合入质量靠"到店联调时再说",现场交付就会变成灾难。
我们的做法是把「每个驱动必须自带模拟器 + 一致性测试套件(CTS)」设为 CI 硬门禁。
驱动抽象¶
所有外设收敛到一个小接口:
type Driver interface {
Open(cfg Config) error
Close() error
Health() Status
Read(req Request) (Response, error)
Write(req Request) (Response, error)
Describe() string
}
type FaultInjector interface {
SetFault(f Fault) // 无卡/超时/离线/写失败/忙
}
要点有三个:
- 用 Read/Write + Op 语义而不是每设备一套方法,上层业务模块才能对驱动做统一编排;
- FaultInjector 是一等公民:离线、超时、无卡、写失败都要能按需注入;
- Describe() 输出契约摘要,设备监控页直接渲染,现场排障不用翻文档。
CTS:同一套用例跑模拟器与真机¶
每个驱动交付时附带 CTS(Conformance Test Suite):开设备、健康检查、正常读写、各类故障注入下的错误语义、并发访问。CTS 先在模拟器上跑绿;到了有真机的环境(HIL 台位),同一套用例指向真实驱动再跑一遍,结果记入台账。
这带来两个直接收益:
- CI 里永远有完整的外设测试,不依赖真机在不在工位上;
- 真机替换模拟器时业务代码零改动——模拟器必须实现与真机相同的错误语义(
ErrNoCard、ErrTimeout、ErrOffline……),这是抽象质量的试金石。
实战里最有价值的两类用例¶
错误语义归一。业务层不该理解每家 SDK 的错误码,驱动层把"设备未连接""无卡在位""读写超时"归一成统一错误,再由业务层转成前台话术。这类用例在模拟器上注入故障即可覆盖,把现场"设备连不上"类工单的定位时间从小时级压到分钟级。
操作留痕与幂等。发卡、销卡这类动作必须有操作历史(卡操作时间线),且重试不能重复写卡。用例会故意重复调用并断言记录数——这类"看起来不可能出错"的幂等细节,恰恰是最常被漏测的。
门禁的边界¶
门禁不能替代真机验证:协议细节(波特率、指令时序、固件差异)只有真机能暴露。所以流程是「模拟器 CTS 全绿才允许合入,真机 HIL 用例补台账后才算交付」。工具链管住可以管住的,剩下的用流程清单兜底。