最近到鋼鐵公司出差,項目其中有個需求是需要對鋼鐵稱重,利用吊鉤秤基於串口通信,其中萬準衡器向串口發送的數據格式是五個字節,分別是一個字節的起始位(0xFF),一個字節的狀態位,三個字節的數據位。在取數過程中也是琢磨很久,需要經歷一個計算過程。以下是自己在這個項目中整理的案例:
package com.kingdee.test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TooManyListenersException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
public class Test extends Thread implements SerialPortEventListener {
InputStream inputStream; // 從串口來的輸入流
static OutputStream outputStream;// 向串口輸出的流
static SerialPort serialPort; // 串口的引用
static Enumeration<?> portList; // 有效連接上的端口的枚舉
static CommPortIdentifier port; // 串口通信管理類
private List<String> list = new ArrayList<String>();
@Override
public void serialEvent(SerialPortEvent arg0) {
switch (arg0.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:// 當有可用數據時讀取數據
try {
inputStream = serialPort.getInputStream();
} catch (IOException e1) {
e1.printStackTrace();
}
try {
byte[] bytes = new byte[inputStream.available()];
int numBytes = 0;
while (inputStream.available() > 0) {
String str = "";
numBytes = inputStream.read(bytes);
// 記錄吊鉤秤的狀態,穩定還是不穩定
String status = "";
// 記錄小數位
int decimalPlace = 0;
// 記錄重量參數
String strValue;
byte byteValue;
double doubleValue;
// 第二位爲狀態字 十進制18代表靜態並且有一位小數 十進制2代表動態並且有一位小數
// 若傳輸一次的數據長度不爲5,拋出異常
if (bytes.length != 5) {
throw new Exception("數據格式不正確!");
} else {
// 若起始位不爲16進制的0xFF即java中byte類型的-1,拋出異常
if (bytes[0] != (byte) 0xFF) {
throw new Exception("起始位異常!!!");
} else {
System.out.println("獲取數據正常");
// 判斷狀態位的二進制數
// D7--0--量程以內 1--超載
// D6--0
// D5--0--正數 1--負數
// D4--0--動態 1--靜態
// D3--0
// D2 D1 D0--001無小數 010--一位小數 011--兩位 100--三位
// 101--四位
if ((bytes[1] & 128) == 128) {
throw new Exception("超載!!!");
} else if ((bytes[1] & 32) == 32) {
status = "穩定";
}
// 根據狀態位取小數位
if ((bytes[1] & 0x1) == 0x1) {
decimalPlace = 0;
} else if ((bytes[1] & 0x2) == 0x2) {
decimalPlace = 10;
} else if ((bytes[1] & 0x3) == 0x3) {
decimalPlace = 100;
} else if ((bytes[1] & 0x4) == 0x4) {
decimalPlace = 1000;
} else if ((bytes[1] & 0x5) == 0x5) {
decimalPlace = 10000;
}
// 計算重量 由高位到中位到低位
// 高位
strValue = "";
byteValue = rightMoveByte(bytes[4], 4);
strValue = strValue + byteValue;
byteValue = (byte) (bytes[4] & 15);
strValue = strValue + byteValue;
// 中位
byteValue = rightMoveByte(bytes[3], 4);
strValue = strValue + byteValue;
byteValue = (byte) (bytes[3] & 15);
strValue = strValue + byteValue;
// 低位
byteValue = rightMoveByte(bytes[2], 4);
strValue = strValue + byteValue;
byteValue = (byte) (bytes[2] & 15);
strValue = strValue + byteValue;
doubleValue = Double.parseDouble(strValue);
if (decimalPlace > 0) {
doubleValue = doubleValue / decimalPlace;
}
}
}
System.out.println("重量值爲:" + doubleValue + "kg");
// 取到的數據存在str字符串
// list.add(new Date() + "真實收到的數據爲: " + str);
// 重新構造緩衝對象,否則有可能會影響接下來接收的數據
bytes = new byte[inputStream.available()];
// System.out.println(" 字節數爲: " + numBytes);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 向右移位方法,用來計算重量
private static byte rightMoveByte(byte b, int n) {
byte target = b;
for (int i = 1; i <= n; i++) {
target = (byte) (target / 2);
}
return target;
}
public int startComPort() throws Exception {
// 通過串口通信管理類獲得當前連接上的串口列表
portList = CommPortIdentifier.getPortIdentifiers();
while (portList.hasMoreElements()) {
// 獲取相應串口對象
port = (CommPortIdentifier) portList.nextElement();
System.out.println("設備類型:--->" + port.getPortType());
System.out.println("設備名稱:---->" + port.getName());
// 判斷端口類型是否爲串口
if (port.getPortType() == CommPortIdentifier.PORT_SERIAL) {
// 判斷哪幾個串口存在,就打開該串口
switch (port.getName()) {
case "COM1":
serialPort = (SerialPort) port.open("COM1", 1000);
break;
case "COM2":
serialPort = (SerialPort) port.open("COM2", 1000);
break;
case "COM3":
serialPort = (SerialPort) port.open("COM3", 1000);
break;
}
// 設置當前串口的輸入輸出流
try {
inputStream = serialPort.getInputStream();
outputStream = serialPort.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
return 0;
}
// 給當前串口添加一個監聽器
try {
serialPort.addEventListener(this);
} catch (TooManyListenersException e) {
e.printStackTrace();
return 0;
}
// 設置監聽器生效,即:當有數據時通知
// 設置當端口有可用數據時觸發事件,此設置必不可少。
serialPort.notifyOnDataAvailable(true);
// 設置串口的一些讀寫參數
try {
// 比特率、數據位、停止位、奇偶校驗位
serialPort.setSerialPortParams(4800, SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
return 0;
}
return 1;
} else {
throw new Exception("端口類型不是串口!");
}
}
return 0;
}
public static void main(String[] args) throws Exception {
final Test t = new Test();
t.startComPort();
// java定時器
//這裏需要經過很短的時間關閉串口,只需要取一組數據,利用java定時器操作
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
t.serialPort.close();
}
};
timer.schedule(task, 200);
}
}