JAVA读取USB扫描枪

2023-11-17

利用JAVA程序获取扫描枪的数据

package barcode;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
*
*条形码数据缓存区
* 扫描服务获取到扫描枪输入的数据后将数据加入此缓存区
* 消费者线程会从此缓冲区中获取数据并执行数据的保存操作
* 数据的保存可以有多种实现
* 此缓冲区的意义在于不要因为数据保存出错或速度慢而影响扫描服务工作
*
* @author ysc
*/
public class BarcodeBuffer {
//阻塞队列
private static BlockingQueue queue=new LinkedBlockingQueue();
/**
* 生产一条数据,此方法由BarcodeProducter调用
* @param barcode 
*/
public static void product(String barcode){
queue.add(barcode);
}
/**
* 消费一条数据,如果队列中没有数据,此方法阻塞等待数据的到来,此方法由BarcodeConsumer调用
* @return 
*/
public static String consume() throws InterruptedException{
return queue.take();
}
}
package barcode;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
*此消费者线程会从此缓冲区中获取数据并执行数据的保存操作
* 数据的保存调用BarcodeSaveService接口定义的save方法
*
* @author ysc
*/
public class BarcodeConsumer {
//消费者线程
private Thread thread;
//数据保存服务(可有多个)
private List barcodeSaveServices=new ArrayList();
private boolean quit;
/**
* 停止消费者线程
* 此方法在tomcat关闭的时候被调用
*/
public void stopConsume(){
if(thread!=null){
thread.interrupt();
//释放资源
for(BarcodeSaveService barcodeSaveService : barcodeSaveServices){
barcodeSaveService.finish();
}
}
}
/**
* 启动消费者线程
* 此方法在tomcat启动的时候被调用
*/
public void startConsume(){
//防止重复启动
if(thread!=null && thread.isAlive()){
return;
}
System.out.println("条形码消费者线程启动");
System.out.println("注册条形码保存服务");
registerBarcodeSaveServcie();
thread=new Thread(){
@Override
public void run(){
while(!quit){
try{
//当缓冲区没有数据的时候,此方法会阻塞
String barcode=BarcodeBuffer.consume();
if(barcodeSaveServices.isEmpty()){
System.out.println("没有注册任何条形码保存服务");
}
for(BarcodeSaveService barcodeSaveService : barcodeSaveServices){
barcodeSaveService.save(barcode);
}
}catch(InterruptedException e){
quit=true;
}
}
System.out.println("条形码消费者线程退出");
}
};
thread.setName("consumer");
thread.start();
}
/**
* 消费者线程从缓冲区获取到数据后需要调用保存服务对数据进行处理
*/
private void registerBarcodeSaveServcie() {
List classes=getBarcodeSaveServcieImplClasses();
System.out.println("条形码保存服务实现数目有:"+classes.size());
for(String clazz : classes){
try{
BarcodeSaveService barcodeSaveService=(BarcodeSaveService)Class.forName(clazz).newInstance();
barcodeSaveServices.add(barcodeSaveService);
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
* 从类路径下的barcode.save.services文件中获取保存服务类名
* @return 多个保存服务实现类名
*/
private List getBarcodeSaveServcieImplClasses() {
List result=new ArrayList();
BufferedReader reader = null;
InputStream in=null;
try {
//放在WEB-INF/classes下的"barcode.save.services"会覆盖jar包中的"barcode.save.services"
URL url=Thread.currentThread().getContextClassLoader().getResource("barcode.save.services");
System.out.println("url:"+url.getPath());
in=url.openStream();
if(in==null){
System.out.println("没有在类路径下找到条码保存服务配置文件:barcode.save.services");
return result;
}
reader = new BufferedReader(new InputStreamReader(in,"utf-8"));
String line=reader.readLine();
while(line!=null){
//忽略空行和以#号开始的注释行
if(!"".equals(line.trim()) && !line.trim().startsWith("#")){
result.add(line);
}
line=reader.readLine();
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(reader!=null){
reader.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
try {
if(in!=null){
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
}
package barcode;
import java.util.HashMap;
import java.util.Map;/**
*扫码枪模拟的键盘按钮事件监听(0-9键和回车键)
* 关键算法:条形码扫描器在很短的时间内输入了至少 barcodeMinLength 个字符以上信息,并且以“回车”作为结束字符,并且一次扫描要在 maxScanTime 毫秒内完成
* 字符数及扫描时间可根据具体情况设置
* @author ysc
*/
public class BarcodeKeyboardListener{
//条形码数据缓充区
private StringBuilder barcode;
//扫描开始时间
private long start;
private Map keyToLetter=new HashMap();
//一次扫描的最长时间
private static int maxScanTime=300;
//条形码的最短长度
private static int barcodeMinLength=6;
/**
* 初始键盘代码和字母的对于关系
*/
public BarcodeKeyboardListener(){
keyToLetter.put(48,0);
keyToLetter.put(49,1);
keyToLetter.put(50,2);
keyToLetter.put(51,3);
keyToLetter.put(52,4);
keyToLetter.put(53,5);
keyToLetter.put(54,6);
keyToLetter.put(55,7);
keyToLetter.put(56,8);
keyToLetter.put(57,9);
}
/**
* 此方法响应扫描枪事件
* @param keyCode 
*/
public void onKey(int keyCode) {
//获取输入的是那个数字
Integer letter=keyToLetter.get(keyCode);
if(barcode==null){
//开始进入扫描状态
barcode=new StringBuilder();
//记录开始扫描时间
start=System.currentTimeMillis();
}
//需要判断时间
long cost=System.currentTimeMillis()-start;
if(cost > maxScanTime){
//开始进入扫描状态
barcode=new StringBuilder();
//记录开始扫描时间
start=System.currentTimeMillis();
}
//数字键0-9
if (keyCode >= 48 && keyCode <= 57) {
barcode.append(letter);
}
//回车键
if (keyCode == 13) {
//条形码扫描器在很短的时间内输入了至少 barcodeMinLength 个字符以上信息,并且以“回车”作为结束字符
//进入这个方法表示是“回车”
//那么判断回车之前输入的字符数,至少 barcodeMinLength 个字符
//并且一次扫描要在 maxScanTime 毫秒内完成
if(barcode.length() >= barcodeMinLength && cost < maxScanTime){
cost=System.currentTimeMillis()-start;
System.out.println("耗时:"+cost);
System.out.println(barcode.toString());
//将数据加入缓存阻塞队列
BarcodeBuffer.product(barcode.toString());
}
//清空原来的缓冲区
barcode=new StringBuilder();
}
}
}
package barcode;
/**
*启动和关闭条码枪扫描线程
* @author ysc
*/
public class BarcodeProducter {
private boolean quit;
private Thread thread;
private ScanBarcodeService scanBarcodeService;
public BarcodeProducter(){
scanBarcodeService=new ScanBarcodeService();
}
/**
* 启动生产者线程
* 此方法在tomcat启动的时候被调用
*/
public void startProduct() { 
//防止重复启动
if(thread!=null && thread.isAlive()){
return;
}
System.out.println("启动条形码生产者...");
//启动一个线程用于在tomcat关闭的时候卸载键盘钩子
thread=new Thread() {
@Override
public void run() {
System.out.println("条码枪扫描线程启动");
while (!quit) {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (Exception e) {
quit=true;
}
}
scanBarcodeService.stopScanBarcodeService();
System.out.println("条码枪扫描线程退出");
System.exit(0);
}
};
thread.start();
new Thread() {
@Override
public void run() {
scanBarcodeService.startScanBarcodeService();
}
}.start();
}
/**
* 关闭生产者线程
* 此方法在tomcat关闭的时候被调用
*/
public void stopProduct(){
if(thread!=null){
thread.interrupt();
System.out.println("停止条形码生产者...");
}
}
}
package barcode;
/**
*条形码保存服务
* @author ysc
*/
public interface BarcodeSaveService {
/**
* 保存条形码
* @param barcode 
*/
public void save(String barcode);
/**
* 在这里释放资源,如数据库连接,关闭文件,关闭网络连接等
*/
public void finish();
}
package barcode;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
/**
*把条形码数据保存到文件的实现(参考实现)
* 可根据自己的需求做一个自定义实现
* 只要实现BarcodeSaveService接口的方法
* 并在barcode.save.services文件中指定使用的实现类即可
* @author ysc
*/
public class BarcodeSaveToFile implements BarcodeSaveService{
private Writer writer;
/**
* 保存到文件
* @param barcode 
*/
@Override
public void save(String barcode) {
try {
if(writer==null){
System.out.println("打开文件");
writer=new OutputStreamWriter(new FileOutputStream("d:/barcode.txt",true));
}
writer.write(barcode+"n");
writer.flush();
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* 关闭文件
*/
@Override
public void finish() {
System.out.println("关闭文件");
try {
if(writer!=null){
writer.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
package barcode;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
*扫描服务监听,把此监听器配置到web.xml中
*

barcode.ScanBarcodeListener

* @author ysc
*/
public class ScanBarcodeListener implements ServletContextListener{
private BarcodeProducter barcodeProducter;
private BarcodeConsumer barcodeConsumer;
/**
* tomcat启动
* @param sce 
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
barcodeProducter=new BarcodeProducter();
barcodeProducter.startProduct();
barcodeConsumer=new BarcodeConsumer();
barcodeConsumer.startConsume();
}
/**
* tomcat关闭
* @param sce 
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
barcodeProducter.stopProduct();
barcodeConsumer.stopConsume();
}
/**
* 可以在此文件中运行测试
* @param args
* @throws Exception 
*/
public static void main(String[] args) throws Exception {
BarcodeProducter producter=new BarcodeProducter();
BarcodeConsumer consumer=new BarcodeConsumer();
producter.startProduct();
consumer.startConsume();
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入 '' 退出程序");
String line=reader.readLine();
while(line!=null){
if("exit".equals(line)){
producter.stopProduct();
consumer.stopConsume();
System.exit(0);
}
line=reader.readLine();
}
}
}
package barcode;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinDef.LRESULT;
import com.sun.jna.platform.win32.WinDef.WPARAM;
import com.sun.jna.platform.win32.WinUser.HHOOK;
import com.sun.jna.platform.win32.WinUser.KBDLLHOOKSTRUCT;
import com.sun.jna.platform.win32.WinUser.LowLevelKeyboardProc;
import com.sun.jna.platform.win32.WinUser.MSG;
/**
*
* 利用HOOK技术监听键盘事件(扫描枪就相当于只有0-9和回车键的键盘)
* 低级键盘事件监听
*
*/
public class ScanBarcodeService {
private HHOOK hhkKeyBoard;
private final User32 lib = User32.INSTANCE;
/**
* 停止扫码枪服务
*/
public void stopScanBarcodeService() { 
//卸载键盘钩子
lib.UnhookWindowsHookEx(hhkKeyBoard);
}
/**
* 启动扫码枪服务
*/
public void startScanBarcodeService() {
//键盘事件监听
final BarcodeKeyboardListener listener=new BarcodeKeyboardListener();
//回调
LowLevelKeyboardProc keyboardHook = new LowLevelKeyboardProc() {
@Override
public LRESULT callback(int nCode, WPARAM wParam,
KBDLLHOOKSTRUCT info) {
if (nCode >= 0) {
switch (wParam.intValue()) {
case WinUser.WM_KEYUP:
int keyCode = info.vkCode;
//监听数字键0-9
if (keyCode >= 48 && keyCode <= 57) {
//交个监听器处理
listener.onKey(keyCode);
}
//监听回车键
if (keyCode == 13) {
//交个监听器处理
listener.onKey(keyCode);
}
break;
}
}
//交个下一个钩子
return lib.CallNextHookEx(hhkKeyBoard, nCode, wParam,
info.getPointer());
}
};
HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle(null);
//将上面定义的 回调方法 安装到挂钩链中对系统的底层的键盘输入事件进行监控
hhkKeyBoard = lib.SetWindowsHookEx(WinUser.WH_KEYBOARD_LL, keyboardHook, hMod, 0);
// 处理消息(线程阻塞)
int result;
MSG msg = new MSG();
while ((result = lib.GetMessage(msg, null, 0, 0)) != 0) {
if (result == -1) {
System.err.println("error in get message");
break;
} else {
lib.TranslateMessage(msg);
lib.DispatchMessage(msg);
}
}
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA读取USB扫描枪 的相关文章

随机推荐

  • C++ 读写二进制文件

    描述 C 来读取二进制文件 二进制文件的格式可以多种多样 比如dat index等 还可以是自行定义的格式 C 来写二进制文件 一 读二进制文件 结构体定义及头文件 include
  • 38 匹配字符串——findall()方法

    文章目录 语法 案例 语法 findall 方法用于在整个字符串中搜索所有符合正则表达式的字符串 并以列表的形式返回 如果匹配成功 则返回包含匹配结构的列表 否则返回空列表 findall 方法的语法格式如下 re findall patt
  • css实现图片叠加的几种思路(记录笔记)

    背景 实现点击事件 触发原图的img透明度降低 成为透明背景 并且加一个不透明的原图 可以用于加水印 一个div覆盖几个样式 使用的是vue vue cli搭建项目 几种思路 1 切换背景样式 设置一个key 当div元素触发点击事件 di
  • (SUB)选择排序时间、空间复杂度

    基本思想 将一组数据分为两部分 前面是已排序部分 后面是未排序部分 初始状态可认为位置 0 为已排序部分 数组下标从0开始 其余为未排序部分 每一次都从未排序部分选择一个最小元素放在已排序部分的末尾 然后已排序部分增加一个元素 未排序部分减
  • 腾讯云微计算实践:从Serverless说起,谈谈边缘计算的未来

    欢迎大家前往云 社区 获取更多腾讯海量技术实践干货哦 作者 黄文俊 腾讯云高级产品经理 曾经历过企业级存储 企业级容器平台等产品的架构与开发 对容器 微服务 无服务器 DevOps等都有浓厚兴趣 由 腾讯云serverless团队 发布在
  • selenium版本不匹配

    window10解决selenium版本不匹配问题 如运行出现以下错误 File C Python37 lib site packages selenium webdriver chrome webdriver py line 73 in
  • 【Google测试之道】 第二章 软件测试开发工程师

  • 方法简单手把手教你,空闲时间在家剪辑视频,一天收入300多

    做一个视频剪辑号 不开玩笑 认真做一个月真的能做到1w 有不少人都不知道 我们平时在手机上刷到的横屏视频都是可以赚钱的 而且也可以不用露脸拍摄视频 下班在家也可以赚取一份额外收入 很多人就会有疑问了 不露脸 不拍摄视频怎么赚钱 其实做视频剪
  • 【netty】netty HashedWheelTimer 延时队列

    1 概述 想要研究这个是因为 Flink Flink 写入 Clickhouse 大对象直接进入老年代 导致OOM 遇到了这个问题 在这个问题中 我将时间轮改小了 时间轮 512改成16个 Netty中提供的HashedWheelTimer
  • 代码实现对selenium的驱动器WebDrive的配置

    1 条件 1 使用的浏览器是Microsoft Edge 2 简述过程 代码实现 1 pip 安装 2 下载 3 解压 4 运行 3 发现一个报错 1 原因 在给出代码之前 我发现一个报错 很离谱 且听笔者慢慢细说 首先 安装了seleni
  • 编译 - Make 命令教程 以及Makefile - 学习/实践

    1 应用场景 主要用于学习和使用make命令进行软件编译安装 2 学习 操作 1 文档阅读 Make 命令教程 阮一峰的网络日志 Make GNU Project Free Software Foundation 第21讲 如何使用脚本语言
  • 关系表的构成要素主键_数据库:关系型数据库的基本术语有哪些?

    一 关系 relation 关系就是二维表 二维表的名字就是关系的名字 二 属性 attribute 二维表中的每个列就称为一个属性 或叫字段 每个属性有一个名字 称为属性名 三 值域 domain 二维表中属性的取值范围称为值域 四 元组
  • 分治法篇:卷一:最简单的分治法应用例子

    2023年4月25日 周二早上 我想从最简单的分治法应用例子开始 而不是从经典的例子开始 用分治法求解数组中的最大值 纯享版 include
  • 《基于 Vue 组件库 的 Webpack5 配置》6.将字体库和图片等静态资料,编译后打包至指定文件夹

    参考 Rule generator filename package json 的配置如下 module exports module test png svg jpg jpeg gif i type asset resource gene
  • 【C语言-进阶】指针进阶

    1 字符指针 字符指针的基本使用略了 把常量字符串 abcdef 的首元素地址放在字符型指针p中 p中存储的就是字符a的地址 而因为他们是在内存中连续存储的 且字符串末尾有自动添加到 0 所以可以用printf形式打印出来 这时如果用指针p
  • C++复习笔记--STL的常见遍历、查找和排序算法的使用

    目录 1 STL常用算法的概述 2 常用遍历算法 2 1 for each 2 2 transform 3 常用查找算法 3 1 find 3 1 1 查找内置类型元素 3 1 2 查找自定义类型元素 3 2 find if 3 2 1 查
  • unity3d IK动画

    IK动画 反向动力学 Inverse Kinematics 就是反过来的呗 即用物体带动骨骼运动 IK动画绑定 void OnAnimatorIK int layerIndex 看向准星 if front sign null animato
  • bootstrap使用之stickUp(固定导航栏)

    1 stickUp是jQuery下的一个导航栏插件 当界面较长时 导航栏会固定在顶部 不会移动 如下图 2 废话少说 下载http www bootcss com p stickup 在里面有示例文档 只是注意看index的时候js写在下面
  • 2 QT—资料文件管理 动作使用设置快捷键

    图标文件下载 https wwa lanzoui com iiZODwjcsbc 项目文件下载 https wwa lanzoui com is9N5wjct3a 1 创建主窗口菜单 1 新建 Qt Widgets 应用 项目名称为 myM
  • JAVA读取USB扫描枪

    利用JAVA程序获取扫描枪的数据 package barcode import java util concurrent BlockingQueue import java util concurrent LinkedBlockingQue