使用Java读写西克RFID

基于Java实现西克RFID读写

背景

由于自动线需要使用RFID标签来进行业务处理,之前几条线普遍使用过的RFID型号是深圳万全广州晨控两家国产RFID,以及德国巴鲁夫的RFID,下面会对使用过的几种RFID进行对比。

本次项目由于越南客户要求使用西克RFID来进行处理,所以进行了测试,本次测试所使用的协议为TCP协议,使用的RFID型号为RFH620

 使用过的几种高频RFID对比

深圳万全VI-IR610P-HF系列RFID:这款RFID支持多种协议,如TCPUDPRS232等协议、支持的使用场景也挺多的,如果只需要进行读取标签信息的话建议使用TCP Server的方式自动接收芯片信息,如果需要使用读写功能建议使用Modbus Tcp来读写芯片信息。

广州晨控FR08系列RFID:FR08系列RFID有E01E00两种,两种RFID都支持Modbus Tcp协议,但是E01主要是EIP通讯,对Modbus Tcp的支持不是很完善,推荐使用E00的RFID,这款系列的RFID管理工具没有TCP Server的配置,只能是通过Modbus Tcp来进行读写。

德国巴鲁夫BIS018A系列RFID:这款型号的RFID对TCP的支持不是很友好,由于的国外产品,技术支持无法提供上位机相关的对接方式,对于软件开发不是特别友好,主要支持的协议是IOLink协议。这款RFID的配置界面是一个网页,可以通过抓包的方式使用HTTP来请求进行RFID的读写,但是供应商并没有提到过这种方式,经过实测,这款RFID特别容易掉IP,所以不建议使用软件对接这款RFID

德国西克RFH6xx系列RFID:这款RFID支持多种协议,如TCPRS232等协议、用户可根据管理工具自定义配置读取的信息,如果只需要进行读取标签信息的话建议使用TCP Server的方式自动接收芯片信息,如果需要对芯片进行读写操作,可使用TCP Socket来读写芯片信息。

声明:本人通过几款使用过的RFID进行对比,只是阐述软件对接过程中的感受,具体使用哪种RFID需要结合业务实际考虑 

西克RFID配置说明

RFID读写头的配置可以参照手册进行操作,其中有几个关键点需要注意 

读取配置

读取配置:读取方式有自动触发、命令触发、条件触发等几种方式,我们常用的方式为自动和命令模式 

自动模式:当设置为自动模式时,程序通过TCP连接读写器后,读写器会通过TCP不断发送数据到程序中,发送频率取决于设置的间隔时长

命令模式:当需要通过Socket发送命令到读写器进行读写时,必须设置为命令模式进行读取,在自动模式下发送命令会出现命令不生效的情况,软件会充当TCP Client被动接收读写器发送的数据

 

数据配置

西克RFID可通过配置数据块自定义TCP返回的数据

数据块中一个块可存储8个数字,可根据业务情况决定使用多少个数据块

网络配置

可设置以太网配置,配置IP地址以及设备通讯模式,经过实测,设备可同时充当客户端与服务端,当配置为客户端时,使用Aux中的端口,也可以通过TCP与设备建立连接,此时设备为服务端状态。

使用Java对RFID标签进行读写

在读取标签之前,我们先看一下通过TCP Socket向西克RFID发送指令的格式

获取UID

通过以上指令我们可以看出,如果需要获取UID的话,只需要发送指令sMN CSGtUID就行了,那么我们通过Java代码的实现则为如下所示

:命令中的[STX][ETX]指的并不是字符,而是0x02(STX,Start of Text)和0x03(ETX,End of Text),用于标识数据包的开始和结束

    // 发送获取 UID 的命令并接收响应
    private static String sendAndReceiveUid(PrintWriter writer, InputStream inputStream) throws IOException {
        String command = STX + "sMN CSGtUID" + ETX + "\n";
        System.out.println("发送 UID 命令: " + command.trim());
        writer.print(command);
        writer.flush();
        //处理socket返回的数据,取出UID
        return readResponse(inputStream, "CSGtUID", TcpSocketExample::parseUid);
    }
读取多个数据块

通过以上命令我们可以看出,读取数据块我们需要发送的指令为sMN CSRdMltBlck,其次就是uid (芯片本身自带的物理ID),bn(开始读取位),nb(结束读取位)三个参数,通过Java代码实现则为以下方式

     // 发送读取数据块命令并接收 RFID 数据
    private static String sendAndReceiveRfid(PrintWriter writer, InputStream inputStream, String uid, int bn, int nb)
            throws IOException {
        String command = String.format("%ssMN CSRdMltBlck %s +%d %d%s\n", STX, uid, bn, nb, ETX);
        System.out.println("发送读取数据块命令: " + command.trim());
        writer.print(command);
        writer.flush();
        //处理socket返回的信息,提取出数据块的内容
        return readResponse(inputStream, "CSRdMltBlck", TcpSocketExample::parseRfid);
    }
写入多个数据块

通过以上命令我们可以看出,读取数据块我们需要发送的指令为sMN WrtMltBlck,其次就是uid (芯片本身自带的物理ID),bn(开始写入位),nb(结束写入位),lbc(数据长度),bc(写入的数据)五个参数,通过Java代码实现则为以下方式

    // 发送写入数据块命令并确认写入成功
    private static String sendAndConfirmWrite(PrintWriter writer, InputStream inputStream,
                                            String uid, int bn, int nb, String data) throws IOException {
        //获取数据长度
        String lbc = String.format("%02X", data.split(" ").length);
        String command = String.format("%ssMN WrtMltBlck %s +%d %d %s %s%s\n", STX, uid, bn, nb, lbc, data, ETX);
        System.out.println("发送写入数据块命令: " + command.trim());
        writer.print(command);
        writer.flush();
        //处理socket返回的信息
        return readResponse(inputStream, "WrtMltBlck", TcpSocketExample::parseBlockContent);
    }
 完整代码

通过以上配置说明我们掌握了使用Java读写西克RFID的基本操作,以下为完整代码,替换IP和端口后可直接运行

package com.example;


import cn.hutool.core.util.RandomUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class TcpSocketExample {
    private static final String HOST = "192.168.20.12"; // 设备的 IP 地址
    private static final int PORT = 2111; // 设备监听的端口号
    private static final char STX = 0x02; // 起始字符
    private static final char ETX = 0x03; // 结束字符

    public static void main(String[] args) {
        try (Socket socket = new Socket(HOST, PORT)) {
            System.out.println("连接设备成功: " + HOST + ":" + PORT);

            try (InputStream inputStream = socket.getInputStream();
                 OutputStream outputStream = socket.getOutputStream();
                 PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8), true)) {
                long startTime = System.currentTimeMillis();
                // 1. 发送获取 UID 的命令
                String uid = sendAndReceiveUid(writer, inputStream);
                if (uid.isEmpty() || Arrays.stream(uid.split(" "))
                        .allMatch(s -> s.equals("0"))) {
                    System.err.println("无法获取有效的 UID,程序退出");
                    return;
                }


                // 2. 读取数据块
                String rfid = sendAndReceiveRfid(writer, inputStream, uid, 0, 1);
                System.out.println("读取到的 RFID 数据: " + rfid);


                while (true) {
                    try {
                        //随机生成16位的RFID号码
                        String newRfid = RandomUtil.randomNumbers(16);
                        //处理RFID数据,将RFID两两分开
                        String writeData = formatDataForWrite(newRfid);
                        //发送指令写入RFID信息
                        String result = sendAndConfirmWrite(writer, inputStream, uid, 0, 1, writeData);
                        if (StringUtils.equals("0", result)) {
                            System.out.println("写入成功");
                        } else {
                            System.out.println("写入失败,结果: " + result);
                        }
                    } catch (Exception e) {
                        System.err.println("写入过程中发生异常: " + e.getMessage());
                    }

                    try {
                        // 暂停两秒
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt(); // 恢复中断状态
                        System.err.println("线程被中断: " + e.getMessage());
                        break; // 可能需要退出循环
                    }
                }
                long endTime = System.currentTimeMillis();
                System.out.println("程序运行时间: " + (endTime - startTime) + " 毫秒");
            }
        } catch (IOException e) {
            System.err.println("设备通信失败: " + e.getMessage());
            e.printStackTrace();
        }
    }

    // 发送获取 UID 的命令并接收响应
    private static String sendAndReceiveUid(PrintWriter writer, InputStream inputStream) throws IOException {
        String command = buildUidCommand();
        System.out.println("发送 UID 命令: " + command.trim());
        writer.print(command);
        writer.flush();

        return readResponse(inputStream, "CSGtUID", TcpSocketExample::parseUid);
    }

    // 发送读取数据块命令并接收 RFID 数据
    private static String sendAndReceiveRfid(PrintWriter writer, InputStream inputStream, String uid, int bn, int nb)
            throws IOException {
        String command = buildReadBlocksCommand(uid, bn, nb);
        System.out.println("发送读取数据块命令: " + command.trim());
        writer.print(command);
        writer.flush();

        return readResponse(inputStream, "CSRdMltBlck", TcpSocketExample::parseRfid);
    }

    // 发送写入数据块命令并确认写入成功
    private static String sendAndConfirmWrite(PrintWriter writer, InputStream inputStream,
                                            String uid, int bn, int nb, String data) throws IOException {
        String command = buildWriteCommand(uid, bn, nb, data);
        System.out.println("发送写入数据块命令: " + command.trim());
        writer.print(command);
        writer.flush();

        return readResponse(inputStream, "WrtMltBlck", TcpSocketExample::parseBlockContent);
    }

    // 通用方法:读取响应并根据关键字解析
    private static String readResponse(InputStream inputStream, String keyword, ResponseParser parser) throws IOException {
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            String response = new String(buffer, 0, bytesRead, StandardCharsets.UTF_8).trim();
            if (response.contains(keyword)) {
                return parser.parse(response);
            }
        }
        return "";
    }

    // 构建获取 UID 的命令
    private static String buildUidCommand() {
        return STX + "sMN CSGtUID" + ETX + "\n";
    }

    // 构建读取数据块的命令
    private static String buildReadBlocksCommand(String uid, int bn, int nb) {
        return String.format("%ssMN CSRdMltBlck %s +%d %d%s\n", STX, uid, bn, nb, ETX);
    }

    // 构建写入数据块的命令
    private static String buildWriteCommand(String uid, int bn, int nb, String data) {
        String lbc = String.format("%02X", data.split(" ").length);
        return String.format("%ssMN WrtMltBlck %s +%d %d %s %s%s\n", STX, uid, bn, nb, lbc, data, ETX);
    }

    // 解析 UID 数据
    private static String parseUid(String response) {
        String[] parts = response.split(" ");
        return IntStream.range(6, parts.length)
                .mapToObj(i -> parts[i].trim())
                .collect(Collectors.joining(" "));
    }

    // 解析 RFID 数据
    private static String parseRfid(String response) {
        String[] parts = response.split(" ");
        return IntStream.range(4, parts.length)
                .mapToObj(i -> parts[i].trim())
                .collect(Collectors.joining());
    }

    private static String parseBlockContent(String response) {
        String[] parts = response.split(" ");
        return parts[2];
    }

    // 格式化待写入的数据为两个字符一组
    private static String formatDataForWrite(String data) {
        return IntStream.range(0, data.length() / 2)
                .mapToObj(i -> data.substring(i * 2, i * 2 + 2))
                .collect(Collectors.joining(" "));
    }

    // 响应解析器函数式接口
    @FunctionalInterface
    interface ResponseParser {
        String parse(String response);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值