最近花了好長時間去研究~上代碼
1 package test; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 import java.nio.charset.StandardCharsets; 7 import java.util.Enumeration; 8 import java.util.TooManyListenersException; 9 10 import com.serotonin.io.serial.SerialPortException; 11 12 import gnu.io.CommPort; 13 import gnu.io.CommPortIdentifier; 14 import gnu.io.PortInUseException; 15 import gnu.io.SerialPort; 16 import gnu.io.SerialPortEvent; 17 import gnu.io.SerialPortEventListener; 18 import gnu.io.UnsupportedCommOperationException; 19 20 public class SerialPortUtils implements SerialPortEventListener { 21 // 檢測系統中可用的通訊端口類 22 private CommPortIdentifier commPortId; 23 // 枚舉類型 24 private Enumeration<CommPortIdentifier> portList; 25 // RS232串口 26 private SerialPort serialPort; 27 // 輸入流 28 private InputStream inputStream; 29 // 輸出流 30 private OutputStream outputStream; 31 // 保存串口返回信息 32 private String data; 33 // 保存串口返回信息十六進制 34 private String dataHex; 35 36 /** 37 * 初始化串口 38 * 39 * @throws 40 * @author LinWenLi 41 * @date 2018年7月21日下午3:44:16 42 * @Description: TODO 43 * @param: paramConfig 存放串口連接必要參數的對象(會在下方給出類代碼) 44 * @return: void 45 */ 46 @SuppressWarnings("unchecked") 47 public void init() throws SerialPortException { 48 // 獲取系統中所有的通訊端口 49 portList = CommPortIdentifier.getPortIdentifiers(); 50 // 記錄是否含有指定串口 51 boolean isExsist = false; 52 // 循環通訊端口 53 while (portList.hasMoreElements()) { 54 commPortId = portList.nextElement(); 55 // 判斷是否是串口 56 if (commPortId.getPortType() == CommPortIdentifier.PORT_SERIAL) { 57 // 比較串口名稱是否是指定串口 58 if ("COM7".equals(commPortId.getName())) { 59 // 串口存在 60 isExsist = true; 61 // 打開串口 62 try { 63 // open:(應用程序名【隨意命名】,阻塞時等待的毫秒數) 64 serialPort = (SerialPort) commPortId.open(Object.class.getSimpleName(), 2000); 65 // 設置串口監聽 66 serialPort.addEventListener(this); 67 // 設置串口數據時間有效(可監聽) 68 serialPort.notifyOnDataAvailable(true); 69 // 設置串口通訊參數:波特率,數據位,停止位,校驗方式 70 serialPort.setSerialPortParams(9600, 8, 71 1, 0); 72 } catch (PortInUseException e) { 73 throw new SerialPortException("端口被占用"); 74 } catch (TooManyListenersException e) { 75 throw new SerialPortException("監聽器過多"); 76 } catch (UnsupportedCommOperationException e) { 77 throw new SerialPortException("不支持的COMM端口操作異常"); 78 } 79 // 結束循環 80 break; 81 } 82 } 83 } 84 // 若不存在該串口則拋出異常 85 if (!isExsist) { 86 throw new SerialPortException("不存在該串口!"); 87 } 88 } 89 90 /** 91 * 實現接口SerialPortEventListener中的方法 讀取從串口中接收的數據 92 */ 93 @Override 94 public void serialEvent(SerialPortEvent event) { 95 } 96 97 98 /** 99 * 讀取串口返回信息 100 * 101 * @author LinWenLi 102 * @date 2018年7月21日下午3:43:04 103 * @return: void 104 */ 105 public void readCommPort() throws SerialPortException { 106 } 107 108 /** 109 * 發送信息到串口 110 * 111 * @throws 112 * @author LinWenLi 113 * @date 2018年7月21日下午3:45:22 114 * @param: data 115 * @return: void 116 */ 117 public void sendComm(String data) throws SerialPortException { 118 byte[] writerBuffer = null; 119 try { 120 // writerBuffer = hexToByteArray(data); 121 writerBuffer = data.getBytes(StandardCharsets.UTF_8); 122 } catch (NumberFormatException e) { 123 throw new SerialPortException("命令格式錯誤!"); 124 } 125 try { 126 outputStream = serialPort.getOutputStream(); 127 outputStream.write(writerBuffer); 128 outputStream.flush(); 129 } catch (NullPointerException e) { 130 throw new SerialPortException("找不到串口。"); 131 } catch (IOException e) { 132 throw new SerialPortException("發送信息到串口時發生IO異常"); 133 } 134 } 135 136 /** 137 * 關閉串口 138 * 139 * @throws 140 * @author LinWenLi 141 * @date 2018年7月21日下午3:45:43 142 * @Description: 關閉串口 143 * @param: 144 * @return: void 145 */ 146 public void closeSerialPort() throws SerialPortException { 147 if (serialPort != null) { 148 serialPort.notifyOnDataAvailable(false); 149 serialPort.removeEventListener(); 150 if (inputStream != null) { 151 try { 152 inputStream.close(); 153 inputStream = null; 154 } catch (IOException e) { 155 throw new SerialPortException("關閉輸入流時發生IO異常"); 156 } 157 } 158 if (outputStream != null) { 159 try { 160 outputStream.close(); 161 outputStream = null; 162 } catch (IOException e) { 163 throw new SerialPortException("關閉輸出流時發生IO異常"); 164 } 165 } 166 serialPort.close(); 167 serialPort = null; 168 } 169 } 170 171 /** 172 * 十六進制串口返回值獲取 173 */ 174 public String getDataHex() { 175 String result = dataHex; 176 // 置空執行結果 177 dataHex = null; 178 // 返回執行結果 179 return result; 180 } 181 182 /** 183 * 串口返回值獲取 184 */ 185 public String getData() { 186 String result = data; 187 // 置空執行結果 188 data = null; 189 // 返回執行結果 190 return result; 191 } 192 193 /** 194 * Hex字符串轉byte 195 * 196 * @param inHex 待轉換的Hex字符串 197 * @return 轉換后的byte 198 */ 199 public static byte hexToByte(String inHex) { 200 return (byte) Integer.parseInt(inHex, 16); 201 } 202 203 /** 204 * hex字符串轉byte數組 205 * 206 * @param inHex 待轉換的Hex字符串 207 * @return 轉換后的byte數組結果 208 */ 209 public static byte[] hexToByteArray(String inHex) { 210 int hexlen = inHex.length(); 211 byte[] result; 212 if (hexlen % 2 == 1) { 213 // 奇數 214 hexlen++; 215 result = new byte[(hexlen / 2)]; 216 inHex = "0" + inHex; 217 } else { 218 // 偶數 219 result = new byte[(hexlen / 2)]; 220 } 221 int j = 0; 222 for (int i = 0; i < hexlen; i += 2) { 223 result[j] = hexToByte(inHex.substring(i, i + 2)); 224 j++; 225 } 226 return result; 227 } 228 229 /** 230 * 數組轉換成十六進制字符串 231 * 232 * @param bArray 233 * @return HexString 234 */ 235 public static final String bytesToHexString(byte[] bArray) { 236 StringBuffer sb = new StringBuffer(bArray.length); 237 String sTemp; 238 for (int i = 0; i < bArray.length; i++) { 239 sTemp = Integer.toHexString(0xFF & bArray[i]); 240 if (sTemp.length() < 2) 241 sb.append(0); 242 sb.append(sTemp.toUpperCase()); 243 } 244 return sb.toString(); 245 } 246 247 /** 248 * 打卡串口 249 * @param portName 串口名 250 * @param baudRate 波特率 251 * @param dataBits 數據位 252 * @param stopBits 停止位 253 * @param parity 校驗位 254 * @return 串口對象 255 */ 256 public static SerialPort open(String portName, Integer baudRate, Integer dataBits, 257 Integer stopBits, Integer parity) { 258 SerialPort result = null; 259 try { 260 // 通過端口名識別端口 261 CommPortIdentifier identifier = CommPortIdentifier.getPortIdentifier(portName); 262 // 打開端口,並給端口名字和一個timeout(打開操作的超時時間) 263 if(identifier.isCurrentlyOwned()) { 264 return null; 265 } 266 CommPort commPort = identifier.open(portName, 2000); 267 // 判斷是不是串口 268 if (commPort instanceof SerialPort) { 269 result = (SerialPort) commPort; 270 // 設置一下串口的波特率等參數 271 result.setSerialPortParams(baudRate, dataBits, stopBits, parity); 272 }else{ 273 } 274 } catch (Exception e) { 275 e.printStackTrace(); 276 } 277 return result; 278 } 279 280 /** 281 * 關閉串口 282 * @param serialPort 283 */ 284 public static void close(SerialPort serialPort) { 285 if (serialPort != null) { 286 serialPort.close(); 287 } 288 } 289 290 }
1 package modbus_rtu; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.io.OutputStream; 6 7 import org.slf4j.Logger; 8 import org.slf4j.LoggerFactory; 9 10 import com.serotonin.modbus4j.serial.SerialPortWrapper; 11 12 import gnu.io.SerialPort; 13 import test.SerialPortUtils; 14 15 /** 16 * 自定義串口封裝 17 * 18 * @author wusq 19 * @date 2021/1/3 20 */ 21 public class SerialPortWrapperImpl implements SerialPortWrapper { 22 23 private final Logger log = LoggerFactory.getLogger(this.getClass()); 24 25 /** 26 * 串口對象 27 */ 28 private SerialPort serialPort; 29 30 /** 31 * 串口 32 */ 33 private String port; 34 35 /** 36 * 波特率 37 */ 38 private Integer baudRate; 39 40 /** 41 * 數據位的位數,RTU是8位,ASCII是7位 42 */ 43 private Integer dataBits; 44 45 /** 46 * 停止位的位數,如果無奇偶校驗為2,有奇偶校驗為1 47 */ 48 private Integer stopBits; 49 50 /** 51 * 奇偶校驗位,無校驗是0,奇校驗是1,偶校驗是2 52 */ 53 private Integer parity; 54 55 /** 56 * 硬件之間輸入流應答控制 57 */ 58 private Integer flowControlIn; 59 60 /** 61 * 硬件之間輸出流應答控制 62 */ 63 private Integer flowControlOut; 64 65 public SerialPortWrapperImpl() { 66 super(); 67 } 68 69 public SerialPortWrapperImpl(String port, int baudRate, int dataBits, int stopBits, int parity, 70 int flowControlIn, int flowControlOut) { 71 this.port = port; 72 this.baudRate = baudRate; 73 this.dataBits = dataBits; 74 this.stopBits = stopBits; 75 this.parity = parity; 76 this.flowControlIn = flowControlIn; 77 this.flowControlOut = flowControlOut; 78 } 79 80 @Override 81 public void close() throws Exception { 82 SerialPortUtils.close(serialPort); 83 } 84 85 @Override 86 public void open() throws Exception { 87 serialPort = SerialPortUtils.open(port, baudRate, dataBits, stopBits, parity); 88 } 89 90 @Override 91 public InputStream getInputStream() { 92 InputStream in = null; 93 try { 94 in = serialPort.getInputStream(); 95 } catch (IOException e) { 96 log.error("獲取串口輸入流錯誤", e); 97 } 98 99 return in; 100 } 101 102 @Override 103 public OutputStream getOutputStream() { 104 OutputStream out = null; 105 try { 106 out = serialPort.getOutputStream(); 107 } catch (IOException e) { 108 log.error("獲取串口輸出流錯誤", e); 109 } 110 111 return out; 112 } 113 114 @Override 115 public int getBaudRate() { 116 return this.baudRate; 117 } 118 119 @Override 120 public int getDataBits() { 121 return this.dataBits; 122 } 123 124 @Override 125 public int getStopBits() { 126 return this.stopBits; 127 } 128 129 @Override 130 public int getParity() { 131 return this.parity; 132 } 133 134 public int getFlowControlIn() { 135 return this.flowControlIn; 136 } 137 138 public int getFlowControlOut() { 139 return this.flowControlOut; 140 } 141 142 public SerialPort getSerialPort() { 143 return serialPort; 144 } 145 146 public void setSerialPort(SerialPort serialPort) { 147 this.serialPort = serialPort; 148 } 149 150 public String getPort() { 151 return port; 152 } 153 154 public void setPort(String port) { 155 this.port = port; 156 } 157 158 public void setBaudRate(Integer baudRate) { 159 this.baudRate = baudRate; 160 } 161 162 public void setDataBits(Integer dataBits) { 163 this.dataBits = dataBits; 164 } 165 166 public void setStopBits(Integer stopBits) { 167 this.stopBits = stopBits; 168 } 169 170 public void setParity(Integer parity) { 171 this.parity = parity; 172 } 173 174 public void setFlowControlIn(Integer flowControlIn) { 175 this.flowControlIn = flowControlIn; 176 } 177 178 public void setFlowControlOut(Integer flowControlOut) { 179 this.flowControlOut = flowControlOut; 180 } 181 }
1 package modbus_rtu; 2 3 import javax.annotation.Resource; 4 5 6 import com.serotonin.modbus4j.ModbusFactory; 7 import com.serotonin.modbus4j.ModbusMaster; 8 import com.serotonin.modbus4j.exception.ModbusTransportException; 9 import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest; 10 import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse; 11 import com.serotonin.modbus4j.msg.WriteRegisterRequest; 12 import com.serotonin.modbus4j.msg.WriteRegisterResponse; 13 import com.serotonin.modbus4j.msg.WriteRegistersRequest; 14 import com.serotonin.modbus4j.msg.WriteRegistersResponse; 15 import com.serotonin.modbus4j.sero.util.queue.ByteQueue; 16 17 public class ModbusRtuMaster { 18 // 檢測系統中可用的通訊端口類 19 private static SerialPortWrapperImpl wrapper; 20 21 private static ModbusMaster master; 22 23 private static ModbusFactory modbusFactory; 24 25 26 //private static ModbusRtuMaster modbusRtuMaster; 27 28 public ModbusRtuMaster() { 29 30 } 31 32 /* 33 * public static synchronized ModbusRtuMaster getInstance() { if(modbusRtuMaster 34 * == null) { modbusRtuMaster = new ModbusRtuMaster(); } return modbusRtuMaster; 35 * } 36 */ 37 /** 38 * 端口是否在使用 39 * @param serialNumber 40 * @return 41 */ 42 public boolean inUse(String serialNumber) { 43 boolean bl = false; 44 if(this.wrapper == null) { 45 return bl; 46 } 47 if(this.wrapper.getPort() !=null && this.wrapper.getPort().equalsIgnoreCase(serialNumber)) { 48 bl = master.isInitialized(); 49 } 50 return bl; 51 } 52 53 public void createRtuMaster(String serialNumber,int baudRate,int dataBit,int stopBit,int checkoutBit) throws Exception{//relayParamConfig.getSerialNumber(), relayParamConfig.getBaudRate(), 54 //relayParamConfig.getDataBit(),relayParamConfig.getStopBit(), relayParamConfig.getCheckoutBit() 55 // 設置串口參數,串口是COM1,波特率是9600 56 wrapper = new SerialPortWrapperImpl(serialNumber, baudRate, 57 dataBit,stopBit, checkoutBit,0,0);//SerialPort.PARITY_NONE 58 modbusFactory = new ModbusFactory(); 59 // System.out.println("relayParamConfig.getSerialNumber()->"+relayParamConfig.getSerialNumber()); 60 master = modbusFactory.createRtuMaster(wrapper); 61 master.init(); 62 // 從站設備ID是1 63 // int slaveId = 1; 64 65 // 讀取保持寄存器 66 // readHoldingRegisters(master, slaveId, 0, 3); 67 // 將地址為0的保持寄存器數據修改為0 68 // writeRegister(master, slaveId, offset, value); 69 // 再讀取保持寄存器 70 // readHoldingRegisters(master, slaveId, 0, 3); 71 // short[] s = {00}; 72 // controlRelay(master, slaveId, 0, s); 73 // RtuMasterTest.writeRegistersTest(master, slaveId, 0, s); 74 } 75 76 /* 77 * public void init(RelayParamConfig relayParamConfig) throws Exception { 78 * this.relayParamConfig = relayParamConfig; 79 * if(!this.inUse(relayParamConfig.getSerialNumber())) { this.createRtuMaster(); 80 * } } 81 */ 82 83 public void closeRtuMaster() throws Exception{ 84 wrapper.close(); 85 } 86 87 public void controlRelay(int slaveId, int offset, int value) throws Exception{ 88 writeRegister(master, slaveId, offset, value); 89 // this.closeRtuMaster(); 90 } 91 // int slaveId = 1; 92 93 // 讀取保持寄存器 94 // readHoldingRegisters(master, slaveId, 0, 3); 95 // 將地址為0的保持寄存器數據修改為0 96 // writeRegister(master, slaveId, offset, value); 97 98 private void readHoldingRegisters(ModbusMaster master, int slaveId, int start, int len) throws Exception{ 99 ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, start, len); 100 ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request); 101 if (response.isException()){ 102 System.out.println("讀取保持寄存器錯誤,錯誤信息是" + response.getExceptionMessage()); 103 }else { 104 // System.out.println("讀取保持寄存器=" + Arrays.toString(response.getShortData())); 105 } 106 } 107 108 private void writeRegister(ModbusMaster master, int slaveId, int offset, int value) throws Exception{ 109 WriteRegisterRequest request = new WriteRegisterRequest(slaveId, offset, value); 110 ByteQueue queue = new ByteQueue(); 111 112 WriteRegisterResponse response = (WriteRegisterResponse) master.send(request); 113 if (response.isException()){ 114 System.out.println("寫保持寄存器錯誤,錯誤信息是" + response.getExceptionMessage()); 115 }else{ 116 System.out.println("指令發送成功!"); 117 } 118 } 119 120 public void writeRegistersTest(ModbusMaster master, int slaveId, int start, short[] values) { 121 try { 122 WriteRegistersRequest request = new WriteRegistersRequest(slaveId, start, values); 123 System.out.println("request:"+request); 124 System.out.println("FunctionCode:"+request.getFunctionCode()); 125 System.out.println("SlaveId:"+request.getSlaveId()); 126 WriteRegistersResponse response = (WriteRegistersResponse) master.send(request); 127 128 if (response.isException()) 129 System.out.println("Exception response: message=" + response.getExceptionMessage()); 130 else { 131 System.out.println("Success"); 132 } 133 } 134 catch (ModbusTransportException e) { 135 e.printStackTrace(); 136 } 137 } 138 public static void main(String[] args) { 139 ModbusRtuMaster rmt = new ModbusRtuMaster(); 140 try { 141 rmt.createRtuMaster("COM7",9600,8,1,0); 142 rmt.controlRelay(1,1,0); 143 } catch (Exception e) { 144 // TODO Auto-generated catch block 145 e.printStackTrace(); 146 } 147 } 148 }
上述程序所需關鍵包我上傳到了百度雲盤
鏈接:https://pan.baidu.com/s/1JaZaNcnQu1fp7DBIuowazg
提取碼:us3z