📄 serialport.cs
字号:
{
get
{
return rtsEnable;
}
set
{
if (isOpen)
internalSerialStream.RtsEnable = value;
rtsEnable = value;
}
}
// StopBits represented in C# as StopBits enum type and in Win32 as an integer 1, 2, or 3.
[Browsable(true),
DefaultValue(defaultStopBits),
Description("The number of stop bits per transmitted/received byte.")]
public StopBits StopBits
{
get
{
return stopBits;
}
set
{
if (isOpen)
internalSerialStream.StopBits = value;
stopBits = value;
}
}
// timeout for all write operations. May be set to SerialPort.InfiniteTimeout or any positive value
[Browsable(true),
DefaultValue(defaultWriteTimeout),
Description("Milliseconds after any write operation starts before timeout if no data transmitted.")]
public int WriteTimeout
{
get
{
return writeTimeout;
}
set
{
if (isOpen)
internalSerialStream.WriteTimeout = value;
writeTimeout = value;
}
}
// -------- SECTION: constructors -----------------*
public SerialPort(System.ComponentModel.IContainer container)
{
///
/// Required for Windows.Forms Class Composition Designer support
///
container.Add(this);
}
public SerialPort()
{
}
// Non-design SerialPort constructors here chain, using default values for members left unspecified by parameters
// Note: Calling SerialPort() does not open a port connection but merely instantiates an object.
// : A connection must be made using SerialPort's Open() method.
public SerialPort(string resource) : this (resource, defaultBaudRate, defaultParity, defaultDataBits, defaultStopBits)
{
}
public SerialPort(string resource, int baudRate) : this (resource, baudRate, defaultParity, defaultDataBits, defaultStopBits)
{
}
public SerialPort(string resource, int baudRate, Parity parity) : this (resource, baudRate, parity, defaultDataBits, defaultStopBits)
{
}
public SerialPort(string resource, int baudRate, Parity parity, int dataBits) : this (resource, baudRate, parity, dataBits, defaultStopBits)
{
}
// all the magic happens in the call to the instance's .Open() method.
// Internally, the SerialStream constructor opens the file handle, sets the device
// control block and associated Win32 structures, and begins the event-watching cycle.
public SerialPort(string resource, int baudRate, Parity parity, int dataBits, StopBits stopBits)
{
// RAD paradigm allows us to set these to ANYTHING, so long as .Open() checks for
// invalid parameters.
this.portName = resource;
this.baudRate = baudRate;
this.parity = parity;
this.dataBits = dataBits;
this.StopBits = stopBits;
}
// DEFAULT DESTRUCTOR used here.
protected override void Dispose( bool disposing )
{
if( disposing )
{
internalSerialStream.Flush();
}
if (isOpen == false) Close();
base.Dispose( disposing );
}
// ----- SECTION: public methods
public void ClearBreak()
{
if (!isOpen)
throw new InvalidOperationException("ClearBreak - port not open");
internalSerialStream.ClearBreak();
inBreak = false;
}
// Calls internal Serial Stream's Close() method on the internal Serial Stream.
public void Close()
{
if (!isOpen)
throw new InvalidOperationException("Serial Port - port already closed!");
if (internalSerialStream != null)
{
internalSerialStream.Close();
internalSerialStream = null;
}
isOpen = false;
}
public void DiscardInBuffer()
{
if (!isOpen)
throw new InvalidOperationException("DiscardInBuffer - port not open");
internalSerialStream.DiscardInBuffer();
}
public void DiscardOutBuffer()
{
if (!isOpen)
throw new InvalidOperationException("DiscardOutBuffer - port not open");
internalSerialStream.DiscardOutBuffer();
}
// SerialPort is open <=> SerialPort has an associated SerialStream.
// The two statements are functionally equivalent here, so this method basically calls underlying Stream's
// constructor from the main properties specified in SerialPort: baud, stopBits, parity, dataBits,
// comm resource, handshaking, and timeouts.
public void Open()
{
if (isOpen)
throw new InvalidOperationException("Serial Port - port already open!");
// Equivalent error checking done here and in properties individually.
if (parity < Parity.None || parity > Parity.Space)
throw new ArgumentOutOfRangeException("parity", InternalResources.GetResourceString("ArgumentOutOfRange_Enum"));
if (dataBits < minDataBits || dataBits > maxDataBits)
throw new ArgumentOutOfRangeException("dataBits",
InternalResources.GetResourceString("ArgumentOutOfRange_Bounds_Lower_Upper", minDataBits, maxDataBits));
if (stopBits < StopBits.One || stopBits > StopBits.OnePointFive)
throw new ArgumentOutOfRangeException("stopBits", InternalResources.GetResourceString("ArgumentOutOfRange_Enum"));
if (baudRate <= 0)
throw new ArgumentOutOfRangeException("baudRate", InternalResources.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
if (portName == null)
throw new ArgumentNullException("resource", InternalResources.GetResourceString("ArgumentNull_String"));
if (handshake < Handshake.None || handshake > Handshake.RequestToSendXOnXOff)
throw new ArgumentOutOfRangeException("handshake", InternalResources.GetResourceString("ArgumentOutOfRange_Enum"));
if (readTimeout < 0 && readTimeout != SerialPort.InfiniteTimeout)
throw new ArgumentOutOfRangeException("readTimeout", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (writeTimeout <= 0 && writeTimeout != SerialPort.InfiniteTimeout)
throw new ArgumentOutOfRangeException("writeTimeout", InternalResources.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
// disallow access to device resources beginning with @"\\", instead requiring "COM2:", etc.
// Note that this still allows freedom in mapping names to ports, etc., but blocks security leaks.
if (portName.StartsWith("\\\\"))
throw new ArgumentException("resource", InternalResources.GetResourceString("Arg_SecurityException"));
internalSerialStream = new SerialStream(portName, baudRate, parity, dataBits, stopBits, readTimeout,
writeTimeout, handshake, dtrEnable, rtsEnable, discardNull, parityReplace);
internalSerialStream.ErrorEvent += new SerialEventHandler(CatchErrorEvents);
internalSerialStream.PinChangedEvent += new SerialEventHandler(CatchPinChangedEvents);
internalSerialStream.ReceivedEvent += new SerialEventHandler(CatchReceivedEvents);
isOpen = true;
}
// Read Design pattern:
// : Read() returns -1 on timeout, or the first available full char if found before.
// : Read(byte[] buffer..., int count) returns all data available before read timeout expires up to *count* bytes
// : Read(char[] buffer..., int count) returns all data available before read timeout expires up to *count* chars.
// : Note, this does not return "half-characters".
// : ReadByte() is the binary analogue of the first one.
// : ReadLine(): returns null string on timeout, saves received data in buffer
// : ReadAvailable(): returns all full characters which are IMMEDIATELY available.
public int Read(byte[] buffer, int offset, int count)
{
if (!isOpen)
throw new InvalidOperationException("Serial Port Read - port not open");
if (buffer==null)
throw new ArgumentNullException("buffer", InternalResources.GetResourceString("ArgumentNull_Buffer"));
if (offset < 0)
throw new ArgumentOutOfRangeException("offset", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException("count", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (buffer.Length - offset < count)
throw new ArgumentException(InternalResources.GetResourceString("Argument_InvalidOffLen"));
int beginReadPos = readPos;
byte [] tempReturnBuffer = new byte[count];
// if any bytes available in internal buffer, return those without calling any read ops.
if (readLen - readPos >= 1)
{
int min = (readLen - readPos < count) ? readLen - readPos : count;
Buffer.BlockCopy(inBuffer, readPos, buffer, 0, min);
readPos += min;
if (min == count) {
if (readPos == readLen) readPos = readLen = 0; // just a check to see if we can reset buffer
return count;
}
// if we have read some bytes but there's none immediately available, return.
if (InBufferBytes == 0) return min;
}
int bytesLeftToRead = count - (readPos - beginReadPos);
// check that internal buffer is big enough to hold incoming bytes before requesting.
if (bytesLeftToRead + readLen >= inBuffer.Length) ResizeBuffer();
// request to read the requested number of bytes to fulfill the contract,
// doesn't matter if we time out. We still return all the data we have available.
int returnCount = internalSerialStream.Read(inBuffer, readLen, bytesLeftToRead);
// Return all we have. This will leave nothing left in the inbuffer, and will not be more than *count*.
Buffer.BlockCopy(inBuffer, beginReadPos, buffer, offset, returnCount + (readPos - beginReadPos));
readLen = readPos = 0;
return returnCount + readPos - beginReadPos; // return the number of bytes we threw into the buffer plus what we had.
}
// publicly exposed "ReadOneChar"-type: Read()
// reads one full character from the stream
public int Read()
{
return ReadOneChar(readTimeout);
}
// gets next available full character, which may be from the buffer, the stream, or both.
// this takes size^2 time at most, where *size* is the maximum size of any one character in an encoding.
// The user can call Read(1) to mimic this functionality.
private int ReadOneChar(int timeout)
{
int beginReadPos = readPos;
int nextByte;
int timeUsed = 0;
Debug.Assert(isOpen, "ReadOneChar - port not open");
// simply return if we have no time rather than throw exception, since we have
// to use this multiple times for one ReadLine() call, and our toes might end up
// just over the edge.
if (timeout < 0 && timeout != SerialPort.InfiniteTimeout) return -1;
// case 1: we have >= 1 character in the internal buffer.
if (encoding.GetCharCount(inBuffer, readPos, readLen - readPos) != 0)
{
// get characters from buffer.
do
{
nextByte = (int) inBuffer[readPos++];
} while (encoding.GetCharCount(inBuffer, beginReadPos, readPos - beginReadPos) < 1);
encoding.GetChars(inBuffer, beginReadPos, readPos - beginReadPos, oneChar, 0);
return oneChar[0];
}
else
{
// need to return immediately.
if (timeout == 0) {
// read all bytes in the serial driver in here.
if (InBufferBytes + readPos >= inBuffer.Length) ResizeBuffer();
int bytesRead = internalSerialStream.Read(inBuffer, readLen, InBufferBytes - (readLen - readPos)); // read all immediately avail.
readLen += bytesRead;
// check all we have in the buffer - if not enough, return -1
if (ReadBufferIntoChars(oneChar, 0, 1) == 0) return -1;
else return oneChar[0];
}
// case 2: we need to read from outside to find this.
// timeout is either infinite or positive.
int startTicks = SafeNativeMethods.GetTickCount();
do
{
timeUsed = SafeNativeMethods.GetTickCount() - startTicks;
if (timeout != SerialPort.InfiniteTimeout && (timeout - timeUsed <= 0))
{
nextByte = -1;
break; // break for timeouts
}
nextByte = internalSerialStream.ReadByte((timeout == InfiniteTimeout) ? InfiniteTimeout : timeout - timeUsed);
if (nextByte == -1) break; // timed out
if (readLen >= inBuffer.Length) ResizeBuffer();
inBuffer[readLen++] = (byte) nextByte; // we must add to the end of the buffer
readPos++;
} while (encoding.GetCharCount(inBuffer, beginReadPos, readPos - beginReadPos) < 1);
}
if (nextByte == -1) return -1; // timed out, return -1
encoding.GetChars(inBuffer, beginReadPos, readPos - beginReadPos, oneChar, 0);
return oneChar[0];
}
// return value is either a valid byte from the buffer or stream, or -1 if a timeout occurs.
// reads as many full characters as available
public int Read(char[] buffer, int offset, int count)
{
if (buffer==null)
throw new ArgumentNullException("buffer", InternalResources.GetResourceString("ArgumentNull_Buffer"));
if (offset < 0)
throw new ArgumentOutOfRangeException("offset", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException("count", InternalResources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
if (buffer.Length - offset < count)
throw new ArgumentException(InternalResources.GetResourceString("Argument_InvalidOffLen"));
if (!isOpen)
throw new InvalidOperationException("Serial Port Read - port not open");
if (count == 0) return 0; // immediately return on zero chars desired. This simplifies things later.
// read everything else into internal buffer, which we know we can do instantly, and see if we NOW have enough.
int bytesInDriver = InBufferBytes - (readLen - readPos);
if (readLen + bytesInDriver >= inBuffer.Length) ResizeBuffer();
internalSerialStream.Read(inBuffer, readLen, bytesInDriver); // should execute instantaneously.
readLen += bytesInDriver;
int charsWeAlreadyHave = encoding.GetCharCount(inBuffer, readPos, readLen - readPos); // full chars already in our buffer
if (charsWeAlreadyHave >= count)
{
return ReadBufferIntoChars(buffer, offset, count);
}
// else: we need to do incremental reads from the stream.
// -----
// our internal algorithm for finding exactly n characters is a bit complicated, but must overcome the
// hurdle of NEVER READING TOO MANY BYTES from the Stream, since we can time out. A variable-length encoding
// allows anywhere between minimum and maximum bytes per char times number of chars to be the exactly correct
// target, and we have to take care not to overuse GetCharCount(). The problem is that GetCharCount() will never tell
// us if we've read "half" a character in our current set of collected bytes; it underestimates.
// size = maximum bytes per character in the encoding. n = number of characters requested.
// Solution I: Use ReadOneChar() to read successive characters until we get to n.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -