java硬件对接经验总结

1 篇文章 0 订阅
1 篇文章 0 订阅

一、问题描述

项目现场需要对接硬件,机缘巧合下派我出差对接。暂定要对接读卡器通道门两个硬件。

难点描述:
1、业务流程不清楚、数据流转来源和去向未确定,不知从何下手;
2、时间和学习成本高;
3、基本知识不懂,和硬件厂家沟通比较慢;
4、部署调试、数据交互方案设计;
总结一下就是之前没有过硬件对接经验现场业务流程不熟悉

二、解决思路

1、厂家有提供他们的说明文档,先把文档大致看下,然后再和厂家沟通;
2、业务和数据流转和现场的负责人沟通;
总结一下就是:不耻下问,明白自己要做什么之后再动手去做。

3、业务流程:读卡器通过usb口连接Windows电脑,读卡器刷卡,硬件后端(下面简称hardware)代码读取卡片的芯片数据,然后推送给A系统,A系统把芯片数据推送给前端展示。即:读卡器刷卡,前端页面展示芯片数据。

4、为方便大家理解业务,可以认为是运动员和工牌的证件写入和证件核验两部分。证件写入:运动员和工牌绑定;证件核验,在证件写入之后,检验芯片关联出的用户数据是否正确。

三、解决方案

1、人证读卡器

(1)通过串口调试工具获取到芯片数据

先用工具调试通过,学习一下指令怎么处理。
①读卡器数据交互格式:
在这里插入图片描述

②读卡器需要先设置读卡模式,就以这个指令来调试吧
在这里插入图片描述
解释:
0x代表是16进制,不用管,之后取后两位即可。如0x03,则取03.
校验字可以通过网页计算:在线异或计算
在这里插入图片描述
③使用工具发送指令:

在这里插入图片描述
使用的工具是,友善串口调试工具。红框设置连接参数;蓝框发送指令;黄框是响应数据

(2)代码开发及交互方案演进

具体代码就不再多描述了,百度一下java串口编程即可。

读卡器是一问一答模式,即发送读卡指令给读卡器,读卡器才会响应给你,即被动模式
①hardware获取芯片:
暂定用定时任务,发送读卡指令给读卡器,读取到芯片数据则推送到mq,让A系统接收芯片数据推送给前端;
②重复推送问题:
假设定时任务1S一次,一张卡放在读卡器上10S,则hardware会读取十次芯片数据推送mq,A系统接收到mq的数据就推送给前端。从效果来说,前端没有明显的闪动,但是从交互来说,需要优化一下。即,卡放在读卡器上面10S,在此期间hardware只发送一次数据到mq里。个人的方案是,读取一个芯片数据,将该芯片数据作为key存入redis,并给一个过期时间(略大于定时任务间隔,尽量减少延迟),下次读取数据校验redis是否有该key,有的话则不推送至mq。
③hardware兼容读卡器模式:
后续和读卡器厂家沟通,读卡器升级后支持主动模式(有芯片数据,读卡器主动推送),考虑到老系统的兼容问题,即hardware要支持读卡器的主动和被动两种模式。
④数据展示和读卡器要一一对应:
前面说到,读卡器通过usb连接Windows,hardware是读取读卡器的芯片数据。则Windows、读卡器、hardware是一一对应关系,即hardware部署在有读卡器的Windows上,读卡器-A的数据只发送给hardware-A,然后在Windows-A页面展示;但是问题在于,A系统,也就是接收mq芯片数据的服务,是部署在Linux上的,也就是说A系统无法区分芯片数据是哪个读卡器读的,要推送至哪个前端页面。解决方案:hardware推送至mq的芯片数据,携带一个本机ip字段;Windows操作页面和A系统建立socket通道时,前端传参本机ip,A系统通过map保存socket通道,key为ip,value为socket。这样就能区分该芯片数据,是哪个读卡器推送,要推送至哪个前端页面,不会让数据交叉。
⑤离线模式回显芯片
客户后来提出需求:在局域网断网的情况下,刷卡的时候,页面也要显示芯片数据(只针对芯片回显,其他的比如接口请求则不做要求)。解决方案:前端新建一个socket通道连接本机hardware。一共建立两个socket通道,一个连服务器的A系统,用于在线流程;一个连本地Windows的hardware,用于离线流程(分两个socket是因为不想让hardware沾染A系统的业务代码,各司其职就好)。则hardware需要定时检测相关服务器是否可达,具体代码就不描述了,根据服务器是否可达走相关代码逻辑。
概括一下流程:
在线流程:读卡器 -> hardware -> rabbitmq -> A系统 -> netty -> 前端
离线流程:读卡器 -> hardware -> netty -> 前端
自动获取串口名称
代码开发到这程度,功能是实现了,但是还有优化的地方。比如hardware是通过配置文件读取串口号的,同一个读卡器用在不同Windows电脑,串口号可能不一样,可能Windows-A是COM3,到了Windows-B就是COM7,所以如果hardware能自动获取串口号,那么也节省了部署的成本。
在这里插入图片描述
截图的是Windows连接显示屏的串口,和读卡器的一样,都是COM开头。如果能获取前面的中文,用来区分那就完美了,详情参考:
Java获取串口名称
JAVA代码执行CMD命令
⑦Windows部署jar脚本(简单):
启动脚本start.bat:

@echo off
REM 取消 快速编辑模式,防止程序卡着
reg add HKEY_CURRENT_USER\Console /v QuickEdit /t REG_DWORD /d 00000000 /f
REM 指明控制台编码UTF-8
chcp 65001
REM 命令行窗口的名字
title "hardware"
REM jar包存放在Windows的绝对位置,指明外置配置文件绝对路径
java -Dfile.encoding=utf-8 -jar D:\hardware.jar --spring.cloud.bootstrap.location=D:\bootstrap.yml
pause

停止脚本stop.bat:

@echo off
REM hardware的运行port
set port=8080
for /f "tokens=1-5" %%i in ('netstat -ano^|findstr ":%port%"') do (
 echo kill the process %%m who use the port 
 taskkill /pid %%m -t -f
 goto q
)
:q

前端部署:
Windows部署前端

2、通道门

通道门对接就大致概括下吧,通道门的作用本来我以为是最终入场的时候,运动员通过通道门,电脑屏幕显示该人员的信息。但是实际不是这么回事,通道门暂时是用来做“证件校验”的另一个方法。也就是说,人员的证件写入,芯片数据来源只有读卡器;但是证件核验,有读卡器和通道门两种。

(1)通道门对接方式是厂家的dll动态库

相关资料请百度JNA或JNI,hardware加载动态度用的是绝对路径。通道门也是问答模式,和人证读卡器的区别在于,通道门有厂家提供的dll动态库,直接调用相关函数即可。dll动态度处理代码:

public static String loadLib(String libName) {
        String resourcePath = "/" + libName;
        String folderName = System.getProperty("user.dir") + "/dll/";
        File folder = new File(folderName);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        File libFile = new File(folder, libName);
        if (libFile.exists()) {
            System.load(libFile.getAbsolutePath());
        } else {
            try {
                InputStream in = ChannelDoorConfig.class.getResourceAsStream(resourcePath);
                FileUtils.copyInputStreamToFile(in, libFile);
                in.close();
                System.load(libFile.getAbsolutePath());
            } catch (Exception e) {
                throw new RuntimeException("Failed to load required lib", e);
            }
        }
        String libAbsolutePath = libFile.getAbsolutePath();
        log.info("{} absolute-path -> {}",libName,libAbsolutePath);
        return libAbsolutePath;
}

(2)双网卡配置问题

在三-1-(2)-④提到过,芯片数据要能区分是哪个Windows的数据,所以hardware会获取本机ip。但是由于通道门是单独的网段,所以本机Windows配置双ipv4网段后,代码没办法区分内网ip和通道门ip。
在这里插入图片描述

解决方案:通道门采用的是网口对接,所以配置文件会有通道门的ip,可以截取前三段的ip段落,然后和获取的ip进行比较。

3、车证读卡器

(1)流程

车证读卡器对接的业务流程和人证的一摸一样,也分证件写入和证件核验等流程,不再多说。

(2)SDK对接

相关概念及流程:
①盘点,读卡器搜查芯片数据的过程就叫盘点。
②盘点期间,只要芯片在范围内,就会一直向hardware推送数据
③数据交互:调用dll的盘点函数(可设置盘点时长),程序实现回调函数,盘点到芯片数据就调用回调函数。即一次盘点,可能回调函数多次被调用。

dll盘点函数之一,EPC芯片盘点函数:
EPC_SEARCH(INT32U handle, EPC_INV_PARAM_SIMPLE *param, RFID_PACKET_CALLBACK_FUNCTION func)
④JNA实现dll结构体,
即定义一个实体类,继承com.sun.jna.Structure。根据dll中的结构体定义相关字段即可,注意要定义取值顺序:
在这里插入图片描述
⑤JNA实现dll回调函数,以及Callback的问题
自定义接口,继承com.sun.jna.Callback接口,自己定义方法即可,入参要和dll函数保持一致,让impl类实现回调逻辑。注意回调函数入参不支持数组,要使用Pointer类,否则报错
在这里插入图片描述
在这里插入图片描述
debug查看原因:
com.sun.jna.CallbackReference类的构造方法:
在这里插入图片描述
com.sun.jna.CallbackReference类的isAllowableNativeType方法:

private static boolean isAllowableNativeType(Class cls) {
        return cls == Void.TYPE || cls == Void.class 
		|| cls == Boolean.TYPE || cls == Boolean.class 
		|| cls == Byte.TYPE || cls == Byte.class 
		|| cls == Short.TYPE || cls == Short.class 
		|| cls == Character.TYPE || cls == Character.class 
		|| cls == Integer.TYPE || cls == Integer.class 
		|| cls == Long.TYPE || cls == Long.class 
		|| cls == Float.TYPE || cls == Float.class 
		|| cls == Double.TYPE || cls == Double.class 
		|| ByValue.class.isAssignableFrom(cls) && Structure.class.isAssignableFrom(cls) || Pointer.class.isAssignableFrom(cls);
}

解决方法:将数组类型换成Pointer类型即可,调用getByteArray方法获取byte[]数据。

四、总结

1、不怕任务多和难,只要有相关文档和厂家人员沟通,什么都不是事;
2、对于从未接触的领域,一定要不耻下问,不耻下问,不耻下问;
3、这次对接硬件,也是想着多接触一些技术,见见世面;

萌新发言,不喜勿喷,欢迎大佬指出不当之处!

  • 31
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值