美文网首页
Java使用RXTX进行串口SerialPort通讯

Java使用RXTX进行串口SerialPort通讯

作者: 小土豆哥哥 | 来源:发表于2019-01-07 14:14 被阅读0次

    RXTX简介

    RXTX是一个提供串口和并口通信的开源java类库,由该项目发布的文件均遵循LGPL协议。
    RXTX项目提供了Windows,Linux,Mac os X,Solaris操作系统下的兼容javax.comm串口通讯包API的实现,为其他开发人员在此类系统下开发串口应用提供了相当的方便。
    RXTX的使用上与sun提供的comm.jar基本相同,编程时最明显的不同是要包含的包名由javax.comm.改成了gnu.io.

    RxtxAPI 的核心是抽象的CommPort类(用于描述一个被底层系统支持的端口的抽象类,它包含一些高层的IO控制方法,这些方法对于所有不同的通讯端口来说是通用的)及其两个子类:SerialPort类和ParallePort类。其中,SerialPort类是用于串口通信的类,ParallePort类是用于并行口通信的类。CommPort类还提供了常规的通信模式和方法,例如:getInputStream( )方法和getOutputStream( )方法,专用于与端口上的设备进行通信。
    然而,这些类的构造方法都被有意的设置为非公有的(non-public)。所以,不能直接构造对象,而是先通过静态的CommPortIdentifer.getPortIdentifiers()获得端口列表,再从这个端口列表中选择所需要的端口,并调用CommPortIdentifer对象的Open( )方法,这样,就能得到一个CommPort对象。当然,还要将这个CommPort对象的类型转换为某个非抽象的子类,表明是特定的通讯设备,该子类可以是SerialPort类和ParallePort类中的一个。下面将分别对CommPortIdentifier类,串口类SerialPort进行详细的介绍。

    接口
     
    CommDriver可负载设备(the loadable device)驱动程序接口的一部分
     
    CommPortOwnershipListener传递各种通讯端口的所有权事件
     
    ParallelPortEventListener传递并行端口事件
     
    SerialPortEventListener传递串行端口事件
     
    类
     
    CommPort通讯端口
     
    CommPortIdentifier通讯端口管理
     
    ParallelPort并行通讯端口
     
    ParallelPortEvent并行端口事件
     
    SerialPortRS-232串行通讯端口
     
    SerialPortEvent 串行端口事件
     
    异常类
     
    NoSuchPortException当驱动程序不能找到指定端口时抛出
     
    PortInUseException当碰到指定端口正在使用中时抛出
     
    UnsupportedCommOperationException驱动程序不允许指定操作时抛出
     
    CommPortIdentifier类
     
    这个类主要用于对通信端口进行管理和设置,是对端口进行访问控制的核心类,主要包括以下方法:
     
    addPortName(String,int, CommDriver) 添加端口名到端口列表里
     
    addPortOwnershipListener(CommPortOwnershipListener)添加端口拥有的监听器
     
    removePortOwnershipListener(CommPortOwnershipListener)移除端口拥有的监听器
     
    getCurrentOwner()获取当前占有端口的对象或应用程序
     
    getName()获取端口名称
     
    getPortIdentifier(CommPort)获取指定打开的端口的CommPortIdentifier类型对象
     
    getPortIdentifier(String)获取以参数命名的端口的CommPortIdentifier类型对象
     
    getPortIdentifiers()获取系统中的端口列表
     
    getPortType()获取端口的类型
     
    isCurrentlyOwned()判断当前端口是否被占用
     
    open(FileDescriptor)用文件描述的类型打开端口
     
    open(String,int) 打开端口,两个参数:程序名称,延迟时间(毫秒数)
     
    SerialPort类
     
    这个类用于描述一个RS-232串行通信端口的底层接口,它定义了串口通信所需的最小功能集。通过它,用户可以直接对串口进行读、写及设置工作。
     
    SerialPort类中关于串口参数的静态成员变量说明:
     
    DATABITS_5 数据位为5
     
    DATABITS_6 数据位为6
     
    DATABITS_7 数据位为7
     
    DATABITS_8 数据位为8
     
    PARITY_NONE 空格检验
     
    PARITY_ODD 奇检验
     
    PARITY_EVEN 偶检验
     
    PARITY_MARK 标记检验
     
    PARITY_SPACE 无检验
     
    STOPBITS_1 停止位为1
     
    STOPBITS_2 停止位为2
     
    STOPBITS_1_5 停止位为1.5
     
     
     
    SerialPort类中关于串口参数的方法说明:
     
    getBaudRate()得到波特率
     
    getParity()得到检验类型
     
    getDataBits()得到数据位数
     
    getStopBits()得到停止位数
     
    setSerialPortParams(int,int, int, int) 设置串口参数依次为(波特率,数据位,停止位,奇偶检验)
     
    SerialPort类中关于事件的静态成员变量说明:
     
    BI Break interrupt 通讯中断
     
    FE Framing error 帧错误
     
    CD Carrier detect 载波侦听
     
    OE Overrun error 溢位错误
     
    CTS Clear to send 清除发送
     
    PE Parity error 奇偶检验错误
     
    DSR Data set ready 数据设备准备好
     
    RI Ring indicator 响铃侦测
     
    DATA_AVAILABLE 串口中的可用数据
     
    OUTPUT_BUFFER_EMPTY 输出缓冲区已清空
     
     
     
    SerialPort类中关于事件的方法说明:
     
    isCD()是否有载波
     
    isCTS()是否清除以传送
     
    isDSR()数据是否备妥
     
    isDTR()是否数据端备妥
     
    isRI()是否响铃侦测
     
    isRTS()是否要求传送
     
    addEventListener(SerialPortEventListener)向SerialPort对象中添加串口事件监听器
     
    removeEventListener()移除SerialPort对象中的串口事件监听器
     
    notifyOnBreakInterrupt(boolean)设置中断事件true有效,false无效
     
    notifyOnCarrierDetect(boolean)设置载波监听事件true有效,false无效
     
    notifyOnCTS(boolean)设置清除发送事件true有效,false无效
     
    notifyOnDataAvailable(boolean)设置串口有数据的事件true有效,false无效
     
    notifyOnDSR(boolean)设置数据备妥事件true有效,false无效
     
    notifyOnFramingError(boolean)设置发生错误事件true有效,false无效
     
    notifyOnOutputEmpty(boolean)设置发送缓冲区为空事件true有效,false无效
     
    notifyOnParityError(boolean)设置发生奇偶检验错误事件true有效,false无效
     
    notifyOnRingIndicator(boolean)设置响铃侦测事件true有效,false无效
     
    getEventType()得到发生的事件类型返回值为int型
     
    sendBreak(int)设置中断过程的时间,参数为毫秒值
     
    setRTS(boolean)设置或清除RTS位
     
    setDTR(boolean)设置或清除DTR位
     
    SerialPort中的其他常用方法说明:
     
    close()关闭串口
     
    getOutputStream()得到OutputStream类型的输出流
     
    getInputStream()得到InputStream类型的输入流
    
    

    准备工作

    • RXTX包:rxtx-2.2pre2-bins.zip
    • 串口虚拟工具:vspd.exe
    • 串口调试工具:amcktszs_v2.4.0.0.exe

    安装RXTX包

    解压rxtx-2.2pre2-bins.zip,将RXTXcomm.jar加入项目依赖库里,对应操作的系统的rxtxSerial.dll和rxtxParallel.dll文件放入jdk的bin目录下

    前提条件:maven已经加入环境变量中
    mvn install:install-file -DgroupId=gnu.io -DartifactId=RXTXcomm -Dversion=1.0 -Dpackaging=jar -Dfile=E:\Work\Yotrio\libs\RXTXcomm.jar
    

    工具创建一对虚拟串口

    Image.png

    打开串口调试工具,配置串口参数

    Image [2].png

    样例Demo

    封装串口读写工具类
    
    /**
     * 模块名称:projects-parent com.yotrio.common
     * 功能说明:串口服务类,提供打开、关闭串口,读取、发送串口数据等服务(采用单例设计模式)
     * <br>
     * 开发人员:Wangyq
     * 创建时间: 2018-09-20 10:05
     * 系统版本:1.0.0
     **/
    
    public class SerialPortUtil {
    
        private static SerialPortUtil serialPortUtil = null;
    
        static {
            //在该类被ClassLoader加载时就初始化一个SerialTool对象
            if (serialPortUtil == null) {
                serialPortUtil = new SerialPortUtil();
            }
        }
    
        //私有化SerialTool类的构造方法,不允许其他类生成SerialTool对象
        private SerialPortUtil() {
        }
    
        /**
         * 获取提供服务的SerialTool对象
         *
         * @return serialPortUtil
         */
        public static SerialPortUtil getSerialPortUtil() {
            if (serialPortUtil == null) {
                serialPortUtil = new SerialPortUtil();
            }
            return serialPortUtil;
        }
    
    
        /**
         * 查找所有可用端口
         *
         * @return 可用端口名称列表
         */
        public static final ArrayList<String> findPort() {
            //获得当前所有可用串口
            Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
    
            ArrayList<String> portNameList = new ArrayList<>();
    
            //将可用串口名添加到List并返回该List
            while (portList.hasMoreElements()) {
                String portName = portList.nextElement().getName();
                portNameList.add(portName);
            }
    
            return portNameList;
        }
    
        /**
         * 打开串口
         *
         * @param portName 端口名称
         * @param baudrate 波特率
         * @param databits 数据位
         * @param parity   校验位(奇偶位)
         * @param stopbits 停止位
         * @return 串口对象
         * @throws SerialPortParameterFailure 设置串口参数失败
         * @throws NotASerialPort             端口指向设备不是串口类型
         * @throws NoSuchPort                 没有该端口对应的串口设备
         * @throws PortInUse                  端口已被占用
         */
        public static final SerialPort openPort(String portName, int baudrate, int databits, int parity, int stopbits) throws SerialPortParameterFailure, NotASerialPort, NoSuchPort, PortInUse {
    
            try {
                //通过端口名识别端口
                CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(portName);
    
                //打开端口,并给端口名字和一个timeout(打开操作的超时时间)
                CommPort commPort = portIdentifier.open(portName, 2000);
    
                //判断是不是串口
                if (commPort instanceof SerialPort) {
    
                    SerialPort serialPort = (SerialPort) commPort;
                    try {
                        //设置一下串口的波特率等参数
                        serialPort.setSerialPortParams(baudrate, databits, stopbits, parity);
                    } catch (UnsupportedCommOperationException e) {
                        throw new SerialPortParameterFailure();
                    }
    
                    //System.out.println("Open " + portName + " sucessfully !");
                    return serialPort;
                } else {
                    //不是串口
                    throw new NotASerialPort();
                }
            } catch (NoSuchPortException e1) {
                throw new NoSuchPort();
            } catch (PortInUseException e2) {
                throw new PortInUse();
            }
        }
    
        /**
         * 关闭串口
         *
         * @param serialPort 待关闭的串口对象
         */
        public static void closePort(SerialPort serialPort) {
            if (serialPort != null) {
                serialPort.close();
                serialPort = null;
            }
        }
    
        /**
         * 往串口发送数据
         *
         * @param serialPort 串口对象
         * @param order      待发送数据
         * @throws SendDataToSerialPortFailure        向串口发送数据失败
         * @throws SerialPortOutputStreamCloseFailure 关闭串口对象的输出流出错
         */
        public static void sendToPort(SerialPort serialPort, byte[] order) throws SendDataToSerialPortFailure, SerialPortOutputStreamCloseFailure {
            OutputStream out = null;
            try {
                out = serialPort.getOutputStream();
                out.write(order);
                out.flush();
            } catch (IOException e) {
                throw new SendDataToSerialPortFailure();
            } finally {
                try {
                    if (out != null) {
                        out.close();
                        out = null;
                    }
                } catch (IOException e) {
                    throw new SerialPortOutputStreamCloseFailure();
                }
            }
        }
    
        /**
         * 从串口读取数据
         *
         * @param serialPort 当前已建立连接的SerialPort对象
         * @return 读取到的数据
         * @throws ReadDataFromSerialPortFailure     从串口读取数据时出错
         * @throws SerialPortInputStreamCloseFailure 关闭串口对象输入流出错
         */
        public static byte[] readFromPort(SerialPort serialPort) throws ReadDataFromSerialPortFailure, SerialPortInputStreamCloseFailure {
    
            InputStream in = null;
            byte[] bytes = null;
    
            try {
                in = serialPort.getInputStream();
                int bufflenth = in.available();        //获取buffer里的数据长度
    
                while (bufflenth != 0) {
                    bytes = new byte[bufflenth];    //初始化byte数组为buffer中数据的长度
                    in.read(bytes);
                    bufflenth = in.available();
                }
            } catch (IOException e) {
                throw new ReadDataFromSerialPortFailure();
            } finally {
                try {
                    if (in != null) {
                        in.close();
                        in = null;
                    }
                } catch (IOException e) {
                    throw new SerialPortInputStreamCloseFailure();
                }
            }
    
            return bytes;
    
        }
    
        /**
         * 添加监听器
         *
         * @param port     串口对象
         * @param listener 串口监听器
         * @throws TooManyListeners 监听类对象过多
         */
        public static void addListener(SerialPort port, SerialPortEventListener listener) throws TooManyListeners {
    
            try {
                //给串口添加监听器
                port.addEventListener(listener);
                //设置当有数据到达时唤醒监听接收线程
                port.notifyOnDataAvailable(true);
                //设置当通信中断时唤醒中断线程
                port.notifyOnBreakInterrupt(true);
            } catch (TooManyListenersException e) {
                throw new TooManyListeners();
            }
        }
    
        /**
         * 删除监听器
         *
         * @param port     串口对象
         * @param listener 串口监听器
         * @throws TooManyListeners 监听类对象过多
         */
        public static void removeListener(SerialPort port, SerialPortEventListener listener) {
            //删除串口监听器
            port.removeEventListener();
        }
    
    }
    
    整合websocket获取并推送给前台页面实时显示
    @ServerEndpoint(value = "/websocket") //接受websocket请求路径
    @Component
    public class PoundWebSocket {
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
        /**
         * 保存所有在线socket连接
         */
        private static Map<String, PoundWebSocket> webSocketMap = new LinkedHashMap<>();
    
        /**
         * 记录当前在线数目
         */
        private static int count = 0;
    
        /**
         * 当前连接(每个websocket连入都会创建一个MyWebSocket实例
         */
        private Session session;
    
        /**
         * 创建监听串口
         */
        private static SerialPort serialPort = null;
    
        /**
         * 创建监听器
         */
        private static SerialPortEventListener serialPortEventListener = null;
    
        /**
         * 监听串口
         */
        private static String PORT_NAME;
    
        /**
         * 监听串口波特率
         */
        private static int BAUD_RATE;
    
        /**
         * 数据位
         */
        private static int DATA_BITS;
    
        /**
         * 停止位
         */
        private static int STOP_BITS;
    
        /**
         * 奇偶位
         */
        private static int PARITY;
    
        /**
         * 地磅型号
         */
        private static String MODEL;
    
        private static IPoundInfoService poundInfoService;
    
        private static ApplicationContext applicationContext;
    
        public static void setApplicationContext(ApplicationContext applicationContext) {
            PoundWebSocket.applicationContext = applicationContext;
        }
    
        private static StringBuffer stringBuffer = new StringBuffer();
    
        /**
         * 处理连接建立
         *
         * @param session
         */
        @OnOpen
        public void onOpen(Session session) {
            if (poundInfoService == null) {
                poundInfoService = applicationContext.getBean(IPoundInfoService.class);
            }
            //获取地磅信息
            PoundInfo poundInfo = poundInfoService.findOne();
            PORT_NAME = poundInfo.getSerialPort();
            BAUD_RATE = poundInfo.getBaudRate();
            MODEL = poundInfo.getModel();
            DATA_BITS = poundInfo.getDataBits() != null ? poundInfo.getDataBits() : SerialPort.DATABITS_8;
            STOP_BITS = poundInfo.getStopBits() != null ? poundInfo.getStopBits() : SerialPort.STOPBITS_1;
            PARITY = poundInfo.getParity() != null ? poundInfo.getParity() : SerialPort.PARITY_NONE;
    
            this.session = session;
            webSocketMap.put(session.getId(), this);
            addCount();
    //        logger.info("新的连接加入:{}", session.getId());
            try {
                //确保串口已被关闭,未关闭会导致重新监听串口失败
                if (serialPort != null) {
                    SerialPortUtil.closePort(serialPort);
                    serialPort = null;
                }
                //创建串口 COM5位串口名称 9600波特率
                if (serialPort == null && StringUtils.isNotEmpty(PORT_NAME) && StringUtils.isNotEmpty(MODEL)) {
                    serialPort = SerialPortUtil.openPort(PORT_NAME, BAUD_RATE, DATA_BITS, PARITY, STOP_BITS);
    //                logger.info("创建串口:{}", serialPort);
                    //设置串口监听
                    SerialPortUtil.addListener(serialPort, new SerialPortEventListener() {
    
                        @Override
                        public void serialEvent(SerialPortEvent serialPortEvent) {
                            if (serialPortEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
                                try {
                                    //读取串口数据
                                    byte[] bytes = SerialPortUtil.readFromPort(serialPort);
    
                                    //根据型号解析字符串
                                    switch (MODEL) {
                                        case PoundConstant.MODEL_XK_3190:
                                            parsingString1(bytes);
                                            break;
                                        case PoundConstant.MODEL_XK_3190_10:
                                            parsingString2(bytes);
                                            break;
                                        case PoundConstant.MODEL_D_2008:
                                            parsingString1(bytes);
                                            break;
                                        case PoundConstant.MODEL_DK_3230_D_6:
                                            parsingString3(bytes);
                                            break;
                                        case PoundConstant.MODEL_D_2009_F:
                                            parsingString4(bytes);
                                            break;
                                        default:
                                            String value = String.valueOf(Integer.valueOf(new String(bytes, "GB2312")) - RandomUtil.randomInt(1000, 10000));
                                            sendMessageToAll(value);
                                    }
    
    //                                System.out.println("收到的数据:" + new String(bytes, "GB2312") + "----" + new Date());
    
                                } catch (ReadDataFromSerialPortFailure readDataFromSerialPortFailure) {
                                    logger.error(readDataFromSerialPortFailure.toString());
                                } catch (SerialPortInputStreamCloseFailure serialPortInputStreamCloseFailure) {
                                    logger.error(serialPortInputStreamCloseFailure.toString());
                                } catch (UnsupportedEncodingException e) {
                                    logger.error(e.toString());
                                } catch (IOException e) {
                                    logger.error(e.toString());
                                }
                            }
                        }
                    });
                }
            } catch (SerialPortParameterFailure serialPortParameterFailure) {
                logger.error(serialPortParameterFailure.toString());
            } catch (NotASerialPort notASerialPort) {
                logger.error(notASerialPort.toString());
            } catch (NoSuchPort noSuchPort) {
                logger.error(noSuchPort.toString());
            } catch (PortInUse portInUse) {
                logger.error(portInUse.toString());
            } catch (TooManyListeners tooManyListeners) {
                logger.error(tooManyListeners.toString());
            }
        }
    
        /**
         * 解析字符串 方法1
         *
         * @param bytes 获取的字节码
         */
        private void parsingString1(byte[] bytes) {
            StringBuffer sb = new StringBuffer();
            //将ASCII码转成字符串
            for (int i = 0; i < bytes.length; i++) {
                sb.append((char) Integer.parseInt(String.valueOf(bytes[i])));
            }
    
            //解析字符串
            String[] strs = sb.toString().trim().split("\\+");
            int weight = 0;
            for (int j = 0; j < strs.length; j++) {
                if (strs[j].trim().length() >= 6) {
                    weight = Integer.parseInt(strs[j].trim().substring(0, 6));
                    //发送数据
                    sendMessageToAll(String.valueOf(weight));
                    break;
                }
            }
        }
    
        /**
         * 解析字符串 方法2
         *
         * @param bytes 获取的字节码
         */
        private void parsingString2(byte[] bytes) {
            StringBuffer sb = new StringBuffer();
            //将ASCII码转成字符串
            for (int i = 0; i < bytes.length; i++) {
                sb.append((char) Integer.parseInt(String.valueOf(bytes[i])));
            }
            //解析字符串
            String[] strs = sb.toString().trim().split("\\+");
            double weight = 0;
            for (int j = 0; j < strs.length; j++) {
                if (strs[j].trim().length() >= 6) {
                    weight = Double.parseDouble(strs[j].trim().substring(0, 6)) / 10;
                    //发送数据
                    sendMessageToAll(String.valueOf(weight));
                    break;
                }
            }
        }
    
        /**
         * 解析字符串 方法3
         *
         * @param bytes 获取的字节码
         */
        private void parsingString3(byte[] bytes) {
            StringBuffer sb = new StringBuffer();
            //将ASCII码转成字符串
            for (int i = 0; i < bytes.length; i++) {
                sb.append((char) Integer.parseInt(String.valueOf(bytes[i])));
            }
    
    //        logger.info("sb:" + sb.toString());
            sb.reverse();
    
            //解析字符串
            String[] strs = sb.toString().trim().split("\\=");
            double weight = 0;
            for (int j = 0; j < strs.length; j++) {
                if (strs[j].trim().length() >= 6) {
                    weight = Double.parseDouble(strs[j].trim());
                    //发送数据
                    sendMessageToAll(String.valueOf(weight));
                    break;
                }
            }
        }
    
        /**
         * 解析字符串 方法3
         *
         * @param bytes 获取的字节码
         */
        private void parsingString4(byte[] bytes) {
            StringBuffer sb = new StringBuffer();
            //将ASCII码转成字符串
            for (int i = 0; i < bytes.length; i++) {
                sb.append((char) Integer.parseInt(String.valueOf(bytes[i])));
            }
    
    //        logger.info("sb:" + sb.reverse());
            //字符串反转
            sb.reverse();
    
            //解析字符串
            String[] strs = sb.toString().trim().split("\\=");
            int weight = 0;
            for (int j = 0; j < strs.length; j++) {
                if (strs[j].trim().length() >= 6) {
                    weight = Integer.parseInt(strs[j].trim().substring(0, 6));
                    //发送数据
                    sendMessageToAll(String.valueOf(weight));
                    break;
                }
            }
        }
    
        /**
         * 接受消息
         *
         * @param message
         * @param session
         */
        @OnMessage
        public void onMessage(String message, Session session) {
            logger.info("收到客户端{}消息:{}", session.getId(), message);
            try {
                this.sendMessage(message);
            } catch (Exception e) {
                logger.error(e.toString());
            }
        }
    
        /**
         * 处理错误
         *
         * @param error
         * @param session
         */
        @OnError
        public void onError(Throwable error, Session session) {
            logger.info("发生错误{},{}", session.getId(), error.getMessage());
        }
    
        /**
         * 处理连接关闭
         */
        @OnClose
        public void onClose() {
            webSocketMap.remove(this.session.getId());
            reduceCount();
            logger.info("连接关闭:{}", this.session.getId());
    
            //连接关闭后关闭串口,下一次打开连接重新监听串口
            if (serialPort != null) {
                SerialPortUtil.closePort(serialPort);
                serialPort = null;
            }
        }
    
        /**
         * 群发消息
         *
         * @param message
         */
        public void sendMessageToAll(String message) {
            for (int i = 0; i < webSocketMap.size(); i++) {
                try {
    //                logger.info("session:id=" + session.getId());
                    this.session.getBasicRemote().sendText(message);
                } catch (IOException e) {
                    logger.error(e.getMessage());
                }
            }
        }
    
        /**
         * 发送消息
         *
         * @param message
         * @throws IOException
         */
        public void sendMessage(String message) throws IOException {
    //        logger.info("session:id=" + session.getId());
            this.session.getBasicRemote().sendText(message);
        }
    
        //广播消息
        public static void broadcast() {
            PoundWebSocket.webSocketMap.forEach((k, v) -> {
                try {
                    v.sendMessage("这是一条测试广播");
                } catch (Exception e) {
                }
            });
        }
    
        //获取在线连接数目
        public static int getCount() {
            return count;
        }
    
        //操作count,使用synchronized确保线程安全
        public static synchronized void addCount() {
            PoundWebSocket.count++;
        }
    
        public static synchronized void reduceCount() {
            PoundWebSocket.count--;
        }
    }
    

    友情链接

    相关文章

      网友评论

          本文标题:Java使用RXTX进行串口SerialPort通讯

          本文链接:https://www.haomeiwen.com/subject/cedurqtx.html