当前位置:   article > 正文

Java 串口通信(RS232/485)_java串口通信

java串口通信

一.串口通信页面

在这里插入图片描述
Java 实现串口通信,同时通过 WebSocket 与 UI 实时交互传递通信数据

准备工作:

虚拟串口工具:Launch Virtual Serial Port Driver
串口调试助手:SSCOM

在这里插入图片描述

RS485

根据 Modbus 协议,常规485通讯的信息发送形式如下:
地址  功能码 数据信息 校验码
1byte 1byte  nbyte  2byte
  • 1
  • 2
  • 3

在线 CRC检验码计算:CRC 测试链接

二.串口服务实现

1.Java 串口通信配置

1.扩展包和依赖库

RXTXcomm.jar   放入 {JAVA_HOME}/jre/lib/ext
rxtxserial.dll 放入 {JAVA_HOME}/jre/bin
  • 1
  • 2

以上两个包可以直接网上下载,注意和JDK版本搭配即可

2.Pom配置

串口通信包:rxtx

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>SerialPort</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.4</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>5.3.27</version>
        </dependency>
        <dependency>
            <groupId>org.rxtx</groupId>
            <artifactId>rxtx</artifactId>
            <version>2.1.7</version>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>nexus-aliyun</id>
            <name>nexus-aliyun</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

</project>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

2.启动类

package com.serial.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author
 * @date 2023-07-01 12:41
 * @since 1.8
 */
@SpringBootApplication
public class SerialApplication {

    public static void main(String[] args) {
        SpringApplication.run(SerialApplication.class,args);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3.工具包类

1.Common

package com.serial.demo.util;

/**
 * @author
 * @date 2023-07-03 22:17
 * @since 1.8
 */
public class Common {

    public static String HEX_STRING = "0123456789ABCDEF";
    public static final String NONE = "无";
    public static final String ODD = "奇";
    public static final String EVEN = "偶";

    public static final String FORMAT_HEX="HEX";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.Crc16Modbus

CRC16 Modbus Java 实现:计算数据的校验码
  • 1
package com.serial.demo.util;

/**
 * @author
 * @date 2023-07-04 20:37
 * @since 1.8
 */
public class Crc16Modbus {

    /**
     * CRC 循环冗余校验 即通过生成多项式对原始数据进行计算,将计算结果拼接到数据上一起发送
     *     接收方计算接收到的数据校验接收结果是否准确
     * CRC 即对生成多项式的模二运算
     *
     * 1.预置1个16位的寄存器为十六进制 FFFF(即全为1),称此寄存器为CRC寄存器
     * 2.把第1个8位二进制数据(帧头字节)与 CRC 寄存器的低8位相异或并写回寄存器 高8位数据不变
     * 3.把 CRC 循环右移 高位补 0 取得移出位
     * 4.如果移出位为 0 继续右移 如果移出位为 1 则 CRC 寄存器与多项式 A001(1010 0000 0000 0001)进行异或运算
     * 5.重复步骤 3 和 4 直到右移 8 次
     * 6.重复步骤 2 到 5 进行数据帧下一个字节的处理 直到将数据帧所有字节按上述步骤计算
     * 7.根据需要将寄存器的高、低字节进行交换 得到最终 CRC码
     *
     */

    /**
     * 初始值 CRC-16 寄存器
     */
    private static final int INITIAL_VALUE = 0xFFFF;
    private static final boolean IS_OUT_PUT_OVER_TURN = true;

    /**
     * 原始数据 + CRC码
     *
     * @param hexes 16 进制字符串
     * @return
     */
    public static byte[] getData(String... hexes) {
        byte[] data = new byte[hexes.length];
        int i = 0;
        for (String hex:hexes){
            //先转为数字在转为 byte
            data[i++] = (byte) Integer.parseInt(hex, 16);
        }
        return merge(data);
    }

    /**
     * 原始数据 + CRC码
     *
     * @param data
     * @return
     */
    public static byte[] merge(byte[] data) {
        byte[] crc = getCrc16(data);
        int dLen = data.length;
        int cLen = crc.length;
        byte[] result = new byte[dLen + cLen];
        System.arraycopy(data,0,result,0,dLen);
        System.arraycopy(crc,0,result,dLen,cLen);
        return result;
    }

    /**
     * 基于 CRC16 Modbus 计算校验码
     * CRC 16 Modbus 默认多项式为 x16+x15+x2+1 => 8005 反转即 A001
     *
     * @param data
     * @return
     */
    private static byte[] getCrc16(byte[] data) {
        int len = data.length;
        int crc = INITIAL_VALUE;
        int i, j;
        for (i = 0; i < len; i++) {
            // 把第一个 8 位二进制数据 与 16 位的 CRC寄存器的低 8 位相异或, 把结果放于 CRC寄存器
            crc = ((crc & 0xFF00) | (crc & 0x00FF) ^ (data[i] & 0xFF));
            for (j = 0; j < 8; j++) {
                // 把 CRC 寄存器的内容右移一位(朝低位)用 0 填补最高位, 并检查右移后的移出位
                if ((crc & 0x0001) > 0) {
                    // 如果移出位为 1, CRC寄存器与多项式A001进行异或
                    crc = crc >> 1;
                    crc = crc ^ 0xA001;
                } else {
                    // 如果移出位为 0,再次右移一位
                    crc = crc >> 1;
                }
            }
        }
        return intToBytes(crc);
    }

    /**
     * 将 int 转换成 byte 数组 低位在前 高位在后
     */
    private static byte[] intToBytes(int value)  {
        byte[] src = new byte[2];
        byte hig = (byte) ((value>>8) & 0xFF);
        byte low = (byte) (value & 0xFF);
        if (IS_OUT_PUT_OVER_TURN){
            src[0] = low;
            src[1] = hig;
        } else {
            src[0] = hig;
            src[1] = low;
        }
        return src;
    }

    /**
     * 将字节数组转换成十六进制字符串
     */
    public static String byteTo16String(byte[] data) {
        StringBuffer buffer = new StringBuffer();
        for (byte b : data) {
            byteToHex(buffer,b);
        }
        return buffer.toString().toUpperCase();
    }

    /**
     * 将字节转换成十六进制字符串
     *
     * int 转 byte 对照表
     * [128,255],0,[1,128)
     * [-128,-1],0,[1,128)
     */
    public static void byteToHex(StringBuffer buffer ,byte b) {
        if (b < 0) {
            buffer.append(Integer.toString(b + 256, 16));
        } else if (b == 0) {
            buffer.append("00 ");
        } else if (b > 0 && b <= 15) {
            buffer.append("0" + Integer.toString(b, 16));
        } else if (b > 15) {
            buffer.append(Integer.toString(b, 16));
        }
        buffer.append(" ");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

3.SerialUtil

package com.serial.demo.util;

import gnu.io.SerialPort;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

/**
 * @author
 * @date 2023-07-03 21:52
 * @since 1.8
 */
public class SerialUtil {


    /**
     * 转为 HEX
     * @param str
     * @return
     */
    public static String toHex(String str){
        StringBuffer sbf = new StringBuffer();
        byte[] b = str.getBytes(StandardCharsets.UTF_8);
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sbf.append(hex.toUpperCase() + "  ");
        }
        return sbf.toString().trim();
    }

    /**
     *
     * @param hex
     * @return
     */
    public static String toStr(String hex) {
        return new String(hexToByte(hex));
    }

    /**
     * 转 HEX 字节
     * @param hex
     * @return
     */
    public static byte[] hexToByte(String hex){
        hex = hex.toUpperCase().replace(" ","");
        ByteArrayOutputStream bao = new ByteArrayOutputStream(hex.length() / 2);
        // 将每2位16进制整数组装成一个字节
        for (int i = 0; i < hex.length(); i += 2) {
            bao.write((Common.HEX_STRING.indexOf(hex.charAt(i)) << 4 | Common.HEX_STRING.indexOf(hex.charAt(i + 1))));
        }
        return bao.toByteArray();
    }

    /**
     * 获取校验位配置
     * @param checkBit
     * @return
     */
    public static int getParity(String checkBit){
        if (Common.NONE.equals(checkBit)){
            return SerialPort.PARITY_NONE;
        } else if (Common.ODD.equals(checkBit)){
            return SerialPort.PARITY_ODD;
        } else if (Common.EVEN.equals(checkBit)){
            return SerialPort.PARITY_EVEN;
        } else {
            return SerialPort.PARITY_NONE;
        }
    }

    /**
     * 读取数据
     * @param in
     * @return
     */
    public static byte[] readFromPort(InputStream in) {
        byte[] bytes = {};
        try {
            // 缓冲区大小为一个字节
            byte[] readBuffer = new byte[1];
            int bytesNum = in.read(readBuffer);
            while (bytesNum > 0) {
                bytes = concat(bytes, readBuffer);
                bytesNum = in.read(readBuffer);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                    in = null;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bytes;
    }

    /**
     * 字节转换
     * @param format
     * @param b
     * @return
     */
    public static String printHexString(String format, byte[] b) {
        String result = new String(b);
        if (Common.FORMAT_HEX.equals(format)){
            return SerialUtil.toHex(result);
        }
        return result;
    }

    /**
     * 合并数组
     *
     * @param firstArray  第一个数组
     * @param secondArray 第二个数组
     * @return 合并后的数组
     */
    public static byte[] concat(byte[] firstArray, byte[] secondArray) {
        if (firstArray == null || secondArray == null) {
            if (firstArray != null) {
                return firstArray;
            }
            if (secondArray != null) {
                return secondArray;
            }
            return null;
        }
        byte[] bytes = new byte[firstArray.length + secondArray.length];
        System.arraycopy(firstArray, 0, bytes, 0, firstArray.length);
        System.arraycopy(secondArray, 0, bytes, firstArray.length, secondArray.length);
        return bytes;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144

4.WebSocket 配置

1.启动配置

package com.serial.demo.socket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author
 * @date 2023-07-02 21:05
 * @since 1.8
 */
@Configuration
public class WebSocketConfig {

    /**
     * 开启 websocket 配置
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

2.监听配置

package com.serial.demo.socket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author
 * @date 2023-07-02 21:07
 * @since 1.8
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/{sid}")
public class SerialWebSocket {

    /**
     * 缓存通信实例
     */
    private static Map<String,SerialWebSocket> webSocketMap = new ConcurrentHashMap<>(16);

    /**
     * 会话
     */
    private Session session;

    /**
     * 标识
     */
    private String sid;

    /**
     * 建立连接
     * @param sid
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("sid") String sid,Session session){
        this.session = session;
        this.sid = sid;
        webSocketMap.put(sid,this);
        //sendMessage(sid,"Hello:");
    }

    /**
     * 关闭连接
     * @param sid
     */
    @OnClose
    public void onClose(@PathParam("sid") String sid){
        try {
            SerialWebSocket socket = webSocketMap.remove(sid);
            if (socket != null){
                socket.session.close();
                socket = null;
            }
        } catch (IOException e) {
            log.error("Close {} exception:",sid,e);
        }
    }

    /**
     * 接收消息
     * @param message
     */
    @OnMessage
    public void onMessage(String message){
        log.info("sid {} msg {}",this.sid,message);
    }

    /**
     * 发送消息
     * @param message
     * @param sid
     */
    public static void sendMessage(String sid,String message){
        SerialWebSocket socket = webSocketMap.get(sid);
        if (socket != null){
            try {
                socket.session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                log.error("Send {} message {} exception:",sid,message,e);
            }
        }
    }

    /**
     * 广播消息
     * @param message
     */
    public static void broadcast(String message){
        for (String sid:webSocketMap.keySet()){
            sendMessage(sid,message);
        }
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

5.UI交互类

1.串口配置对象

package com.serial.demo.entity;

import lombok.Data;

/**
 * @author
 * @date 2023-07-02 22:58
 * @since 1.8
 */
@Data
public class SerialEntity {

    private String portId;
    private int bitRate;
    private int dataBit;
    private int stopBit;
    private String checkBit;
    private String format;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

2.串口信息获取接口

package com.serial.demo.controller;

import com.serial.demo.config.SerialPortConfig;
import com.serial.demo.util.SerialUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author
 * @date 2023-07-01 16:37
 * @since 1.8
 */
@CrossOrigin
@RestController
@RequestMapping("/serial")
public class SerialController {

    @Autowired
    SerialPortConfig serial;

    /**
     * 获取端口列表
     * @return
     */
    @GetMapping("/getSerialPortList")
    public List<String> getSerialPortList(){
        return serial.getSerialPortList();
    }

    /**
     * 字符串 转 HEX
     * @return
     */
    @GetMapping("/toHex")
    public String toHex(String str){
        return SerialUtil.toHex(str);
    }

    /**
     * HEX 转 字符串
     * @return
     */
    @GetMapping("/toStr")
    public String toStr(String hex){
        return SerialUtil.toStr(hex);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

3.RS232接口

package com.serial.demo.controller;

import com.serial.demo.config.Rs232Config;
import com.serial.demo.entity.SerialEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author
 * @date 2023-07-03 1:03
 * @since 1.8
 */
@CrossOrigin
@RestController
@RequestMapping("/serial/232")
public class Rs232Controller {

    @Autowired
    Rs232Config rs232Config;

    /**
     * 监听端口
     * @param serial
     */
    @PostMapping("/open")
    public boolean open(@RequestBody SerialEntity serial){
        return rs232Config.openPort(serial);
    }

    /**
     * 获取端口列表
     * @return
     */
    @GetMapping("/close/{portId}")
    public void close(@PathVariable("portId") String portId){
        rs232Config.closePort(portId);
    }

    /**
     * 获取端口列表
     * @return
     */
    @GetMapping("/send/{portId}/{format}/{msg}")
    public void close(@PathVariable("portId") String portId,@PathVariable("format") String format,@PathVariable("msg") String msg){
        rs232Config.sendData(portId,format,msg);
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

4.RS485接口

package com.serial.demo.controller;

import com.serial.demo.config.Rs485Config;
import com.serial.demo.entity.SerialEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author
 * @date 2023-07-03 23:33
 * @since 1.8
 */
@CrossOrigin
@RestController
@RequestMapping("/serial/485")
public class Rs485Controller {

    @Autowired
    Rs485Config rs485Config;

    /**
     * 监听端口
     * @param serial
     */
    @PostMapping("/open")
    public boolean open(@RequestBody SerialEntity serial){
        return rs485Config.openPort(serial);
    }

    /**
     * 获取端口列表
     * @return
     */
    @GetMapping("/close/{portId}")
    public void close(@PathVariable("portId") String portId){
        rs485Config.closePort(portId);
    }

    /**
     * 获取端口列表
     * @return
     */
    @GetMapping("/send/{portId}/{format}/{msg}")
    public void close(@PathVariable("portId") String portId,@PathVariable("format") String format,@PathVariable("msg") String msg){
        rs485Config.sendData(portId,format,msg);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

6.串口配置类

1.串口配置

package com.serial.demo.config;

import gnu.io.CommPortIdentifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author
 * @date 2023-07-03 1:01
 * @since 1.8
 */
@Slf4j
@Component
public class SerialPortConfig {

    /**
     * 缓存端口信息
     */
    private static Map<String, CommPortIdentifier> serialMap;

    @PostConstruct
    private void init(){
        refreshCom();
    }

    /**
     * 刷新端口
     */
    public void refreshCom(){
        Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
        CommPortIdentifier serial;
        Map<String,CommPortIdentifier> temp = new ConcurrentHashMap<>(16);
        while (portList.hasMoreElements()){
            serial = portList.nextElement();
            if (serial.getPortType() == CommPortIdentifier.PORT_SERIAL){
                temp.put(serial.getName(),serial);
            }
        }
        serialMap = Collections.unmodifiableMap(temp);
    }

    /**
     * 获取端口列表
     * @return
     */
    public List<String> getSerialPortList(){
        return serialMap.keySet().stream().sorted().collect(Collectors.toList());
    }

    /**
     * 获取串口
     * @return
     */
    public Map<String, CommPortIdentifier> getSerialMap(){
        return serialMap;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

2.RS232串口配置

package com.serial.demo.config;

import com.serial.demo.entity.SerialEntity;
import com.serial.demo.util.Common;
import com.serial.demo.util.SerialUtil;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author
 * @date 2023-07-01 16:22
 * @since 1.8
 */
@Slf4j
@Component
public class Rs232Config {

    private static final int DELAY_TIME = 1000;


    @Autowired
    SerialPortConfig config;

    /**
     * 缓存端口实例
     */
    private Map<String, SerialPort> serialPortMap = new ConcurrentHashMap<>(16);

    /**
     * 监听端口
     * @param serial
     */
    public boolean openPort(SerialEntity serial) {
        String portId = serial.getPortId();
        CommPortIdentifier commPortIdentifier = config.getSerialMap().get(portId);
        if (null != commPortIdentifier){
            SerialPort serialPort = null;
            int bitRate = 0,dataBit = 0,stopBit = 0,parity = 0;
            try {
                serialPort = (SerialPort) commPortIdentifier.open(portId,DELAY_TIME);
                // 设置监听器生效 当有数据时通知
                serialPort.notifyOnDataAvailable(true);
                // 比特率、数据位、停止位、奇偶校验位
                bitRate = serial.getBitRate();
                dataBit = serial.getDataBit();
                stopBit = serial.getStopBit();
                parity = SerialUtil.getParity(serial.getCheckBit());
                serialPort.setSerialPortParams(bitRate, dataBit, stopBit,parity);
            } catch (PortInUseException e) {
                log.error("Open CommPortIdentifier {} Exception:",serial.getPortId(),e );
                return false;
            } catch (UnsupportedCommOperationException e) {
                log.error("Set SerialPortParams BitRate {} DataBit {} StopBit {} Parity {} Exception:",bitRate,dataBit,stopBit,parity,e);
                return false;
            }

            // 设置当前串口的输入输出流
            InputStream input;
            OutputStream output;
            try {
                input = serialPort.getInputStream();
                output = serialPort.getOutputStream();
            } catch (IOException e) {
                log.error("Get serialPort data stream exception:",e);
                return false;
            }

            // 给当前串口添加一个监听器
            try {
                serialPort.addEventListener(new Serial232Listener(input,output,serial.getFormat()));
            } catch (TooManyListenersException e) {
                log.error("Get serialPort data stream exception:",e);
                return false;
            }
            serialPortMap.put(portId,serialPort);
            return true;
        }
        return false;
    }

    /**
     * 关闭端口
     * @param portId
     */
    public void closePort(String portId){
        SerialPort serialPort = serialPortMap.remove(portId);
        if (null != serialPort){
            serialPort.close();
        }
    }

    /**
     * 发送数据
     * @param portId
     * @param format
     * @param message
     */
    public void sendData(String portId,String format,String message){
        SerialPort serialPort = serialPortMap.get(portId);
        if (null == serialPort){
            return;
        }
        OutputStream output = null;
        try {
            byte[] bytes;
            if (Common.FORMAT_HEX.equals(format)){
                bytes = SerialUtil.hexToByte(message);
            } else {
                bytes = message.getBytes(StandardCharsets.UTF_8);
            }
            output = serialPort.getOutputStream();
            output.write(bytes);
            output.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (null != output){
                try {
                    output.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141

3.RS232串口监听

package com.serial.demo.config;

import com.serial.demo.socket.SerialWebSocket;
import com.serial.demo.util.Crc16Modbus;
import com.serial.demo.util.SerialUtil;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author
 * @date 2023-07-01 17:06
 * @since 1.8
 */
public class Serial232Listener implements SerialPortEventListener {

    InputStream inputStream;
    OutputStream outputStream;
    String format;

    public Serial232Listener(InputStream input, OutputStream output, String format){
        inputStream = input;
        outputStream = output;
        this.format = format;
    }

    @Override
    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
            case SerialPortEvent.BI:
            case SerialPortEvent.OE:
            case SerialPortEvent.FE:
            case SerialPortEvent.PE:
            case SerialPortEvent.CD:
            case SerialPortEvent.CTS:
            case SerialPortEvent.DSR:
            case SerialPortEvent.RI:
            case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                break;
            case SerialPortEvent.DATA_AVAILABLE:
                // 当有可用数据时读取数据
                byte[] readBuffer = null;
                int availableBytes = 0;
                try {
                    availableBytes = inputStream.available();
                    while (availableBytes > 0) {
                        readBuffer = SerialUtil.readFromPort(inputStream);
                        String needData = Crc16Modbus.byteTo16String(readBuffer);
                        SerialWebSocket.broadcast(needData);
                        availableBytes = inputStream.available();
                    }
                } catch (IOException e) {
                }
            default:
                break;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

4.RS485串口配置

package com.serial.demo.config;

import com.serial.demo.entity.SerialEntity;
import com.serial.demo.util.Common;
import com.serial.demo.util.Crc16Modbus;
import com.serial.demo.util.SerialUtil;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author
 * @date 2023-07-03 1:00
 * @since 1.8
 */
@Slf4j
@Component
public class Rs485Config {

    private static final int DELAY_TIME = 1000;


    @Autowired
    SerialPortConfig config;

    /**
     * 缓存端口实例
     */
    private Map<String, SerialPort> serialPortMap = new ConcurrentHashMap<>(16);

    /**
     * 监听端口
     * @param serial
     */
    public boolean openPort(SerialEntity serial) {
        String portId = serial.getPortId();
        CommPortIdentifier commPortIdentifier = config.getSerialMap().get(portId);
        if (null != commPortIdentifier){
            SerialPort serialPort;
            int bitRate = 0,dataBit = 0,stopBit = 0,parity = 0;
            try {
                serialPort = (SerialPort) commPortIdentifier.open(portId,DELAY_TIME);
                // 设置监听器生效 当有数据时通知
                serialPort.notifyOnDataAvailable(true);
                serialPort.setDTR(true);
                serialPort.setRTS(true);
                // 比特率、数据位、停止位、奇偶校验位
                bitRate = serial.getBitRate();
                dataBit = serial.getDataBit();
                stopBit = serial.getStopBit();
                parity = SerialUtil.getParity(serial.getCheckBit());
                serialPort.setSerialPortParams(bitRate, dataBit, stopBit,parity);
            } catch (PortInUseException e) {
                log.error("Open CommPortIdentifier {} Exception:",serial.getPortId(),e );
                return false;
            } catch (UnsupportedCommOperationException e) {
                log.error("Set SerialPortParams BitRate {} DataBit {} StopBit {} Parity {} Exception:",bitRate,dataBit,stopBit,parity,e);
                return false;
            }

            // 设置当前串口的输入输出流
            InputStream input;
            OutputStream output;
            try {
                input = serialPort.getInputStream();
                output = serialPort.getOutputStream();
            } catch (IOException e) {
                log.error("Get serialPort data stream exception:",e);
                return false;
            }

            // 给当前串口添加一个监听器
            try {
                serialPort.addEventListener(new Serial485Listener(input,output,serial.getFormat()));
            } catch (TooManyListenersException e) {
                log.error("Get serialPort data stream exception:",e);
                return false;
            }
            serialPortMap.put(portId,serialPort);
            return true;
        }
        return false;
    }

    /**
     * 关闭端口
     * @param portId
     */
    public void closePort(String portId){
        SerialPort serialPort = serialPortMap.remove(portId);
        if (null != serialPort){
            serialPort.close();
        }
    }

    /**
     * 发送数据
     * @param portId
     * @param format
     * @param message
     */
    public void sendData(String portId,String format,String message){
        SerialPort serialPort = serialPortMap.get(portId);
        if (null == serialPort){
            return;
        }
        OutputStream output = null;
        try {
            byte[] bytes = new byte[0];
            if (Common.FORMAT_HEX.equals(format)){
                bytes = SerialUtil.hexToByte(message);
                bytes = Crc16Modbus.merge(bytes);
            }

            output = serialPort.getOutputStream();
            output.write(bytes);
            output.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (null != output){
                try {
                    output.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143

5.RS485串口监听

package com.serial.demo.config;

import com.serial.demo.socket.SerialWebSocket;
import com.serial.demo.util.Crc16Modbus;
import com.serial.demo.util.SerialUtil;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @author
 * @date 2023-07-03 23:21
 * @since 1.8
 */
public class Serial485Listener implements SerialPortEventListener {

    InputStream inputStream;
    OutputStream outputStream;
    String format;

    public Serial485Listener(InputStream input, OutputStream output, String format){
        inputStream = input;
        outputStream = output;
        this.format = format;
    }

    @Override
    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
            case SerialPortEvent.BI:
            case SerialPortEvent.OE:
            case SerialPortEvent.FE:
            case SerialPortEvent.PE:
            case SerialPortEvent.CD:
            case SerialPortEvent.CTS:
            case SerialPortEvent.DSR:
            case SerialPortEvent.RI:
            case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                break;
            case SerialPortEvent.DATA_AVAILABLE:
                // 当有可用数据时读取数据
                byte[] readBuffer = null;
                int availableBytes = 0;
                try {
                    availableBytes = inputStream.available();
                    while (availableBytes > 0) {
                        readBuffer = SerialUtil.readFromPort(inputStream);
                        String needData = printHexString(readBuffer);
                        SerialWebSocket.broadcast(needData);
                        availableBytes = inputStream.available();
                    }
                } catch (IOException e) {
                }
            default:
                break;
        }
    }

    /**
     * 转为 16 进制字符串
     * @param b
     * @return
     */
    public static String printHexString(byte[] b) {
        return Crc16Modbus.byteTo16String(b);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

三.UI代码

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Serial Communication</title>
        <meta name="robots" content="all" />
        <meta name="keywords" content="Serial Communication" />
        <meta name="description" content="Serial Communication" />
        <style>
            .btn-group{
                display: inline-block;
            }
            .left {
                width: 300px;
                height: 500px;
                float: left;
            }
            .right {
                width: calc(100% - 330px);
                height: 500px;
                margin-left: 300px;
            }

            .configSelect{
                width: 100%;
            }

            .bk{
                border-color: #D5DBDB;
                background-color: #1E1E1E;
                margin: 1px;
            }

            .bkw{
                border-color: #D5DBDB;
                background-color: #1E1E1E;
                margin: 1px;
                border-left: none;
                border-right: none;
                border-bottom: none;
                border-top: none;
            }

            select{
                background-color: #3C3C3C;
                color: white;
            }

            button{
                background-color: #3C3C3C;
                color: white;
                cursor: pointer;
            }


        </style>
    </head>
    <body style="background-color: #1E1E1E;color: white;">
        <div>
            <div class="left">
                <fieldset class="bk">
                    <legend>串口配置</legend>
                    <div style="background: #1E1E1E;width: 260px;">
                        <table>
                            <tr><th style="width:100px">端口</th><td style="width:160px"><select class="configSelect" id="serialPortList"></select></td></tr>
                            <tr><th>波特率</th><td><select class="configSelect" id="bitRate"></select></td></tr>
                            <tr><th>数据位</th><td><select class="configSelect" id="dataBit"></select></td></tr>
                            <tr><th>停止位</th><td><select class="configSelect" id="stopBit"></select></td></tr>
                            <tr><th>校验位</th><td><select class="configSelect" id="checkBit"></select></td></tr>
                            <tr><th>操作</th><td><button type="button" class="btn btn-default" style="width:100%" id="switchSerialPort" >打开串口</button></td></tr>
                        </table>

                    </div>
                </fieldset>
                <fieldset class="bk">
                    <legend>RS485</legend>
                    <div>
                        <table>
                            <tr><th style="width:100px">RS485</th><td style="width:160px"><input type="checkbox" id="isRS485"/></td></tr>
                            <!-- <tr><th>DTR</th><td><input type="checkbox" id="isDTR"/></td></tr>
                            <tr><th>RTS</th><td><input type="checkbox" id="isRTS"/></td></tr> -->
                        </table>
                    </div>
                </fieldset>
                <fieldset class="bk">
                    <legend>接收区设置</legend>
                    <div>
                        <table>
                            <tr><th style="width:100px">数据格式</th><td style="width:160px"><select class="configSelect" id="receiveDataType"></select></td></tr>
                            <tr><th>停止显示</th><td><input type="checkbox" id="stopShow"/></td></tr>
                            <tr><th></th><td><button type="button" class="btn btn-default" style="width:100%" id="clearReceiveData" >清空接收区</button></td></tr>
                            <tr><th></th><td><button type="button" class="btn btn-default" style="width:100%" id="saveToFile" >保存到文件</button></td></tr>
                        </table>
                    </div>
                </fieldset>
                
                

            </div>
            <div class="right">
                <fieldset class="bkw">
                    <legend>WebSocket</legend>
                        <input type='text' value='ws://localhost:8080/websocket/test-0' class="form-control" style='width:390px;display:inline' id='wsaddr' />
                        <div class="btn-group" >
                            <button type="button" class="btn btn-default" onclick='addsocket();'>连接</button>
                            <button type="button" class="btn btn-default" onclick='closesocket();'>断开</button>
                            <button type="button" class="btn btn-default" onclick='$("#wsaddr").val("")' style="display:none">清空</button>
                        </div>
                        <div class="row">
                            <div id="output" style="border:1px solid #ccc;height:390px;overflow: auto;margin: 20px 0;background: #4B4B4B;"></div>
                            <div style="display:none">
                                <input type="text" id='message' class="form-control" style='width:810px' placeholder="待发信息" onkeydown="en(event);">
                                <span class="input-group-btn">
                                    <button class="btn btn-default" type="button" onclick="doSend();">发送</button>
                                </span>
                            </div>
                        </div>
                </fieldset>    
                
                
            </div>
        </div>
        <div>
            <div class="left" style="height: 160px;">
                <fieldset class="bk">
                    <legend>发送区设置</legend>
                        <table>
                        <tr><th style="width:100px">自动发送</th><td style="width:160px"><input id="autoSendTimer" value="1000"/></td></tr>
                        <tr><th>数据格式</th><td><select class="configSelect" id="dataType"></select></td></tr>
                        <tr><th>类型</th><td><select class="configSelect" id="sendType"></select></td></tr>
                        <tr><th>发送</th><td><button type="button" class="btn btn-default" style="width:100%" id="sendData" >发送数据</button></td></tr>
                    </table>
                </fieldset>
            </div>
            
            <div class="right" style="height: 160px;">
                <fieldset class="bkw">
                    <legend></legend>
                    <textarea id="sendMessages" style="width:100%;height:133px;margin-top: 5px;background-color: #4B4B4B;color: white;"></textarea>
                </fieldset>
                
            </div>
        </div>

        <div>
            <div class="left" style="height: 90px;">
                <fieldset class="bk">
                    <legend>类型转换</legend>
                        <table>
                        <tr><th style="width:90px"></th><td style="width:160px"><button type="button" class="btn btn-default" style="width:100%" id="toHex" >字符串转HEX</button></td></tr>
                        <tr><th></th><td><button type="button" class="btn btn-default" style="width:100%" id="toStr" >HEX转字符串</button></td></tr>
                        </tr>
                    </table>
                </fieldset>
            </div>
            
            <div class="right" style="height: 90px;">
                <table style="width:100%">
                    <tr style="width:100%">
                        <th style="width:50%">
                            <fieldset class="bkw">
                                <legend>STR</legend>
                                <textarea id="strShow" style="width:100%;height:54px;margin-top: 5px;background-color: #4B4B4B;color: white;"></textarea>
                            </fieldset>
                        </th>
                        <th style="width:50%">
                            <fieldset class="bkw">
                                <legend>HEX</legend>
                                <textarea id="hexShow" style="width:100%;height:54px;margin-top: 5px;background-color: #4B4B4B;color: white;"></textarea>
                            </fieldset>
                        </th>
                    </tr>
                </table>
            </div>
        </div>
    </body>     
        
        <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
        <script language="javascript" type="text/javascript">

            function formatDate(now) {
                var year = now.getFullYear();
                var month = now.getMonth() + 1;
                var date = now.getDate();
                var hour = now.getHours();
                var minute = now.getMinutes();
                var second = now.getSeconds();
                return year + "-" + (month = month < 10 ? ("0" + month) : month) + "-" + (date = date < 10 ? ("0" + date) : date) +
                    " " + (hour = hour < 10 ? ("0" + hour) : hour) + ":" + (minute = minute < 10 ? ("0" + minute) : minute) + ":" + (
                        second = second < 10 ? ("0" + second) : second);
            }
            var output;
            var websocket;
 
            function addsocket() {
                $("#output").text("");
                var wsaddr = $("#wsaddr").val();
                if (wsaddr == '') {
                    alert("set websocket address!");
                    return false;
                }
                StartWebSocket(wsaddr);
            }
 
            function closesocket() {
                websocket.close();
            }
 
            function StartWebSocket(wsUri) {
                websocket = new WebSocket(wsUri);
                websocket.onopen = function(evt) {
                    onOpen(evt)
                };
                websocket.onclose = function(evt) {
                    onClose(evt)
                };
                websocket.onmessage = function(evt) {
                    onMessage(evt)
                };
                websocket.onerror = function(evt) {
                    onError(evt)
                };
            }
 
            function onOpen(evt) {
                // writeToScreen("<span style='color:red'>连接成功,现在你可以发送信息啦!!!</span>");
            }
 
            function onClose(evt) {
                // writeToScreen("<span style='color:red'>websocket连接已断开!!!</span>");
                websocket.close();
            }
 
            function onMessage(evt) {
                var stopShow = $("#stopShow").prop('checked');
                if (!stopShow) {
                    writeToScreen('<span style="color:blue">' + formatDate(new Date()) + ' : </span><span class="bubble">' + evt.data + '</span>');
                }
            }
 
            function onError(evt) {
                writeToScreen('<span style="color: red;">error:</span> ' + evt.data);
            }
 
            function doSend() {
                var message = $("#message").val();
                if (message == '') {
                    alert("Please input message");
                    $("#message").focus();
                    return false;
                }
                if (typeof websocket === "undefined") {
                    alert("websocket is not connected");
                    return false;
                }
                if (websocket.readyState == 3) {
                    alert("websocket is closed,please reconnected");
                    return false;
                }
                console.log(websocket);
                $("#message").val('');
                writeToScreen('<span style="color:green">你发送的信息&nbsp;' + formatDate(new Date()) + '</span><br/>' + message);
                websocket.send(message);
            }
 
            function writeToScreen(message) {
                var div = "<div class='newmessage'>" + message + "</div>";
                var d = $("#output");
                var d = d[0];
                var doScroll = d.scrollTop == d.scrollHeight - d.clientHeight;
                $("#output").append(div);
                if (doScroll) {
                    d.scrollTop = d.scrollHeight - d.clientHeight;
                }
            }
 
 
            function en(event) {
                var evt = evt ? evt : (window.event ? window.event : null);
                if (evt.keyCode == 13) {
                    doSend()
                }
            }


            var http = "http://localhost:8080/serial";
            var httpSerial = "http://localhost:8080/serial/232";


            function getSerialPortList(){
                $("#serialPortList").html('');
                $.ajax({url:http + "/getSerialPortList",success:function(result){
                    for (var i in result) {
                        $("#serialPortList").append("<option value='" + result[i] + "''>" + result[i] + "</option>");
                    }
                }});
            }

            function getBitRate() {
                var list = [2400,4800,9600,19200,38400,57600,115200,128000,230400,256000,460800]
                for (var i in list) {
                    $("#bitRate").append("<option value='" + list[i] + "''>" + list[i] + "</option>");
                }

                var list = [5,6,7,8]
                for (var i in list) {
                    $("#dataBit").append("<option value='" + list[i] + "''>" + list[i] + "</option>");
                }

                var list = [1,2]
                for (var i in list) {
                    $("#stopBit").append("<option value='" + list[i] + "''>" + list[i] + "</option>");
                }

                var list = ['无','奇','偶']
                for (var i in list) {
                    $("#checkBit").append("<option value='" + list[i] + "''>" + list[i] + "</option>");
                }

                var list = ['ASCII','HEX']
                for (var i in list) {
                    $("#dataType").append("<option value='" + list[i] + "''>" + list[i] + "</option>");
                    $("#receiveDataType").append("<option value='" + list[i] + "''>" + list[i] + "</option>");
                }

                var list = ['发送新行','自动发送']
                for (var i in list) {
                    $("#sendType").append("<option value='" + list[i] + "''>" + list[i] + "</option>");
                }

            }

            function updateConfig(type){
                if (type == 1) {
                    $("#serialPortList").attr("disabled","disabled")
                    $("#bitRate").attr("disabled","disabled")
                    $("#dataBit").attr("disabled","disabled")
                    $("#stopBit").attr("disabled","disabled")
                    $("#checkBit").attr("disabled","disabled")
                    $("#receiveDataType").attr("disabled","disabled")
                    $("#isRS485").attr("disabled","disabled")
                } else {
                    $("#serialPortList").removeAttr("disabled")
                    $("#bitRate").removeAttr("disabled")
                    $("#dataBit").removeAttr("disabled")
                    $("#stopBit").removeAttr("disabled")
                    $("#checkBit").removeAttr("disabled")
                    var isRS485 = $("#isRS485").prop('checked');
                    if (!isRS485) {
                        $("#receiveDataType").removeAttr("disabled")
                    }
                    $("#isRS485").removeAttr("disabled")
                }
            }

            function init() {
                addsocket();
                getSerialPortList();
                getBitRate();
                $("#bitRate").val(9600)
                $("#dataBit").val(8)
            }

            init();

            $("#switchSerialPort").click(function(){

                var status = $("#switchSerialPort").html();
                if (status == '关闭串口') {
                    $.ajax({
                        url:httpSerial + "/close/" +  $("#serialPortList").val(),
                        success:function(result){
                            $("#switchSerialPort").html('打开串口')
                            $("#switchSerialPort").css("background-color","");
                            updateConfig(0)
                        },
                        error:function(result){

                        }
                    });
                } else {
                    var portId = $("#serialPortList").val();
                    var bitRate = $("#bitRate").val();
                    var dataBit = $("#dataBit").val();
                    var stopBit = $("#stopBit").val();
                    var checkBit = $("#checkBit").val();
                    var format =  $("#receiveDataType").val();
                    var serialData = {"portId": portId, "bitRate": bitRate, "dataBit": dataBit, "stopBit": stopBit, "checkBit": checkBit,"format":format};
                    // console.log(serialData)
                    $.ajax({
                        url:httpSerial + "/open" ,
                        type: "post",
                        contentType : "application/json",
                        dataType: "json",
                        data: JSON.stringify(serialData),
                        success:function(result){
                            if (result) {
                                $("#switchSerialPort").html('关闭串口')
                                $("#switchSerialPort").css("background-color","#02A1DD");
                                updateConfig(1)
                            }
                        },
                        error:function(result){

                        }
                    });

                }
            });

            $("#sendData").click(function(){
                var portId = $("#serialPortList").val();
                var data = $("#sendMessages").val();
                var dataType = $("#dataType").val();

                if (data == '') {
                    alert('Please input message .')
                    $("#sendMessages").focus();
                    return;
                }

                $.ajax({
                    url:httpSerial + "/send/" + portId + "/" + dataType + "/" + data,
                    success:function(result){
                        var sendType = $("#sendType").val();
                        if (sendType == '发送新行') {
                            $("#sendMessages").val('');
                        }
                    },
                    error:function(result){

                    }
                });
            });    

            var timer 
            $("#sendType").change(()=>{
                var data = $("#sendMessages").val();
                if (data == '') {
                    clearInterval(timer)
                    $("#sendType").val("发送新行");
                    $("#sendType").attr("disabled","disabled")
                }

                var sendType = $("#sendType").val();
                if (sendType == '自动发送') {
                    $("#sendData").attr("disabled","disabled")
                    $("#sendData").css("background-color","#02A1DD");
                    var interval = $("#autoSendTimer").val()
                    timer = setInterval(function(){
                        $("#sendData").trigger("click")
                    },interval);
                } else {
                    clearInterval(timer)
                    $("#sendData").removeAttr("disabled")
                    $("#sendData").css("background-color","");
                }
            })

            $("#sendType").attr("disabled","disabled")
            $("#sendMessages").keyup(()=>{
                var data = $("#sendMessages").val();
                if (data == '') {
                    clearInterval(timer)
                    $("#sendType").val("发送新行");
                    $("#sendType").attr("disabled","disabled")
                } else {
                    $("#sendType").removeAttr("disabled")
                }
            })

            $("#clearReceiveData").click(function(){
                $("#output").text("");
            });    

            $("#saveToFile").click(function(){
                let a = document.createElement('a')
                let url = window.URL.createObjectURL(
                  new Blob([$("#output").text()], {
                    type: ''
                  })
                )
                a.href = url
                a.download = 'ReveiveData' + Date.parse(new Date()) + '.log'
                a.click()
                window.URL.revokeObjectURL(url)
            }); 


            $("#toHex").click(function(){
                var str = $("#strShow").val();
                $.ajax({
                    url:http + "/toHex?str=" + str,
                    success:function(result){
                        $("#hexShow").val(result)
                    },
                    error:function(result){

                    }
                });
            })

            $("#toStr").click(function(){
                var hex = $("#hexShow").val();
                $.ajax({
                    url:http + "/toStr?hex=" + hex,
                    success:function(result){
                        $("#strShow").val(result)
                    },
                    error:function(result){

                    }
                });
            })

            $("#isRS485").click(function(){
                var isRS485 = $("#isRS485").prop('checked');
                if (isRS485) {
                    $("#dataType").val("HEX");
                    $("#receiveDataType").val("HEX");
                    $("#dataType").attr("disabled","disabled")
                    $("#receiveDataType").attr("disabled","disabled")
                    httpSerial = "http://localhost:8080/serial/485";
                } else {
                    $("#dataType").removeAttr("disabled")
                    $("#receiveDataType").removeAttr("disabled")
                    httpSerial = "http://localhost:8080/serial/232";
                }
            })
 
        </script>
 
</html>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536

四.测试效果

1.串口通信

ASCII 收数
  • 1

在这里插入图片描述

ASCII发数
  • 1

在这里插入图片描述

切换为自动发送后即自动发送当前数据
  • 1

在这里插入图片描述

Hex 收数
  • 1

在这里插入图片描述

Hex 发数
  • 1

在这里插入图片描述

2.CRC16通信

Hex 收数
  • 1

在这里插入图片描述

Hex 发数
  • 1

在这里插入图片描述

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号