⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serialport.cs

📁 SerialPort 串口通讯用
💻 CS
📖 第 1 页 / 共 3 页
字号:
			// Read calls: size * n; GetCharCount calls: size * n; each byte "counted": size times.
			// Solution II: Use a binary reduction and backtracking to reduce the number of calls.
			// Read calls: size * log n; GetCharCount calls: size * log n; each byte "counted": size * (log n) / n times.
			// We use the second, more complicated solution here.  Note log is actually log_(size/size - 1)...
		
			
			// we need to read some from the stream
			// read *up to* the maximum number of bytes from the stream
			// we can read more since we receive everything instantaneously, and we don't have enough,
			// so when we do receive any data, it will be necessary and sufficient.
			
			if (readTimeout == 0) return ReadBufferIntoChars(buffer, offset, count);
			 
			int startTicks = SafeNativeMethods.GetTickCount();
			int justRead;
			do {
				internalSerialStream.Read(inBuffer, readLen, Encoding.GetMaxByteCount(count - charsWeAlreadyHave));
				justRead = ReadBufferIntoChars(buffer, offset, count);
				if (justRead > 0) return justRead;
			} while (readTimeout == SerialPort.InfiniteTimeout || readTimeout - (SafeNativeMethods.GetTickCount() - startTicks) > 0);
			
			// must've timed out w/o getting a character.
			return 0;
		}

		
		// ReadBufferIntoChars reads from Serial Port's inBuffer up to *count* chars and 
		// places them in *buffer* starting at *offset*.
		// This does not call any stream Reads, and so takes "no time".
		private int ReadBufferIntoChars(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;
			
			// variables required for Binary-Reduce-Read algorithm developed below.
			int totalBytesRead = 0;	// total Bytes read from outside the buffer, i.e. the stream 
			int totalCharsRead = 0;	// total chars read from outside the stream, => totalCharsRead <= totalBytesRead
			int totalBytesJustRead; // total bytes read on any one internalSerialStream.Read call
			int totalCharsJustRead; // total chars COMPLETED by any one such Read call, => totalCharsJustRead <= totalBytesJustRead
			int lastFullCharPos = readPos; // first index AFTER last full char read, capped at ReadLen.
			int backtrack = 0;	// for backtracking part of the algorithm, temporary index walking back from the end of what we've read.
			
		
			if (encoding.GetMaxByteCount(1) == 1) 
			{		// kill ASCII/ANSI encoding easily.
				// read at least one and at most *count* characters
				int bytesToRead = (count < (readLen - readPos) ? count : readLen - readPos);
				 
				encoding.GetChars(inBuffer, readPos, bytesToRead, buffer, offset);
								
				readPos += bytesToRead;
				if (readPos == readLen) readPos = readLen = 0;
				return bytesToRead;
			} 
			else 
			{
				do 
				{
					backtrack = 0;
					// "read" count - totalCharsRead, one byte for each expected char.
					totalBytesJustRead = count - totalCharsRead;	// if there are count - totalChars Read chars left, we can safely read that many bytes.
					totalBytesRead += totalBytesJustRead;	// here totalBytesRead means total examined in inBuffer.
					totalCharsJustRead = encoding.GetCharCount(inBuffer, lastFullCharPos, readPos + totalBytesRead - lastFullCharPos);
					if (totalCharsJustRead > 0) 
					{
						// go backwards until we know we have a full set of totalCharsJustRead bytes with no extra lead-bytes.
						do 
						{
							backtrack += 1;
						} while (encoding.GetCharCount(inBuffer, lastFullCharPos, readPos + totalBytesRead - lastFullCharPos - backtrack) == totalCharsJustRead);
						lastFullCharPos = readPos + totalBytesRead - backtrack + 1;	// go back to starting position of last known char.
						totalCharsRead += totalCharsJustRead;
					}
				} while (totalCharsRead < count); 
				
				// fill into destination buffer all the COMPLETE characters we've read.		
				int numCharsRead = encoding.GetChars(inBuffer, readPos, lastFullCharPos - readPos, buffer, offset);
				readPos = lastFullCharPos;
				
				if (readPos == readLen) readPos = readLen = 0;
				return numCharsRead;
			}
		}
		
		public int ReadByte() 
		{
			if (!isOpen) 
				throw new InvalidOperationException("Serial Port Read - port not open");
			if (readLen != readPos) 		// stuff left in buffer, so we can read from it
				return inBuffer[readPos++];	
			
			return internalSerialStream.ReadByte(); // otherwise, ask the stream.
		}	
	
		public string ReadAvailable() 
		{
			byte [] bytesReceived = new byte[InBufferBytes];

			if (readPos < readLen)	
			{			// stuff in internal buffer
				Buffer.BlockCopy(inBuffer, readPos, bytesReceived, 0, readLen - readPos);
			}
			internalSerialStream.Read(bytesReceived, readLen - readPos, bytesReceived.Length - (readLen - readPos));	// get everything
			int numCharsReceived = Encoding.GetCharCount(bytesReceived);	
			int lastFullCharIndex = bytesReceived.Length;
			
			if (numCharsReceived == 0) 
			{
				Buffer.BlockCopy(bytesReceived, 0, inBuffer, 0, bytesReceived.Length); // put it all back!
				// don't change readPos. --> readPos == 0?
				readPos = 0;
				readLen = bytesReceived.Length;
				return "";
			}
				
			do 
			{
				lastFullCharIndex--;
			} while (Encoding.GetCharCount(bytesReceived, 0, lastFullCharIndex) == numCharsReceived);
			
		
			// we cleared the buffers above, so we can reset, but we needn't resize, since it was originally
			// big enough to fit our trailing characters AND MORE.
			readPos = readLen = 0;
			
			Buffer.BlockCopy(bytesReceived, lastFullCharIndex + 1, inBuffer, 0, bytesReceived.Length - (lastFullCharIndex + 1));
			return Encoding.GetString(bytesReceived, 0, lastFullCharIndex + 1);
		}

	
		//  Design discussion:
		//  Problem: if '\r' occurs at the end of available stream data, should we wait for the potential '\n'?	
		//	Solution: We have chosen to NOT wait for \n if \r occurs at the end of a ReadLine().
		//	Additionally, we have chosen to essentially "forget" about the potential '\n' following any terminating \r,
		//	NOT remembering through internal state that '\r' occurred as the most-recently read char, and in a ReadLine() call.
		//	Cons: 1. This means we may drop '\n' onto another read, possibly binary, if it follows in a subsequent transmission but
		//			was intended to be read by this ReadLine().
		//		2. We thus give users the feeling we're corrupting their data, which may be happening.
		//	Pros: 1. We don't hang until timeout if we get "xxx\r"
		//		2. We don't remove any '\n' text patterns or 0x0a (= 10 = '\n') from tranmissions to which a
		//			leading '\n' should be grouped.
		//		3. We do not clutter an otherwise intuitive, self-explanatory SerialPort API with a property requiring users to read documentation.
		//		4. We recongize all flavors of newline: '\r', '\n', "\r\n".
		//		5. We follow the pattern of the StreamReader class in Pro #4, and in disregarding an absent line feed char.
		 
		 
		public string ReadLine() 
		{
			if (!isOpen) 
				throw new InvalidOperationException("Serial Port Read - port not open");
            	
			string inBufferString;
			bool carriageReturnFlag = false;
			int startTicks = SafeNativeMethods.GetTickCount();
			int lastChar;	// holds return of ReadOneChar(), which may be a timeout indicator, so we need an int.
			int beginReadPos = readPos;
			
			// store encoding-based byte lengths of carraige return, line feed characters.
			char [] charTestBuf = new char[1];
			charTestBuf[0] = '\r';
			int crLength = encoding.GetByteCount(charTestBuf);
			charTestBuf[0] = '\n'; 
			int lfLength = encoding.GetByteCount(charTestBuf);					
			int timeUsed = 0;
			int timeNow;
			
			// for timeout issues, best to read everything already on the stream into our buffers.
			readLen += internalSerialStream.Read(inBuffer, readLen, InBufferBytes - (readLen - readPos));
			
			// read through the buffer one *character* at a time to find '\r', '\n', or '\r\n'
			while (true) 
			{
				timeNow = SafeNativeMethods.GetTickCount();
				lastChar = ReadOneChar((readTimeout == InfiniteTimeout) ? InfiniteTimeout : readTimeout - timeUsed);
				timeUsed += SafeNativeMethods.GetTickCount() - timeNow;
				
				if (lastChar == -1) break;	// we timed out.
				
				// note, we assume this for all encodings, true for Unicode, ASCII, UTF7, UTF8.
				
				if ((char) lastChar == '\r') 
				{	
					if (InBufferBytes == 0) 
					{
						// return string representation of all characters UP TO '\r'
						inBufferString = encoding.GetString(inBuffer, beginReadPos, readPos - beginReadPos - crLength);
						readPos = readLen = 0;	// reset read buffer, since we're at the very end
						return inBufferString;
					} 
					else if (carriageReturnFlag == true) 
					{
						inBufferString = encoding.GetString(inBuffer, beginReadPos, readPos - beginReadPos - 2 * crLength);
						// "unread" last non-linefeed character, which means we can't set it to zero beforehand in ReadOneChar().
						readPos -= crLength;	
						return inBufferString;
					} 
					else 
					{
						// wait to look at next char, to see if it's '\n'.  We're returning after next pass-through.  
						carriageReturnFlag = true; 
					} 
				} 
				else if ((char) lastChar == '\n') 
				{
					// case: we found '\r\n'.  Return everything up to, not including, those characters.
					if (carriageReturnFlag == true) 
					{
						inBufferString = encoding.GetString(inBuffer, beginReadPos, readPos - beginReadPos - crLength - lfLength);
						// readPos incremented in ReadOneChar(), so do not do it here.
						if (readPos == readLen) readPos = readLen = 0;
						return inBufferString;
					} 
					else 
					{
						// case: we found '\n'.  Return everything up to, not including, that character.
						inBufferString = encoding.GetString(inBuffer, beginReadPos, readPos - beginReadPos  - lfLength);
						// readPos incremented in ReadOneChar(), so don't do it here.
						if (readPos == readLen) readPos = readLen = 0;
						return inBufferString;
					}
				} 
				else 
				{
					// here we have "peek"ed beyond '\r' to see if '\n' next, and we just found '\rX...', char x != '\n'.
					// Put X "back in the stream", return everything up to '\r'.
					if (carriageReturnFlag == true) 
					{
						charTestBuf[0] = (char) lastChar; 
						int lastCharLength = encoding.GetByteCount(charTestBuf);
						inBufferString = encoding.GetString(inBuffer, beginReadPos, readPos - beginReadPos - crLength - lastCharLength);
						// "unread" last non-linefeed character, which means we can't set it to zero beforehand in ReadOneChar().
						readPos -= lastCharLength;	
						return inBufferString;
					}
				}
				
			}
			// we broke out due to timeout.
			
			// need to reset read position, since ReadOneChar() advances it.
			readPos = beginReadPos;
			
			// Important note: we are necessarily in a time-out mode here, because even if "ABC\nDEF" occurs at the end of the stream, we
			// can only return ABC as full line, and then wait for whatever will complete DEF, since we have no reason to expect
			// that no more data exists.  We then save DEF in our buffer, which can be read char-by-char, or however the user desires.
			
			// we haven't found a line before time ran out, but we've already tossed everything into the read buffer
			return (string) null; 
		}
		
		
		public void SetBreak()
		{
			if (!isOpen)
				throw new InvalidOperationException("SetBreak - port not open");
			internalSerialStream.SetBreak();
			inBreak = true;
		}
		
			
		// Writes string to output, no matter string's length.
		public void Write(string str) 
		{
			if (!isOpen)
				throw new InvalidOperationException("Serial Port Write - port not open!");
			if (str == null) 
				throw new ArgumentNullException("write buffer", InternalResources.GetResourceString("ArgumentNull_String"));
			if (str.Length == 0) return;	
			byte [] bytesToWrite;
		
			bytesToWrite = encoding.GetBytes(str);
		
			internalSerialStream.Write(bytesToWrite, 0, bytesToWrite.Length, writeTimeout);
		}
		
		// encoding-dependent Write-chars method.
		// Probably as performant as direct conversion from ASCII to bytes, since we have to cast anyway (we can just call GetBytes)
		public void Write(char[] buffer, int offset, int count) {
			if (!isOpen)
				throw new InvalidOperationException("Serial Port Write - port not open!");
			if (buffer == null) 
				throw new ArgumentNullException("write buffer", InternalResources.GetResourceString("ArgumentNull_String"));
			if (buffer.Length == 0) return;
				
			byte [] byteArray = Encoding.GetBytes(buffer,offset, count);
			Write(byteArray, 0, byteArray.Length);
			
		}
		
		// Writes a specified section of a byte buffer to output.
		public void Write(byte[] buffer, int offset, int count)
		{
			if (!isOpen)
				throw new InvalidOperationException("Serial Port Write - 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"));
			if (buffer.Length == 0) return;
			
			internalSerialStream.Write(buffer, offset, count, writeTimeout);
		}


		// ----- SECTION: internal utility methods ----------------*
		
		// included here just to use the event filter to block unwanted invocations of the Serial Port's events.
		// Plus, this enforces the requirement on the received event that the number of buffered bytes >= receivedBytesThreshold
		private void CatchErrorEvents(object src, SerialEventArgs e) 
		{
			int eventsCaught = (int) e.EventType & (int) eventFilter; // nix any unwanted events.
			if ((eventsCaught & (int) (SerialEvents.Frame | SerialEvents.Overrun | SerialEvents.RxOver 
				| SerialEvents.RxParity | SerialEvents.TxFull)) != 0) 
			{
				ErrorEvent(src, e);
			}
		}
		
		private void CatchPinChangedEvents(object src, SerialEventArgs e) 
		{
			int eventsCaught = (int) e.EventType & (int) eventFilter; // nix any unwanted events.
			if (((eventsCaught & (int) (SerialEvents.CDChanged | SerialEvents.CtsChanged | SerialEvents.DsrChanged | SerialEvents.Ring | SerialEvents.Break)) != 0)) 
			{
				PinChangedEvent(src, e);
			}	
		}
		
		private void CatchReceivedEvents(object src, SerialEventArgs e)
		{
			int eventsCaught = (int) e.EventType & (int) eventFilter; // nix any unwanted events.
			int inBufferBytes = InBufferBytes;
			
			if (((eventsCaught & (int) (SerialEvents.ReceivedChars | SerialEvents.EofReceived)) != 0) 
				&& (InBufferBytes >= receivedBytesThreshold))
				ReceivedEvent(src, e);	// here, do your reading, etc.
		}

		
		// doubles size of buffer, copying data resident in old buffer to new buffer, like a C realloc() call.
		private void ResizeBuffer() 
		{
			Debug.Assert(inBuffer.Length >= readLen, "ResizeBuffer - readLen > inBuffer.Length");
			byte[] newBuffer = new byte[inBuffer.Length * 2];
			Buffer.BlockCopy(inBuffer, 0, newBuffer, 0, inBuffer.Length);
			inBuffer = newBuffer;
		}
			
		
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -