JavaWeb 用於windows的串列埠資料讀取
串列埠資料讀取類,用於windows的串列埠資料讀取;
1.例如觀察者為羊,被觀察者為狼 ,模仿的場景為狼叫羊跑,可參考部落格:
https://blog.csdn.net/dada360778512/article/details/6977758
2.模擬串列埠傳輸資料,需要兩個工具,Virtual Serial Port Driver、串列埠除錯助手;可參考:
https://jingyan.baidu.com/article/046a7b3eae7028f9c27fa9f7.html
3.基於RXTX(提供串列埠和並口通訊)開源類庫對串列埠進行操作的。具體配置,詳見:
https://blog.csdn.net/liu4071325/article/details/53392218
必備外掛/工具包:
1.專案裡引用: RXTXcomm.jar
2.windows伺服器需要以下三個檔案:
\JDK目錄\jre\lib\ext ---> RXTXcomm.jar
\JDK目錄\bin ---> rxtxParallel.dll 和 rxtxSerial.dll
2.Linux伺服器需要放置以下三個檔案
\JDK目錄\jre\lib\ext ---> RXTXcomm.jar
\JDK目錄\bin --->librxtxParallel.so 和 librxtxSerial.so
感謝分享!
程式碼:
1.建立被觀察者 SerialReader.java
import gnu.io.CommPort; import gnu.io.CommPortIdentifier; import gnu.io.NoSuchPortException; import gnu.io.PortInUseException; import gnu.io.SerialPort; import gnu.io.SerialPortEvent; import gnu.io.SerialPortEventListener; import gnu.io.UnsupportedCommOperationException; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Observable; import java.util.TooManyListenersException; /** * <p>串列埠資料讀取類,用於windows的串列埠資料讀取</p> * * 宣告被觀察者SerialReader,繼承Observable<br/> * 觀察者,繼承Observer<br/> * * Observer模式 主要是觀察者與被觀察者之間的關係。<br/> * */ public class SerialReader extends Observable implements Runnable, SerialPortEventListener { static CommPortIdentifier portId; int delayRead = 200; int numBytes; // buffer中的實際資料位元組數 private static byte[] readBuffer = new byte[4096]; // 4k的buffer空間,快取串列埠讀入的資料 static Enumeration portList; InputStream inputStream; SerialPort serialPort; //宣告觀察者時,被指定的引數資訊 HashMap serialParams; // 埠讀入資料事件觸發後,等待n毫秒後再讀取,以便讓資料一次性讀完 public static final String PARAMS_DELAY = "delay read"; // 延時等待埠資料準備的時間 public static final String PARAMS_TIMEOUT = "timeout"; // 超時時間 public static final String PARAMS_PORT = "port name"; // 埠名稱 public static final String PARAMS_DATABITS = "data bits"; // 資料位 public static final String PARAMS_STOPBITS = "stop bits"; // 停止位 public static final String PARAMS_PARITY = "parity"; // 奇偶校驗 public static final String PARAMS_RATE = "rate"; // 波特率 public SerialReader() { } /** * 初始化埠操作的引數. * * @see */ public SerialReader(HashMap params) { serialParams = params; init(); } private void init() { try { // 引數初始化 int timeout = Integer.parseInt(serialParams.get(PARAMS_TIMEOUT).toString()); int rate = Integer.parseInt(serialParams.get(PARAMS_RATE).toString()); int dataBits = Integer.parseInt(serialParams.get(PARAMS_DATABITS).toString()); int stopBits = Integer.parseInt(serialParams.get(PARAMS_STOPBITS).toString()); int parity = Integer.parseInt(serialParams.get(PARAMS_PARITY).toString()); delayRead = Integer.parseInt(serialParams.get(PARAMS_DELAY).toString()); String port = serialParams.get(PARAMS_PORT).toString(); // 開啟埠 portId = CommPortIdentifier.getPortIdentifier(port); EWeightConstant.IS_OPEN_PORT = true; serialPort = (SerialPort) portId.open("SerialReader", timeout); inputStream = serialPort.getInputStream(); serialPort.addEventListener(this);// 註冊串列埠監聽 serialPort.notifyOnDataAvailable(true);// 資料可用 serialPort.setSerialPortParams(rate, dataBits, stopBits, parity);// 設定引數 } catch (PortInUseException e) { // System.out.println("串列埠已經被佔用!"); e.printStackTrace(); } catch (TooManyListenersException e) { // System.out.println("串列埠監聽者過多!"); e.printStackTrace(); } catch (UnsupportedCommOperationException e) { // System.out.println("串列埠操作命令不支援!"); e.printStackTrace(); } catch (NoSuchPortException e) { // System.out.println("串列埠不存在!"); e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } Thread readThread = new Thread(this); readThread.start(); } public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } public void serialEvent(SerialPortEvent event) { try { // 等待1秒鐘讓串列埠把資料全部接收後在處理 Thread.sleep(delayRead); // System.out.print("serialEvent[" + event.getEventType() + "] "); } catch (InterruptedException e) { e.printStackTrace(); } switch (event.getEventType()) { case SerialPortEvent.BI: // 10 case SerialPortEvent.OE: // 7 case SerialPortEvent.FE: // 9 case SerialPortEvent.PE: // 8 case SerialPortEvent.CD: // 6 case SerialPortEvent.CTS: // 3 case SerialPortEvent.DSR: // 4 case SerialPortEvent.RI: // 5 case SerialPortEvent.OUTPUT_BUFFER_EMPTY: // 2 break; case SerialPortEvent.DATA_AVAILABLE: // 1 try { // 多次讀取,將所有資料讀入 while (inputStream.available() > 0) { numBytes = inputStream.read(readBuffer); } // numBytes = inputStream.read(readBuffer); changeMessage(readBuffer, numBytes); } catch (IOException e) { e.printStackTrace(); } break; } } // 通過observer pattern將收到的資料傳送給observer // 將buffer中的空位元組刪除後再發送更新訊息,通知觀察者 private String temp1 = ""; public void changeMessage(byte[] message, int length) { setChanged(); byte[] temp = new byte[length]; System.arraycopy(message, 0, temp, 0, length); // 判斷資料是否穩定,穩定則只監聽不執行更新資料 if (!"".equals(temp1)) { if (!temp1.equals(new String(temp).trim())) { temp1 = new String(temp).trim(); notifyObservers(temp); // System.out.println("msg[" + numBytes + "]: [" + new // String(temp).trim() +"]"); } } else { temp1 = new String(temp).trim(); notifyObservers(temp); } } /** * 除錯用,獲取所有的串列埠 */ static void listPorts() { Enumeration portEnum = CommPortIdentifier.getPortIdentifiers(); while (portEnum.hasMoreElements()) { CommPortIdentifier portIdentifier = (CommPortIdentifier) portEnum.nextElement(); System.out.println("串列埠名稱:"+portIdentifier.getName() + " - " +"串列埠型別:"+getPortTypeName(portIdentifier.getPortType())); } } /** * 讀取所有串列埠名字 */ public static String getPortName() { CommPortIdentifier portId; Enumeration en = CommPortIdentifier.getPortIdentifiers(); while (en.hasMoreElements()) { portId = (CommPortIdentifier) en.nextElement(); if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { return portId.getName(); } } return ""; } /** * 讀取所有串列埠名字 */ public static void closePort() { CommPortIdentifier portId; Enumeration en = CommPortIdentifier.getPortIdentifiers(); while (en.hasMoreElements()) { portId = (CommPortIdentifier) en.nextElement(); if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { } } } static String getPortTypeName(int portType) { switch (portType) { case CommPortIdentifier.PORT_I2C: return "I2C"; case CommPortIdentifier.PORT_PARALLEL: return "Parallel"; case CommPortIdentifier.PORT_RAW: return "Raw"; case CommPortIdentifier.PORT_RS485: return "RS485"; case CommPortIdentifier.PORT_SERIAL: return "Serial"; default: return "unknown type"; } } public static HashSet<CommPortIdentifier> getAvailableSerialPorts() { HashSet<CommPortIdentifier> h = new HashSet<CommPortIdentifier>(); Enumeration thePorts = CommPortIdentifier.getPortIdentifiers(); while (thePorts.hasMoreElements()) { CommPortIdentifier com = (CommPortIdentifier) thePorts.nextElement(); switch (com.getPortType()) { case CommPortIdentifier.PORT_SERIAL: try { CommPort thePort = com.open("CommUtil", 50); thePort.close(); h.add(com); } catch (PortInUseException e) { // System.out.println("Port, " + com.getName()+ ", is in // use."); } catch (Exception e) { // System.out.println("Failed to open port " + // com.getName()+ e); } } } return h; } }
2.建立觀察者CommDataObserver.java
import java.util.Observable;
import java.util.Observer;
/**
* 觀察類,繼承Observer<br/>
* 建立一個觀察類,該觀察類有自己的name名稱
*
*/
public class CommDataObserver implements Observer {
private String name;
public CommDataObserver() {
}
public CommDataObserver(String name) {
this.name = name;
}
/**
* 監控串列埠,獲取串列埠資料
*/
public void update(Observable o, Object arg) {
//獲取串列埠返回資料
String weight = new String((byte[]) arg).trim();
//將獲取到的資料,賦值到全域性變數裡,以便可以呼叫該資料
EWeightConstant.GOOD_WEIGHT = weight;
//testing start 根據串列埠型別不同,返回的資料格式也不盡相同,此處除錯列印而已 {{
System.out.println("weight="+weight);
StringBuilder sb = new StringBuilder(weight);// 0.000或者00.000
sb.insert(weight.length() - 3, ".");
System.out.println(sb.toString() + "kg");
//testing end }}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.初始化埠資料,並建立被觀察者和觀察者,並加入觀察GetWeightUtil.java
import java.util.HashMap;
import gnu.io.SerialPort;
/**
* 獲取串列埠工具類
* 此處宣告觀察者和被觀察者
*/
public class GetWeightUtil {
/**
* 串列埠設定, 初始化埠資料
* @param com 埠
*/
public static void comConfig(String com) {
//儲存埠資料
HashMap<String, Comparable> params = new HashMap<String, Comparable>();
// 埠名稱
params.put(SerialReader.PARAMS_PORT, com);
// 波特率
params.put(SerialReader.PARAMS_RATE, 9600);
// 裝置超時時間 1秒
params.put(SerialReader.PARAMS_TIMEOUT, 1000);
// 埠資料準備時間 1秒
params.put(SerialReader.PARAMS_DELAY, 200);
// 資料位
params.put(SerialReader.PARAMS_DATABITS, SerialPort.DATABITS_8);
// 停止位
params.put(SerialReader.PARAMS_STOPBITS, SerialPort.STOPBITS_1);
// 無奇偶校驗
params.put(SerialReader.PARAMS_PARITY, SerialPort.PARITY_NONE);
//建立被觀察者
SerialReader serialReader = new SerialReader(params);
//建立觀察者
CommDataObserver obServer = new CommDataObserver("電子秤");
//新增一個觀察者,加入觀察
serialReader.addObserver(obServer);
//TODO 除錯用,輸出到控制檯 顯示所有串列埠號
SerialReader.listPorts();
}
public static void main(String[] args) throws Exception{
comConfig("COM1");
while(true){
Thread.sleep(1000);
System.out.println(EWeightConstant.GOOD_WEIGHT);
}
}
}
4.EWeightConstant.java
/**
* 記錄串列埠的基礎資訊 全域性變數
*
*/
public class EWeightConstant {
/**
* 串列埠資料
*/
public static String GOOD_WEIGHT = "0";
/**
* 串列埠是否開啟
*/
public static Boolean IS_OPEN_PORT = false;
}
5.Controller方法呼叫獲取串列埠資料,並作用於業務開發
/**
* 前端 呼叫該方法,用於獲取串列埠返回資料
* 如果想一直呼叫該串列埠返回資料,JS前端可做輪詢
* @param request
* @return
*/
@RequestMapping(value = "/getWeight", method = RequestMethod.GET)
@ResponseBody
public String getWeight(HttpServletRequest request) {
// 獲取資料的串列埠
final String E_WEIGHT_COM = "COM1";
try{
if(!EWeightConstant.IS_OPEN_PORT){
GetWeightUtil.comConfig(E_WEIGHT_COM);
}
}catch(Exception e){
e.printStackTrace();
}
//全域性變數,記錄串列埠資料
String weight = EWeightConstant.GOOD_WEIGHT;
//輸出串列埠資料,檢視串列埠資料格式
System.out.println("獲取串列埠原始資料格式 = " + weight);
if(StringUtils.equals(weight, "0")){
return weight;
}
//擷取字串,獲取要取得資料部分
weight = weight.substring(weight.indexOf("+")+1, weight.indexOf(" "));
System.out.println("最終資料,返回擷取後的資料="+weight);
return weight;
}