java語言基於萬準衡器(吊鉤秤)的串口通信案例

最近到鋼鐵公司出差,項目其中有個需求是需要對鋼鐵稱重,利用吊鉤秤基於串口通信,其中萬準衡器向串口發送的數據格式是五個字節,分別是一個字節的起始位(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);
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章