📄 serialportcomm.java
字号:
/***********************************************************************
* Module: SerialPortComm.java
* Author: Administrator
* Purpose: Defines the Class SerialPortComm
***********************************************************************/
package com.zcsoft.comm;
import java.util.*;
import java.io.*;
import javax.comm.*;
/** <p>Title: 串口通信</p>
* <p>Description: 串口通信实验</p>
* <p>Copyright: Copyright (c) 2004-2005</p>
* <p>Company: Zhicheng Software&Service Co. Ltd.</p>
*
* @author 蒋智湘
* @version 2.1
*/
public class SerialPortComm implements SerialPortEventListener
{
/** 通讯端口标识符 */
private CommPortIdentifier portID;
/** 通讯端口标识符对应的端口 */
private SerialPort port;
/** port对应的输入流 */
private InputStream in;
/** 接收缓冲区,大小为端口接收缓冲区大小。
* 因可能对接收的数据处理太慢而导致端口接收缓冲区大小溢出而丢失数据
* 因此不能给其太小的值
* @see #notifyPortChanged(int[], int[])
*/
private byte[] rcvBuffer = new byte[32];
/** port对应的输出流 */
private OutputStream out;
/** 对接收到的字节的处理接口实现 */
private List handlers = new ArrayList(1);
/** 最近一次发送的电报的时间 */
private transient long lastSentTime;
/** 设定接收报文包的长度,仅对定长接收方式有效 */
private int packetSize;
/** 报文结尾符,仅对定字结尾接收方式有效 */
private byte[] terminalBytes;
/** 从接收到缓冲中有数据通知候开始读取报文的延时,仅对定时接收有效 */
private int delayToRead;
/** 未提交给外部程序处理,但已经被该实例读取的字节数量 */
private transient int cntStoredBytes = 0;
/** 强制的连续两次发送间隔时间,默认时间为100毫秒 */
private int mandatoryDelay = 0;
/** 构造一个指定端口的通信实例
*
* @param portName 不为null的端口名:COM1, COM2, etc.
* @exception NoSuchPortException
*/
public SerialPortComm(String portName)
{
this.portID = createByName(portName);
}
/**
*
* @param n 串口名称:COM1, COM2等.
* @return 串口对应的CommPortIdentifier对象
* @exeception IllegalArgumentException 如果找不到指定串口
*/
private static CommPortIdentifier createByName(String n)
{
try
{
return CommPortIdentifier.getPortIdentifier(n);
}
catch (NoSuchPortException ex)
{
throw new IllegalArgumentException("Not found ".concat(n));
}
}
/** 获取所有可被使用的串口之名称的列表 */
public static List getAvailablePorts()
{
Enumeration ports = CommPortIdentifier.getPortIdentifiers();
List portIds = new ArrayList(2);
while (ports.hasMoreElements())
{
CommPortIdentifier portId = (CommPortIdentifier)ports.nextElement();
if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL)
{
if (portId.isCurrentlyOwned())
{
System.err.println("Detected "
+ portId.getName()
+ " in use by "
+ portId.getCurrentOwner());
}
else
{
portIds.add(portId.getName());
}
}
}
return portIds;
}
/** 打开端口
*
* @IllegalStateException 串口被其它程序打开
*/
public void open() throws IllegalStateException
{
close();
//portID.addPortOwnershipListener(this);
SerialPort port;
try
{
port = (SerialPort)portID.open("SerialPortComm", 2000);
try
{
port.addEventListener(this);
}
catch(TooManyListenersException e)
{
debug(e);
}
}
catch(PortInUseException ex)
{
debug(ex);
throw new IllegalStateException(ex.getMessage());
}
setPort(port);
}
/** 关闭端口 */
public void close()
{
if(port != null)
{
port.removeEventListener();
port.close();
setPort(null);
}
/*if(portID != null)
{
portID.removePortOwnershipListener(this);
}//*/
}
/** Handler for all serial port events
*
* @param ev 串口上发生的事件
*/
public void serialEvent(SerialPortEvent ev)
{
switch(ev.getEventType())
{
case SerialPortEvent.DATA_AVAILABLE:
//debug(("//DATA_AVAILABLE"));
if(in != null)
{
notifyAvailable();
}
break;
// case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
// debug(("//OUTPUT_BUFFER_EMPTY"));
// break;
case SerialPortEvent.FE://Framing error
debug(("//Framing error"));
break;
case SerialPortEvent.BI://Break interrupt
debug(("//Break interrupt"));
break;
case SerialPortEvent.OE://Overrun error
debug(("//Overrun error"));
break;
case SerialPortEvent.PE://Parity error
debug(("//Parity error"));
break;
case SerialPortEvent.CD://Carrier detect
debug(("//Carrier detect"));
break;
case SerialPortEvent.CTS://Clear to send
debug(("//Clear to send"));
break;
case SerialPortEvent.DSR://Data set ready
debug(("//Data set ready"));
break;
case SerialPortEvent.RI://Ring indicator
debug(("//Ring indicator"));
break;
}
}
/** 设置打开的端口。当新的SerialPort为null,
* 则关闭输出输入输出流;否则打开输出输入输出流,
* 否则设定端口参数,然后打开输入输出流。
* @param port
*/
public void setPort(SerialPort port)
{
this.port = port;
if(port == null)//端口关闭
{
out = null;
in = null;
}
else
{
configPortWithPacketSize();
// port.notifyOnBreakInterrupt(true);
// port.notifyOnCTS(true);
// port.notifyOnCarrierDetect(true);//在modem上用到的载波检测,表示建立一个MODEM到另一MODEM的连接
// port.notifyOnDSR(true);
// port.notifyOnOutputEmpty(true);
// port.notifyOnRingIndicator(true);//在modem上用到的震铃指示,表示接受到外部电话
port.notifyOnOverrunError(true);
port.notifyOnParityError(true);
port.notifyOnFramingError(true);
port.notifyOnDataAvailable(true);
openOutputstream();
openInputStream();
}
}
private void openOutputstream()
{
try
{
out = port.getOutputStream();
}
catch(IOException ex)
{
debug("Cannot open output stream");
}
}
private void openInputStream()
{
try
{
in = port.getInputStream();
}
catch(IOException ex)
{
debug("Cannot open input stream");
}
}
private void configPortWithPacketSize()
{
try
{
if(this.packetSize > 0)
{
//设定最小读取字节大小为定义的包大小,则只有当接收缓冲中的字节数大于等于该包大小时
//才会通知缓冲中有数据,此时调用readData()方法,从而避免对方法readData不必要的调用
port.enableReceiveThreshold(this.packetSize);
}
else
{
port.disableReceiveThreshold();
}
}
catch (UnsupportedCommOperationException ex)
{
ex.printStackTrace();
}
}
/** 获取打开的端口
*
* @return */
private SerialPort getPort()
{
return this.port;
}
/**
* 直接写报文内容
* 写入报文时,需注意根据接收设备的处理速度控制最小发送间隔时间
* @param b 组成报文的所有字节
* @exception IllegalStateException 发送失败导致的异常。经常是因为端口已经被关闭所引起的。
*/
public synchronized void sendMessage(byte[] b)
{
checkState();
//延时mandatoryDelay毫秒,因为PLC不能正确处理间隔事件很短的连续电报
long now = System.currentTimeMillis();
if (now - lastSentTime < mandatoryDelay)
{
try
{
Thread.sleep(mandatoryDelay - (now - lastSentTime));
}
catch (InterruptedException ex){}
}
try
{
//synchronized(this)//同步处理以避免读取数据的过程中写数据
{
out.write(b);
}
}
catch(IOException ex)
{//出现这类异常的原因多数由于正在写的过程中,端口被关闭
throw new IllegalStateException(ex.toString());
}
lastSentTime = now;
}
private void checkState() throws IllegalStateException
{
if (!isOpened())
{
throw new IllegalStateException("port closed");
}
}
/**
* 判断端口是否被打开
* @return true已被打开,false已关闭
*/
public boolean isOpened()
{
return this.port != null;
}
/** @todo 以下为接收部分
* 通知有数据可以读取
*/
private void notifyAvailable()
{
int available = 0;
try
{
available = in.available();
}
catch (IOException ex){}
if (available <= 0)
{
return;
}
try
{
if (this.packetSize > 0)
{
readDataByFixedLength(available);
}
else if(this.terminalBytes != null)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -