?? commbase.cs
字號:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.IO;
using System.Xml.Serialization;
namespace JH.CommBase
{
/// <summary>
/// Lowest level Com driver handling all Win32 API calls and processing send and receive in terms of
/// individual bytes. Used as a base class for higher level drivers.
/// </summary>
public abstract class CommBase : IDisposable
{
private IntPtr hPort;
private IntPtr ptrUWO = IntPtr.Zero;
private Thread rxThread = null;
private bool online = false;
private bool auto = false;
private bool checkSends = true;
private Exception rxException = null;
private bool rxExceptionReported = false;
private int writeCount = 0;
private ManualResetEvent writeEvent = new ManualResetEvent(false);
private int stateRTS = 2;
private int stateDTR = 2;
private int stateBRK = 2;
/// <summary>
/// Parity settings
/// </summary>
public enum Parity
{
/// <summary>
/// Characters do not have a parity bit.
/// </summary>
none = 0,
/// <summary>
/// If there are an odd number of 1s in the data bits, the parity bit is 1.
/// </summary>
odd = 1,
/// <summary>
/// If there are an even number of 1s in the data bits, the parity bit is 1.
/// </summary>
even = 2,
/// <summary>
/// The parity bit is always 1.
/// </summary>
mark = 3,
/// <summary>
/// The parity bit is always 0.
/// </summary>
space = 4
};
/// <summary>
/// Stop bit settings
/// </summary>
public enum StopBits
{
/// <summary>
/// Line is asserted for 1 bit duration at end of each character
/// </summary>
one = 0,
/// <summary>
/// Line is asserted for 1.5 bit duration at end of each character
/// </summary>
onePointFive = 1,
/// <summary>
/// Line is asserted for 2 bit duration at end of each character
/// </summary>
two = 2
};
/// <summary>
/// Uses for RTS or DTR pins
/// </summary>
public enum HSOutput
{
/// <summary>
/// Pin is asserted when this station is able to receive data.
/// </summary>
handshake = 2,
/// <summary>
/// Pin is asserted when this station is transmitting data (RTS on NT, 2000 or XP only).
/// </summary>
gate = 3,
/// <summary>
/// Pin is asserted when this station is online (port is open).
/// </summary>
online = 1,
/// <summary>
/// Pin is never asserted.
/// </summary>
none = 0
};
/// <summary>
/// Standard handshake methods
/// </summary>
public enum Handshake
{
/// <summary>
/// No handshaking
/// </summary>
none,
/// <summary>
/// Software handshaking using Xon / Xoff
/// </summary>
XonXoff,
/// <summary>
/// Hardware handshaking using CTS / RTS
/// </summary>
CtsRts,
/// <summary>
/// Hardware handshaking using DSR / DTR
/// </summary>
DsrDtr
}
/// <summary>
/// Set the public fields to supply settings to CommBase.
/// </summary>
public class CommBaseSettings
{
/// <summary>
/// Port Name (default: "COM1:")
/// </summary>
public string port = "COM1:";
/// <summary>
/// Baud Rate (default: 2400) unsupported rates will throw "Bad settings"
/// </summary>
public int baudRate = 2400;
/// <summary>
/// The parity checking scheme (default: none)
/// </summary>
public Parity parity = Parity.none;
/// <summary>
/// Number of databits 1..8 (default: 8) unsupported values will throw "Bad settings"
/// </summary>
public int dataBits = 8;
/// <summary>
/// Number of stop bits (default: one)
/// </summary>
public StopBits stopBits = StopBits.one;
/// <summary>
/// If true, transmission is halted unless CTS is asserted by the remote station (default: false)
/// </summary>
public bool txFlowCTS = false;
/// <summary>
/// If true, transmission is halted unless DSR is asserted by the remote station (default: false)
/// </summary>
public bool txFlowDSR = false;
/// <summary>
/// If true, transmission is halted when Xoff is received and restarted when Xon is received (default: false)
/// </summary>
public bool txFlowX = false;
/// <summary>
/// If false, transmission is suspended when this station has sent Xoff to the remote station (default: true)
/// Set false if the remote station treats any character as an Xon.
/// </summary>
public bool txWhenRxXoff = true;
/// <summary>
/// If true, received characters are ignored unless DSR is asserted by the remote station (default: false)
/// </summary>
public bool rxGateDSR = false;
/// <summary>
/// If true, Xon and Xoff characters are sent to control the data flow from the remote station (default: false)
/// </summary>
public bool rxFlowX = false;
/// <summary>
/// Specifies the use to which the RTS output is put (default: none)
/// </summary>
public HSOutput useRTS = HSOutput.none;
/// <summary>
/// Specidies the use to which the DTR output is put (default: none)
/// </summary>
public HSOutput useDTR = HSOutput.none;
/// <summary>
/// The character used to signal Xon for X flow control (default: DC1)
/// </summary>
public ASCII XonChar = ASCII.DC1;
/// <summary>
/// The character used to signal Xoff for X flow control (default: DC3)
/// </summary>
public ASCII XoffChar = ASCII.DC3;
/// <summary>
/// The number of free bytes in the reception queue at which flow is disabled (default: 2048)
/// </summary>
public int rxHighWater = 2048;
/// <summary>
/// The number of bytes in the reception queue at which flow is re-enabled (default: 512)
/// </summary>
public int rxLowWater = 512;
/// <summary>
/// Multiplier. Max time for Send in ms = (Multiplier * Characters) + Constant
/// (default: 0 = No timeout)
/// </summary>
public int sendTimeoutMultiplier = 0;
/// <summary>
/// Constant. Max time for Send in ms = (Multiplier * Characters) + Constant (default: 0)
/// </summary>
public int sendTimeoutConstant = 0;
/// <summary>
/// Requested size for receive queue (default: 0 = use operating system default)
/// </summary>
public int rxQueue = 0;
/// <summary>
/// Requested size for transmit queue (default: 0 = use operating system default)
/// </summary>
public int txQueue = 0;
/// <summary>
/// If true, the port will automatically re-open on next send if it was previously closed due
/// to an error (default: false)
/// </summary>
public bool autoReopen = false;
/// <summary>
/// If true, subsequent Send commands wait for completion of earlier ones enabling the results
/// to be checked. If false, errors, including timeouts, may not be detected, but performance
/// may be better.
/// </summary>
public bool checkAllSends = true;
/// <summary>
/// Pre-configures settings for most modern devices: 8 databits, 1 stop bit, no parity and
/// one of the common handshake protocols. Change individual settings later if necessary.
/// </summary>
/// <param name="Port">The port to use (i.e. "COM1:")</param>
/// <param name="Baud">The baud rate</param>
/// <param name="Hs">The handshake protocol</param>
public void SetStandard(string Port, int Baud, Handshake Hs)
{
dataBits = 8; stopBits = StopBits.one; parity = Parity.none;
port = Port; baudRate = Baud;
switch (Hs)
{
case Handshake.none:
txFlowCTS = false; txFlowDSR = false; txFlowX = false;
rxFlowX = false; useRTS = HSOutput.online; useDTR = HSOutput.online;
txWhenRxXoff = true; rxGateDSR = false;
break;
case Handshake.XonXoff:
txFlowCTS = false; txFlowDSR = false; txFlowX = true;
rxFlowX = true; useRTS = HSOutput.online; useDTR = HSOutput.online;
txWhenRxXoff = true; rxGateDSR = false;
XonChar = ASCII.DC1; XoffChar = ASCII.DC3;
break;
case Handshake.CtsRts:
txFlowCTS = true; txFlowDSR = false; txFlowX = false;
rxFlowX = false; useRTS = HSOutput.handshake; useDTR = HSOutput.online;
txWhenRxXoff = true; rxGateDSR = false;
break;
case Handshake.DsrDtr:
txFlowCTS = false; txFlowDSR = true; txFlowX = false;
rxFlowX = false; useRTS = HSOutput.online; useDTR = HSOutput.handshake;
txWhenRxXoff = true; rxGateDSR = false;
break;
}
}
/// <summary>
/// Save the object in XML format to a stream
/// </summary>
/// <param name="s">Stream to save the object to</param>
public void SaveAsXML(Stream s)
{
XmlSerializer sr = new XmlSerializer(this.GetType());
sr.Serialize(s, this);
}
/// <summary>
/// Create a new CommBaseSettings object initialised from XML data
/// </summary>
/// <param name="s">Stream to load the XML from</param>
/// <returns>CommBaseSettings object</returns>
public static CommBaseSettings LoadFromXML(Stream s)
{
return LoadFromXML(s, typeof(CommBaseSettings));
}
/// <summary>
/// Create a new object loading members from the stream in XML format.
/// Derived class should call this from a static method i.e.:
/// return (ComDerivedSettings)LoadFromXML(s, typeof(ComDerivedSettings));
/// </summary>
/// <param name="s">Stream to load the object from</param>
/// <param name="t">Type of the derived object</param>
/// <returns></returns>
protected static CommBaseSettings LoadFromXML(Stream s, Type t)
{
XmlSerializer sr = new XmlSerializer(t);
try
{
return (CommBaseSettings)sr.Deserialize(s);
}
catch
{
return null;
}
}
}
/// <summary>
/// Byte type with enumeration constants for ASCII control codes.
/// </summary>
public enum ASCII : byte
{
NULL = 0x00,SOH = 0x01,STH = 0x02,ETX = 0x03,EOT = 0x04,ENQ = 0x05,ACK = 0x06,BELL = 0x07,
BS = 0x08,HT = 0x09,LF = 0x0A,VT = 0x0B,FF = 0x0C,CR = 0x0D,SO = 0x0E,SI = 0x0F,DC1 = 0x11,
DC2 = 0x12,DC3 = 0x13,DC4 = 0x14,NAK = 0x15,SYN = 0x16,ETB = 0x17,CAN = 0x18,EM = 0x19,
SUB = 0x1A,ESC = 0x1B,FS = 0x1C,GS = 0x1D,RS = 0x1E,US = 0x1F,SP = 0x20,DEL = 0x7F
}
/// <summary>
/// Opens the com port and configures it with the required settings
/// </summary>
/// <returns>false if the port could not be opened</returns>
public bool Open()
{
Win32Com.DCB PortDCB = new Win32Com.DCB();
Win32Com.COMMTIMEOUTS CommTimeouts = new Win32Com.COMMTIMEOUTS();
CommBaseSettings cs;
Win32Com.OVERLAPPED wo = new Win32Com.OVERLAPPED();
if (online) return false;
cs = CommSettings();
hPort = Win32Com.CreateFile(cs.port, Win32Com.GENERIC_READ | Win32Com.GENERIC_WRITE, 0, IntPtr.Zero,
Win32Com.OPEN_EXISTING, Win32Com.FILE_FLAG_OVERLAPPED, IntPtr.Zero);
if (hPort == (IntPtr)Win32Com.INVALID_HANDLE_VALUE)
{
if (Marshal.GetLastWin32Error() == Win32Com.ERROR_ACCESS_DENIED)
{
return false;
}
else
{
throw new CommPortException("Port Open Failure");
}
}
online = true;
CommTimeouts.ReadIntervalTimeout = 0;
CommTimeouts.ReadTotalTimeoutConstant = 0;
CommTimeouts.ReadTotalTimeoutMultiplier = 0;
CommTimeouts.WriteTotalTimeoutConstant = cs.sendTimeoutConstant;
CommTimeouts.WriteTotalTimeoutMultiplier = cs.sendTimeoutMultiplier;
PortDCB.init(((cs.parity == Parity.odd) || (cs.parity == Parity.even)), cs.txFlowCTS, cs.txFlowDSR,
(int)cs.useDTR, cs.rxGateDSR, !cs.txWhenRxXoff, cs.txFlowX, cs.rxFlowX, (int)cs.useRTS);
PortDCB.BaudRate = cs.baudRate;
PortDCB.ByteSize = (byte)cs.dataBits;
PortDCB.Parity = (byte)cs.parity;
PortDCB.StopBits = (byte)cs.stopBits;
PortDCB.XoffChar = (byte)cs.XoffChar;
PortDCB.XonChar = (byte)cs.XonChar;
PortDCB.XoffLim = (short)cs.rxHighWater;
PortDCB.XonLim = (short)cs.rxLowWater;
if ((cs.rxQueue != 0) || (cs.txQueue != 0))
if (!Win32Com.SetupComm(hPort, (uint)cs.rxQueue, (uint)cs.txQueue)) ThrowException("Bad queue settings");
if (!Win32Com.SetCommState(hPort, ref PortDCB)) ThrowException("Bad com settings");
if (!Win32Com.SetCommTimeouts(hPort, ref CommTimeouts)) ThrowException("Bad timeout settings");
stateBRK = 0;
if (cs.useDTR == HSOutput.none) stateDTR = 0;
if (cs.useDTR == HSOutput.online) stateDTR = 1;
if (cs.useRTS == HSOutput.none) stateRTS = 0;
if (cs.useRTS == HSOutput.online) stateRTS = 1;
checkSends = cs.checkAllSends;
wo.Offset = 0;
wo.OffsetHigh = 0;
if (checkSends)
wo.hEvent = writeEvent.Handle;
else
wo.hEvent = IntPtr.Zero;
ptrUWO = Marshal.AllocHGlobal(Marshal.SizeOf(wo));
Marshal.StructureToPtr(wo, ptrUWO, true);
writeCount = 0;
rxException = null;
rxExceptionReported = false;
rxThread = new Thread(new ThreadStart(this.ReceiveThread));
rxThread.Name = "CommBaseRx";
rxThread.Priority = ThreadPriority.AboveNormal;
rxThread.Start();
Thread.Sleep(1); //Give rx thread time to start. By documentation, 0 should work, but it does not!
auto = false;
if (AfterOpen())
{
auto = cs.autoReopen;
return true;
}
else
{
Close();
return false;
}
}
/// <summary>
/// Closes the com port.
/// </summary>
public void Close()
{
if (online)
{
auto = false;
BeforeClose(false);
InternalClose();
rxException = null;
}
}
private void InternalClose()
{
Win32Com.CancelIo(hPort);
if (rxThread != null)
{
rxThread.Abort();
rxThread = null;
}
Win32Com.CloseHandle(hPort);
if (ptrUWO != IntPtr.Zero) Marshal.FreeHGlobal(ptrUWO);
stateRTS = 2;
stateDTR = 2;
stateBRK = 2;
online = false;
}
/// <summary>
/// For IDisposable
/// </summary>
public void Dispose() {Close();}
/// <summary>
/// Destructor (just in case)
/// </summary>
~CommBase() {Close();}
/// <summary>
/// True if online.
/// </summary>
public bool Online {get {if (!online) return false; else return CheckOnline();}}
/// <summary>
/// Block until all bytes in the queue have been transmitted.
/// </summary>
public void Flush() {
CheckOnline();
CheckResult();
}
/// <summary>
/// Use this to throw exceptions in derived classes. Correctly handles threading issues
/// and closes the port if necessary.
/// </summary>
/// <param name="reason">Description of fault</param>
protected void ThrowException(string reason)
{
if (Thread.CurrentThread == rxThread)
{
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -