Springboot+jSerialComm +JDK17+Rs232协议

注意:1.JSerialComm库的发送和响应数据有两种方式,第一是监听器,第二是轮询模式。此文章使用的是第二种方式,即每处理一个事件,都需要调用响应和发送的方法。

            2.JSerialComm库发送和响应数据不适用inputStream和OutPutStream流,它是通过WriteBytes和readBytes来发送和响应数据的。

1.POM文件的导入

<dependency>
   <groupId>com.fazecast</groupId>
    <artifactId>jSerialComm</artifactId>
    <version>2.10.5</version>
</dependency>

2.写一个实体类

@Component
@AllArgsConstructor
@NoArgsConstructor
@Data
public class SerialEntity {
    @ApiModelProperty(value = "端口名称")
    private String PortName = "COM9";
    @ApiModelProperty(value = "请求指令")
    private String powerOffCommand;
    private int BAUDRATE = 2400;
    private int DATALENGTH = 8;
    private int STOPBIT = 1;
    private String PARITY = "NONE"
@ApiModelProperty(name="过载故障")
private char Overloadwarning;
@ApiModelProperty(name="过温故障")
private char Overtemperature;
@ApiModelProperty(name="电池过低故障")
private char Batterylow;

@ApiModelProperty(name="省电模式名称")
private String Batterymode;
@ApiModelProperty(name="剩余电量")
private String BatteryVoltage;

@ApiModelProperty(name="经纬度")
private String Latitude;
@ApiModelProperty(name="经纬度")
private String longitude;
private int BAUDRATE1 = 115200;
}

3.common工具类

package com.ruoyi.equipment.serialportcommon;
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";
    }

4. SerialUtil配置类        

package com.ruoyi.equipment.serialportcommon;
import com.fazecast.jSerialComm.SerialPort;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class SerialUtil {
    private SerialPort serialPort;
    /**
     * 转为 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.NO_PARITY;
            } else if (common.ODD.equals(checkBit)) {
                return SerialPort.ODD_PARITY;
            } else if (common.EVEN.equals(checkBit)) {
                return SerialPort.EVEN_PARITY;
            } else {
                return SerialPort.NO_PARITY;
            }

    }



    public static byte[] readFromPort(SerialPort serialPort) {

        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0);
            byte[] bytes = {};
            try {
                // 缓冲区大小为一个字节
                byte[] readBuffer = new byte[1];
                int bytesNum = serialPort.readBytes(readBuffer,readBuffer.length);
                System.out.println("readBytes:"+bytesNum);
                while (bytesNum > 0) {
                    bytes = concat(bytes, readBuffer);
                    bytesNum = serialPort.readBytes(readBuffer,readBuffer.length);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bytes;
        }


    此方法是基于经纬度接口接受数据的方法
        
    public static byte[] readFromPort1(SerialPort serialPort) {

        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 1000, 0);
        byte[] bytes = {};
        try {
            // 缓冲区大小为一个字节
            byte[] readBuffer = new byte[1];
            int bytesNum = serialPort.readBytes(readBuffer,readBuffer.length);
            System.out.println("readBytes:"+bytesNum);
            for (int i =0 ; i < 64;i++) {
              bytes = concat(bytes, readBuffer);
              bytesNum = serialPort.readBytes(readBuffer, readBuffer.length);
          }
        } catch (Exception 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;
    }

}

5.RsConfig功能实现类  

代码阐述:

      (1):打开串口并且设置对应的波特率等参

      (2):关闭串口

      (3):发送命令

      (4):响应数据:readdata方法将获取到的ASCII码转为为字符串,取第7,8,12个字符

                             readdata1方法将获取到的ASCII码转为字符串,并按空格分割保存在数组,取                                 第1个数组

                             readdata2同上

                             readdata4方法获取经纬度信息,注意经纬度接收到的数据是一直显示,所以 

                             获取到你想要的数据时就关闭串口,再对获取到的数据进行处理。      

@Slf4j
@Component
public class Rs232Config {
    // 定义一个常量,用于表示延迟时间,单位为毫秒
    /*  private static final int DELAY_TIME = 1000;*/
 
    private SerialUtil serialUtil;

    /**
     * 缓存端口实例
     * 使用ConcurrentHashMap以支持并发访问
     */
    private Map<String, SerialPort> serialPortMap = new ConcurrentHashMap<>(16);

    public boolean closePort(SerialEntity serial) {

        // 从map中移除并获取对应的SerialPort实例
        SerialPort serialPort = serialPortMap.remove(serial.getPortName());
        if (null != serialPort) {
            // 关闭串口
            serialPort.closePort();
            System.out.println("关闭串口成功");
        }
        return false;
    }
    public boolean openPort(SerialEntity serial) {
        if (serial == null) {
            log.error("serial is null");
            return false;
        }
        String portName = serial.getPortName();
        System.out.println("端口号: " + portName);

        // 尝试打开串口
        SerialPort serialPort = SerialPort.getCommPort(portName);
        System.out.println("串口一打开"+serialPort);
        if (serialPort.isOpen()) {
            System.err.println("Error: Port is already open");
            return false;
        }
        // 从serial对象中获取串口参数(这里假设serial对象包含了这些参数)
        int baudRate = serial.getBAUDRATE();
        int dataBits = serial.getDATALENGTH();
        int stopBits = serial.getSTOPBIT(); // 假设有一个转换方法
        int parity = SerialUtil.getParity(serial.getPARITY());// 假设有一个转换方法
        // 打开串口并设置参数
        serialPort.setComPortParameters(baudRate, dataBits, stopBits, parity);
        serialPortMap.put(portName, serialPort);
        return true;
    }
public boolean sendData(String portId, String message) {
    // 假设 serialPortMap 是一个存储了 SerialPort 对象的 Map
    SerialPort serialPort = serialPortMap.get(portId);
    System.out.println("serialPort: " + serialPort);
    // 检查串口是否已经打开
    if (serialPort == null || !serialPort.openPort()) {
        throw new RuntimeException("Serial port not found for ID: " + portId);
    }
    try {
        byte[] bytes = message.getBytes(StandardCharsets.UTF_8);
        // 使用 JSerialComm 的 writeBytes 方法发送数据
        int numBytesWritten = serialPort.writeBytes(bytes, bytes.length);
        System.out.println(numBytesWritten);
        // 检查是否成功发送了所有字节
        if (numBytesWritten == bytes.length) {
        } else {
            // 没有成功发送所有字节,可以根据需要处理这种情况
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return true;
}public SerialEntity readData(String portId) {
    SerialEntity serial = new SerialEntity();
    SerialPort serialPort = serialPortMap.get(portId);
    byte[] bytes =  serialUtil.readFromPort(serialPort);
        System.out.println("bytes" + bytes);
        System.out.println("bytes: ");
        // 创建一个StringBuilder来构建字符串,这比使用String连接更高效
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            // 直接将字节转换为字符(假设是ASCII编码)
            char asciiChar = (char) b;
            sb.append(asciiChar);
    }
    System.out.println("sb: " + sb);
    // 将StringBuilder转换为String并打印
    String resultString = sb.toString();
    System.out.println("Received string: " + resultString); 
     String[] strArray = resultString.split("\\s+");
    char seven = resultString.charAt(7);
    char eight = resultString.charAt(8);
    char tweleve = resultString.charAt(12);
    serial.setBatterylow(seven);
    serial.setOverloadwarning(eight);
    serial.setOvertemperature(tweleve);
    System.out.println(serial.getBatterylow());
    System.out.println(serial.getOverloadwarning());
    System.out.println(serial.getOvertemperature());

    return serial;
}
public SerialEntity readData1(String portId) {
    SerialEntity serial = new SerialEntity();
    SerialPort serialPort = serialPortMap.get(portId);
    byte[] bytes =  serialUtil.readFromPort(serialPort);
    System.out.println("bytes" + bytes);
    System.out.println("bytes: ");
    // 创建一个StringBuilder来构建字符串,这比使用String连接更高效
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        // 直接将字节转换为字符(假设是ASCII编码)
        char asciiChar = (char) b;
        sb.append(asciiChar);
    }
        // 将StringBuilder转换为String并打印
        String resultString = sb.toString();
        System.out.println("Received string: " + resultString); 
        String[] strArray = resultString.split("\\s+");
        // 检查数组长度以确保有足够的部分
        if (strArray.length > 1) {
            // 获取第二个部分(索引为1)
            String firstPart = strArray[2];
            serial.setBatteryVoltage(firstPart);
            System.out.println(serial.getBatteryVoltage());
        } else {
            System.out.println("Not enough parts in the string.");
        }

    return serial;
}

public SerialEntity readData2(String portId) {
    SerialEntity serial = new SerialEntity();
    SerialPort serialPort = serialPortMap.get(portId);
    byte[] bytes =  serialUtil.readFromPort(serialPort);
    System.out.println("bytes" + bytes);
    System.out.println("bytes: ");
    // 创建一个StringBuilder来构建字符串,这比使用String连接更高效
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        // 直接将字节转换为字符(假设是ASCII编码)
        char asciiChar = (char) b;
        sb.append(asciiChar);
    }
        // 将StringBuilder转换为String并打印
        String resultString = sb.toString();
        System.out.println("Received string: " + resultString); 
        String[] strArray = resultString.split("\\s+");
        // 检查数组长度以确保有足够的部分
        if (strArray.length > 0) {
            // 获取第二个部分(索引为1)
            String firstPart = strArray[0];
            serial.setBatterymode(firstPart);
            System.out.println(serial.getBatterymode());
        } else {
            System.out.println("Not enough parts in the string.");
        }

    return serial;
} 
public SerialEntity readData4(String portId) {
        SerialEntity serial = new SerialEntity();
        SerialPort serialPort = serialPortMap.get(portId);
        byte[] bytes =  serialUtil.readFromPort1(serialPort);
        System.out.println("bytes" + bytes);
        System.out.println("bytes: ");
        // 创建一个StringBuilder来构建字符串,这比使用String连接更高效
        StringBuilder sb = new StringBuilder();
       serialPort.closePort();
        for (byte b : bytes) {
            // 直接将字节转换为字符(假设是ASCII编码)
            char asciiChar = (char) b;
            sb.append(asciiChar);
        }
        // 将StringBuilder转换为String并打印
        String resultString = sb.toString();
        System.out.println("Received string: " + resultString);         String[] strArray = resultString.split(",");
        System.out.println("strArray: " + strArray);
        // 检查数组长度以确保有足够的部分
        if (strArray.length > 1) {
            // 获取第二个部分(索引为1)
            String firstPart = strArray[3];
            serial.setLatitude(firstPart);
            String twoPart = strArray[5];
            serial.setLongitude(twoPart);
          /*  String two = strArray[6];
            serial.setBatteryVoltage(two);
*/
            System.out.println(serial.getLatitude());
            System.out.println(serial.getLongitude());
        } else {
            System.out.println("Not enough parts in the string.");
        }
        return serial;
    }

}

6.UpsManager类,逻辑处理类

package com.ruoyi.equipment.config;
import com.ruoyi.equipment.entity.SerialEntity;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class UPSManager {
    // 使用Spring的自动装配特性注入Rs232Config的实例
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    @Autowired
    private Rs232Config rs232Config;

    // 关闭UPS连接的端口,并发送关机命令
    public boolean closePort(SerialEntity serial) {
        try {
            if (rs232Config != null) {
                rs232Config.openPort(serial);
                boolean closeport = rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
                System.out.println("closeport" + closeport);
                boolean portclosed = rs232Config.closePort(serial);
                if (portclosed) {
                    System.out.println("已发送指令");
                }
            } else {
                System.out.println("rs232Config is null");
                return false;
            }
        } catch (RuntimeException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }


     public boolean openPort(SerialEntity serial) {
     
        if (serial == null) {
            System.out.println("SerialEntity 对象为空,无法打开端口");
            return false;
        }
        if (rs232Config == null) {
            System.out.println("rs232Config 对象为空,无法操作端口" + rs232Config);
            return false;
        }
        boolean portOpened = rs232Config.openPort(serial);
        if (portOpened) {
            rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
            System.out.println("已发送指令");
        } else {
            System.out.println("端口未打开");
            return false;
        }
        rs232Config.closePort(serial);
        return true;
    }

    // 获取UPS的当前状态
     public SerialEntity CurrentStatus(SerialEntity serial) {
         SerialEntity SS = new SerialEntity();

            rs232Config.openPort(serial);
           boolean m=  rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
            System.out.println("发送命令成功"+m);
            System.out.println("222222");
            System.out.println( rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand()));

            SS =  rs232Config.readData(serial.getPortName());
            rs232Config.closePort(serial);
        return SS;
    }
         public SerialEntity ReaminBatteryTime (SerialEntity serial) {
        SerialEntity SS = new SerialEntity();
            // 设置UPS电池剩余时间请求命令
             /* statusRequestCommand = "F\r\n";*/
            try {
                Thread.sleep(1200);
                rs232Config.openPort(serial);
                rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
                SS = rs232Config.readData1(serial.getPortName());
                rs232Config.closePort(serial);
                // 这里只是模拟了一个成功的请求,并返回true
            } catch (InterruptedException e) {
                System.out.println("线程被中断");
            }
            return SS;
        }
        public SerialEntity CurrentMode (SerialEntity serial) {
            SerialEntity SS = new SerialEntity();
            try {
                Thread.sleep(1800);
                // 设置UPS当前模式请求命令
                rs232Config.openPort(serial);
                rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
                SS = rs232Config.readData2(serial.getPortName());
                rs232Config.closePort(serial);
                // 这里只是模拟了一个成功的请求,并返回true
                return SS;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
//设置几分钟后 打开串口
        public boolean openPortAfterMinutes (SerialEntity serial){
            scheduler.schedule(() -> {
                try {
                    /* UPSManager upsManager = new UPSManager(); */
                    if (serial == null) {
                        System.out.println("SerialEntity 对象为空,无法打开端口");
                    }
                    if (rs232Config == null) {
                        System.out.println("rs232Config 对象为空,无法操作端口" + rs232Config);
                        // 注意:这里也不需要 return false
                    }
                    boolean portOpened = rs232Config.openPort(serial);
                    if (portOpened) {
                        rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
                        System.out.println("已发送指令");
                    } else {
                        System.out.println("端口未打开");
                        // 注意:这里也不需要 return false
                    }
                    rs232Config.closePort(serial);
                    // 注意:其他代码逻辑...

                    // 注意:这里不需要 return true,因为 Runnable 没有返回值
                } catch (Exception e) {
                    e.printStackTrace(); // 处理异常
                }
            }, serial.getMinutes(), TimeUnit.MINUTES);
            // 返回 true 表示操作已成功安排(但不一定已完成)
            return true;
        }

       //设置几分钟后关闭串口
        public boolean closePortAfterMinutes (SerialEntity serial ){ 
            scheduler.schedule(() -> {
                try {
                    /*  UPSManager upsManager = new UPSManager();*/
                    try {
                        if (rs232Config != null) {
                            rs232Config.openPort(serial);
                            boolean closeport = rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
                            System.out.println("closeport" + closeport);
                            boolean portclosed = rs232Config.closePort(serial);
                            if (portclosed) {
                                System.out.println("已发送指令");
                            }
                        } else {
                            System.out.println("rs232Config is null");
                        }
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                    }
                    // 在 minutes 分钟后执行 closePort
                } catch (Exception e) {
                    e.printStackTrace(); // 处理异常
                }
            }, serial.getMinutes(), TimeUnit.MINUTES);
            // 返回 true 表示操作已成功安排(但不一定已完成)
            return true;
        }

    public SerialEntity latitude(SerialEntity serial) {
        SerialEntity SS = new SerialEntity();
        rs232Config.openPort1(serial);
        boolean m=  rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand());
        System.out.println("发送命令成功"+m);
        System.out.println("222222");
        System.out.println( rs232Config.sendData(serial.getPortName(), serial.getPowerOffCommand()));
        SS =  rs232Config.readData4(serial.getPortName());
        rs232Config.closePort(serial);
        return SS;
    }
}


7.Rs232Controller请求处理类

package com.ruoyi.web.controller.equipment;
import com.ruoyi.common.utils.ResponseObj;
import com.ruoyi.equipment.config.UPSManager;
import com.ruoyi.equipment.entity.SerialEntity;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
/*它用于处理与RS-232串口通信相关的HTTP请求*/


@RestController
@RequestMapping("/UPS/RS232")
@Api(tags = "UPS接入企业端")
public class Rs232Controller {

    @Autowired
    UPSManager upsManager;
    
    @PostMapping("/open")
    public ResponseObj open(@RequestBody SerialEntity serial) {
        return ResponseObj.success(upsManager.openPort(serial));
    }
    @PostMapping("/close")
    public ResponseObj close(@RequestBody SerialEntity serial) {
        /*    serialController.getSerialPortList();*/
        return ResponseObj.success(upsManager.closePort(serial));
    }

    @PostMapping("/CurrentStatus")
    public ResponseObj CurrentStatus(@RequestBody SerialEntity serial) throws IOException{
        return ResponseObj.success(upsManager.CurrentStatus(serial));
    }
    @PostMapping("/ReaminBatteryTime")
    public ResponseObj ReaminBatteryTime(@RequestBody SerialEntity serial) {
        return ResponseObj.success(upsManager.ReaminBatteryTime(serial));
    }
    @PostMapping("/CurrentMode")
    public ResponseObj CurrentMode(@RequestBody SerialEntity serial) {
        return ResponseObj.success(upsManager.CurrentMode(serial));
    }
    @PostMapping("/latitude")
    public ResponseObj latitude(@RequestBody SerialEntity serial) {
        return ResponseObj.success(upsManager.latitude(serial));
    }
 
    @PostMapping("/time")
    public ResponseObj Time(@RequestBody SerialEntity serial) {
        return ResponseObj.success(upsManager.openPortAfterMinutes(serial));
    }
    @PostMapping("/time2")
    public ResponseObj Time2(@RequestBody SerialEntity serial) {
        return ResponseObj.success( upsManager.closePortAfterMinutes(serial));
    }

}

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值