📄 port.cs
字号:
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Text;
namespace NiceTracker.Serial
{
public class CommPortException : Exception
{
public CommPortException(string desc) : base(desc) {}
}
public class Port : IDisposable
{
#region delegates and events
public delegate void CommEvent();
public delegate void CommChangeEvent(bool NewState);
public delegate void CommErrorEvent(string Description);
public event CommErrorEvent OnError;
public event CommEvent DataReceived;
public event CommEvent RxOverrun;
public event CommEvent TxDone;
public event CommEvent FlagCharReceived;
public event CommEvent PowerEvent;
public event CommEvent HighWater;
public event CommChangeEvent DSRChange;
public event CommChangeEvent RingChange;
public event CommChangeEvent CTSChange;
public event CommChangeEvent RLSDChange;
#endregion
#region variable declarations
private string portName;
private IntPtr hPort = (IntPtr)CommAPI.INVALID_HANDLE_VALUE;
// default Rx buffer is 1024 bytes
private uint rxBufferSize = 1024;
private byte[] rxBuffer;
private uint prxBuffer = 0;
private uint rthreshold = 1;
// default Tx buffer is 1024 bytes
private uint txBufferSize = 1024;
private byte[] txBuffer;
private uint ptxBuffer = 0;
private uint sthreshold = 1;
private Mutex rxBufferBusy = new Mutex();
private uint inputLength;
private DCB dcb = new DCB();
private DetailedPortSettings portSettings;
private Thread eventThread;
private ManualResetEvent threadStarted = new ManualResetEvent(false);
private IntPtr closeEvent;
private string closeEventName = "CloseEvent";
private int rts = -1;
private bool rtsavail = false;
private int dtr = -1;
private bool dtravail = false;
private int brk = -1;
private bool isOpen = false;
#endregion
private void Init()
{
// create a system event for synchronizing Closing
closeEvent = CommAPI.CreateEvent(true, false, closeEventName);
rxBuffer = new byte[rxBufferSize];
txBuffer = new byte[txBufferSize];
portSettings = new DetailedPortSettings();
}
#region constructors
public Port(string PortName)
{
portName = PortName;
Init();
}
public Port(string PortName, BasicPortSettings InitialSettings)
{
portName = PortName;
Init();
//override default ettings
portSettings.BasicSettings = InitialSettings;
}
public Port(string PortName, DetailedPortSettings InitialSettings)
{
portName = PortName;
Init();
//override default ettings
portSettings = InitialSettings;
}
public Port(string PortName, uint RxBufferSize, uint TxBufferSize)
{
rxBufferSize = RxBufferSize;
txBufferSize = TxBufferSize;
Init();
}
public Port(string PortName, BasicPortSettings InitialSettings, uint RxBufferSize, uint TxBufferSize)
{
rxBufferSize = RxBufferSize;
txBufferSize = TxBufferSize;
Init();
//override default ettings
portSettings.BasicSettings = InitialSettings;
}
public Port(string PortName, DetailedPortSettings InitialSettings, uint RxBufferSize, uint TxBufferSize)
{
rxBufferSize = RxBufferSize;
txBufferSize = TxBufferSize;
Init();
//override default ettings
portSettings = InitialSettings;
}
#endregion
// since the event thread blocks until the port handle is closed
// implement both a Dispose and destrucor to make sure that we
// clean up as soon as possible
public void Dispose()
{
if(isOpen)
this.Close();
}
~Port()
{
if(isOpen)
this.Close();
}
public string PortName
{
get
{
return portName;
}
set
{
portName = value;
}
}
public bool IsOpen
{
get
{
return isOpen;
}
}
public bool Open()
{
if(isOpen) return false;
hPort = CommAPI.CreateFile(portName);
if(hPort == (IntPtr)CommAPI.INVALID_HANDLE_VALUE)
{
int e = Marshal.GetLastWin32Error();
if(e == (int)APIErrors.ERROR_ACCESS_DENIED)
{
// port is unavailable
return false;
}
// ClearCommError failed!
string error = String.Format("CreateFile Failed: {0}", e);
throw new CommPortException(error);
}
isOpen = true;
// set queue sizes
CommAPI.SetupComm(hPort, rxBufferSize, txBufferSize);
// transfer the port settings to a DCB structure
dcb.BaudRate = (uint)portSettings.BasicSettings.BaudRate;
dcb.ByteSize = portSettings.BasicSettings.ByteSize;
dcb.EofChar = (sbyte)portSettings.EOFChar;
dcb.ErrorChar = (sbyte)portSettings.ErrorChar;
dcb.EvtChar = (sbyte)portSettings.EVTChar;
dcb.fAbortOnError = portSettings.AbortOnError;
dcb.fBinary = true;
dcb.fDsrSensitivity = portSettings.DSRSensitive;
dcb.fDtrControl = (DCB.DtrControlFlags)portSettings.DTRControl;
dcb.fErrorChar = portSettings.ReplaceErrorChar;
dcb.fInX = portSettings.InX;
dcb.fNull = portSettings.DiscardNulls;
dcb.fOutX = portSettings.OutX;
dcb.fOutxCtsFlow = portSettings.OutCTS;
dcb.fOutxDsrFlow = portSettings.OutDSR;
dcb.fParity = (portSettings.BasicSettings.Parity == Parity.none) ? false : true;
dcb.fRtsControl = (DCB.RtsControlFlags)portSettings.RTSControl;
dcb.fTXContinueOnXoff = portSettings.TxContinueOnXOff;
dcb.Parity = (byte)portSettings.BasicSettings.Parity;
dcb.StopBits = (byte)portSettings.BasicSettings.StopBits;
dcb.XoffChar = (sbyte)portSettings.XoffChar;
dcb.XonChar = (sbyte)portSettings.XonChar;
dcb.XonLim = dcb.XoffLim = (ushort)(rxBufferSize / 10);
CommAPI.SetCommState(hPort, dcb);
// store some state values
brk = 0;
dtr = dcb.fDtrControl == DCB.DtrControlFlags.Enable ? 1 : 0;
rts = dcb.fRtsControl == DCB.RtsControlFlags.Enable ? 1 : 0;
// set the Comm timeouts
CommTimeouts ct = new CommTimeouts();
// reading we'll return immediately
// this doesn't seem to work as documented
ct.ReadIntervalTimeout = uint.MaxValue; // this = 0xffffffff
ct.ReadTotalTimeoutConstant = 0;
ct.ReadTotalTimeoutMultiplier = 0;
// writing we'll give 5 seconds
ct.WriteTotalTimeoutConstant = 5;
ct.WriteTotalTimeoutMultiplier = 0;
CommAPI.SetCommTimeouts(hPort, ct);
// start the receive thread
eventThread = new Thread(new ThreadStart(CommEventThread));
eventThread.Priority = ThreadPriority.AboveNormal;
eventThread.Start();
// wait for the thread to actually get spun up
threadStarted.WaitOne();
return true;
}
public bool Close()
{
if(!isOpen) return false;
if(CommAPI.CloseHandle(hPort))
{
CommAPI.SetEvent(closeEvent);
isOpen = false;
hPort = (IntPtr)CommAPI.INVALID_HANDLE_VALUE;
CommAPI.SetEvent(closeEvent);
return true;
}
return false;
}
public byte[] Output
{
set
{
if(!isOpen) return;
int written = 0;
// more than threshold amount so send without buffering
if(value.GetLength(0) > sthreshold)
{
// first send anything already in the buffer
if(ptxBuffer > 0)
{
CommAPI.WriteFile(hPort, txBuffer, ptxBuffer, ref written);
ptxBuffer = 0;
}
CommAPI.WriteFile(hPort, value, (uint)value.GetLength(0), ref written);
}
else
{
// copy it to the tx buffer
value.CopyTo(txBuffer, (int)ptxBuffer);
ptxBuffer += (uint)value.Length;
// now if the buffer is above sthreshold, send it
if(ptxBuffer >= sthreshold)
{
CommAPI.WriteFile(hPort, txBuffer, ptxBuffer, ref written);
ptxBuffer = 0;
}
}
}
}
public byte[] Input
{
get
{
if(!isOpen) return null;
byte[] data = new byte[inputLength];
data.Initialize();
if(prxBuffer > 0)
{
// check to see if we actually have inputLength bytes in the buffer
uint bytesToCopy = prxBuffer < inputLength ? prxBuffer : inputLength;
// prevent the rx thread from adding to the buffer while we use it
rxBufferBusy.WaitOne();
// copy the buffer to an output variable for inputLength bytes
Array.Copy(rxBuffer, 0, data, 0, (int)bytesToCopy);
// shift the data in the Rx Buffer to revove inputLength bytes
Array.Copy(rxBuffer, (int)bytesToCopy, rxBuffer, 0, (int)(rxBuffer.GetUpperBound(0) - bytesToCopy));
prxBuffer -= bytesToCopy;
// release the mutex so the Rx thread can work
rxBufferBusy.ReleaseMutex();
}
return data;
}
}
public uint InputLen
{
get
{
return inputLength;
}
set
{
inputLength = value;
}
}
public uint InBufferCount
{
get
{
if(!isOpen) return 0;
return prxBuffer;
}
}
public uint OutBufferCount
{
get
{
if(!isOpen) return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -