using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using Microsoft.Win32;
namespace CLCom32
{
internal class COM32
{
#region---------------------内部参数---------------------
/// <summary>
/// 波特率
/// </summary>
private string baudRate;
/// <summary>
/// 数据位
/// </summary>
private string dataBits;
/// <summary>
/// 停止位
/// </summary>
private string stopBits;
/// <summary>
/// 校验位
/// </summary>
private string checkBits;
/// <summary>
/// 端口号
/// </summary>
private string portNum;
/// <summary>
/// 停止缓冲接收
/// </summary>
private bool stopReceive = false;
/// <summary>
/// 串口线程互斥
/// </summary>
private object ThreadObject = new object();
/// <summary>
/// 串口控制对象
/// </summary>
private SerialPort spCom;
/// <summary>
/// 返回数据缓冲帧队列
/// </summary>
private Queue<byte[]> returnBuff;
/// <summary>
/// 缓冲帧队列互斥
/// </summary>
private object QueueObject = new object();
/// <summary>
/// 自定义拆帧接口对象
/// </summary>
private IFrameAnalysis fAnalysis = null;
#endregion
/// <summary>
/// 读取超时时间
/// </summary>
private int readTimeOut = 500;
/// <summary>
/// 是否异步读取
/// </summary>
private bool isAsynchronousRead = false;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="Settings">通信参数1200,e,8,1</param>
/// <param name="ComNum">串口号</param>
public COM32(string Settings, int ComNum)
{
string[] Tmp = Settings.Split(',');
if (Tmp.Length != 4)
{
baudRate = "1200";
checkBits = "n";
dataBits = "8";
stopBits = "1";
}
else
{
baudRate = Tmp[0];
checkBits = Tmp[1];
dataBits = Tmp[2];
stopBits = Tmp[3];
}
portNum = string.Format("COM{0}", ComNum);
returnBuff = new Queue<byte[]>();
spCom = new SerialPort();
}
~COM32()
{
if (spCom.IsOpen)
{
spCom.Close();
}
spCom.Dispose();
returnBuff = null;
}
/// <summary>
/// 是否异步读取返回数据
/// </summary>
public bool IsAsynchronousRead
{
get { return isAsynchronousRead; }
set { isAsynchronousRead = value; }
}
/// <summary>
/// 读取和设置下位机返回数据的超时时间(默认500ms)
/// </summary>
public int ReadTimeOut
{
get { return readTimeOut; }
set { readTimeOut = value; }
}
/// <summary>
/// 返回缓冲区帧数量
/// </summary>
public int frameCount
{
get { return returnBuff.Count; }
}
/// <summary>
/// 打开串口
/// </summary>
/// <returns></returns>
public bool Open()
{
lock (ThreadObject)
{
try
{
if (spCom.IsOpen) return true;
spCom.BaudRate = int.Parse(baudRate);
spCom.StopBits = (StopBits)int.Parse(stopBits);
spCom.DataBits = int.Parse(dataBits);
spCom.Parity = checkBits.ToLower() == "n" ? Parity.None : checkBits.ToLower() == "e" ? Parity.Even : Parity.Mark;
spCom.PortName = portNum;
spCom.DtrEnable = true;
spCom.Encoding = Encoding.GetEncoding("iso-8859-1"); //设置编码类型为8859-1,这样可以用字符形式读取0-255的ASCII码
spCom.Open();
return true;
}
catch (Exception e)
{
if (spCom.IsOpen)
{
spCom.Close();
}
throw e;
}
}
}
/// <summary>
/// 关闭串口
/// </summary>
public void Close()
{
stopReceive = true; //停止缓冲打开
returnBuff.Clear(); //清理帧BUFF
lock (ThreadObject)
{
if (spCom.IsOpen) spCom.Close();
}
}
/// <summary>
/// 等待接收数据
/// </summary>
private void WaiteReceiveData()
{
if (!spCom.IsOpen) return;
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(ThreadReceive), null); //开启线程循环取帧
}
/// <summary>
/// 开启一个线程接收数据
/// </summary>
/// <param name="Obj"></param>
private void ThreadReceive(Object Obj)
{
List<byte> tmplist = new List<byte>();
lock (ThreadObject)
{
while (!stopReceive) //如果没有主动停止就一直读
{
if (spCom.BytesToRead > 0)
{
spCom.ReadTimeout = readTimeOut; //设置阅读超时时间,该超时时间只能作为READLINE的超时
lock (QueueObject)
{
if (fAnalysis == null)
{
try
{
returnBuff.Enqueue(spCom.Encoding.GetBytes(string.Format("{0}{1}", spCom.ReadLine(), spCom.NewLine))); //读取缓冲区中断针符之前的数据
}
catch
{
//byte[] tmp = new byte[spCom.BytesToRead];
//spCom.Read(tmp, 0, tmp.Length);
//returnBuff.Enqueue(tmp);
returnBuff.Enqueue(spCom.Encoding.GetBytes(spCom.ReadExisting())); //读取当前缓冲区可用的所有字符
}
}
else
{
tmplist.AddRange(spCom.Encoding.GetBytes(spCom.ReadExisting()));
int vstart, vLen;
if (fAnalysis.FrameAnalysis(tmplist.ToArray(), out vstart, out vLen))
{
try
{
returnBuff.Enqueue(tmplist.GetRange(vstart, vLen).ToArray());
}
catch
{
throw new ExecutionEngineException("拆帧接口长度参数返回越界");
}
tmplist.RemoveRange(0, vstart + vLen);
}
}
}
}
}
}
}
/// <summary>
/// 发送数据\接收数据
/// </summary>
/// <param name="vData">发送的数据和读取返回的数据)</param>
/// <returns></returns>
public bool SendData(ref byte[] vData, bool IsReturn)
{
stopReceive = true; //停止线程
if (!spCom.IsOpen) //检查串口是否打开
{
bool isOpen = this.Open();
if (!isOpen) return false;
}
lock (ThreadObject)
{
spCom.DiscardInBuffer(); //清理缓冲区BUFF
spCom.DiscardOutBuffer();
}
stopReceive = false; //关闭线程循环读取标志
returnBuff.Clear(); //清理帧BUFF
spCom.Write(vData, 0, vData.Length); //发送数据
System.Threading.Thread.Sleep(100); //发送后停100MS
if (!IsReturn) { this.Close(); return true; } //如果不需要返回数据,就直接关闭串口,返回OK
this.WaiteReceiveData(); //如果要返回数据,就开启线程读取数据
if (!isAsynchronousRead) //如果不是异步读取数据最后会关闭串口,同时会更新vdata,如果是异步读取数据,则不会更新vdata
{
vData = new byte[0];
DateTime tmptime1 = DateTime.Now;
while (TimeSub(DateTime.Now, tmptime1) < readTimeOut * 2) //等待超时时间的2倍时间
{
if (returnBuff.Count > 0) //如果帧缓冲区有数据
{
vData = returnBuff.Dequeue(); //获取一帧数据
break;
}
}
this.Close(); //获取完毕后关闭串口
return vData.Length > 0 ? true : false;
}
return true; //如果是异步则不需要关闭串口,直接返回OK
}
/// <summary>
/// 发送数据\接收数据
/// </summary>
/// <param name="vData">发送的数据\发送完毕后会填充返回的数据(如果isAsynchronousRead为真,则vData不返回数据)</param>
/// <param name="stopto">缓冲区中读到的停止字符(当读到该字符时停止读取并返回)</param>
/// <returns></returns>
public bool SendData(ref byte[] vData, byte stopto)
{
spCom.NewLine = Convert.ToChar(stopto).ToString();
return SendData(ref vData, true);
}
/// <summary>
/// 发送数据\接收数据
/// </summary>
/// <param name="vData">发送的数据\发送完毕后会填充返回的数据(如果isAsynchronousRead为真,则vData不返回数据)</param>
/// <param name="stopto">缓冲区中读到的停止字符(当读到该字符时停止读取并返回)</param>
/// <returns></returns>
public bool SendData(ref byte[] vData, byte[] stoptos)
{
string newLine = "";
foreach (byte b in stoptos)
{
newLine = string.Format("{0}{1}", newLine, Convert.ToChar(b));
}
spCom.NewLine = newLine;
return SendData(ref vData, true);
}
/// <summary>
/// 发送数据(有响应数据返回)
/// </summary>
/// <param name="vData">发送的数据帧(如果isAsynchronousRead为真,则vData不返回数据)</param>
/// <returns></returns>
public bool SendData(ref byte[] vData)
{
return SendData(ref vData, true);
}
/// <summary>
/// 发送数据(有响应数据返回),并按照自定义方式拆帧
/// </summary>
/// <param name="vData">发送的数据帧(如果isAsynchronousRead为真,则vData不返回数据)</param>
/// <param name="Analysis">自定义拆帧方式类</param>
/// <returns></returns>
public bool SendData(ref byte[] vData, IFrameAnalysis Analysis)
{
if (Analysis != null) fAnalysis = Analysis;
return SendData(ref vData, true);
}
/// <summary>
/// 读缓冲区的数据帧
/// </summary>
/// <returns></returns>
public byte[] Read()
{
lock (QueueObject)
{
if (returnBuff.Count > 0)
{
return returnBuff.Dequeue(); //出栈一帧数据
}
else
{
return new byte[0]; //如果BUFF为空,则返回0长度字节数组
}
}
}
/// <summary>
/// 时间差计算
/// </summary>
/// <param name="Time1">参与计算的时间1</param>
/// <param name="Time2">参与计算的时间2</param>
/// <returns></returns>
private long TimeSub(DateTime Time1, DateTime Time2)
{
TimeSpan tsSub = Time1.Subtract(Time2);
return tsSub.Hours * 60 * 60 * 1000 + tsSub.Minutes * 60 * 1000 + tsSub.Seconds * 1000 + tsSub.Milliseconds;
}
/// <summary>
/// 获取可用串口列表
/// </summary>
/// <returns></returns>
public static System.Collections.IEnumerable ComList()
{
RegistryKey rKey = Registry.LocalMachine.OpenSubKey("Hardware\\DeviceMap\\SerialComm");
if (rKey != null)
{
string[] sSubKeys = rKey.GetValueNames();
foreach (string sName in sSubKeys)
{
yield return rKey.GetValue(sName);
}
}
}
}
/// <summary>
/// 帧自定义解析长度接口
/// </summary>
public interface IFrameAnalysis
{
/// <summary>
/// 解析帧长度
/// </summary>
/// <param name="vData">数据字节数组</param>
/// <param name="start">帧开始位置</param>
/// <param name="end">帧长度</param>
/// <returns>里面存在一帧返回真,不够一帧返回FLASE</returns>
bool FrameAnalysis(byte[] vData,out int start,out int len);
}