[英]C# SerialPort - RS485 How to receive all data in one part
我有一台通過RS485通信的設備,我需要在C#中進行環回,以接收我的數據並將其發送回去。 第一個字節包含有關即將到來的數據長度的信息。 有時,C#會在多個部分觸發事件。 我只需要分一部分收到它們,這樣我就可以一次性寄回。
var buffer = new byte[1000];
port = new SerialPort(ConfigParams.PORT, 115200, Parity.None, 8, StopBits.One);
port.Handshake = Handshake.None;
port.RtsEnable = true;
port.DtrEnable = true;
port.DataReceived += (sender, e) =>
{
var length = port.ReadByte();
Thread.Sleep(10);
var bytes = port.Read(buffer, 1, length);
buffer[0] = (byte)length;
port.Write(buffer, 0, length + 1);
mainForm.Log("Port (" + (length + 1).ToString() +"): " + Encoding.UTF8.GetString(buffer, 0, length + 1)+"\n");
mainForm.Log("Data was sended.\n");
};
第一步,我讀取第一個字節以獲取即將到來的字節數。 然后,我讀取其余字節。 如果我未在其中插入Thread.Sleep(10)行,則有時會在多個部分觸發Event DataReceived。 使用Thread.Sleep(10),每次都能正常工作。
你知道為什么會這樣嗎? 我希望如果我說我想讀取40個字節,SerialPort將嘗試接收所有這40個字節或發生異常。 我試圖更改屬性ReadTimeout,但沒有更改。
這里的問題是,當您調用SerialPort.Read()
,它將等待緩沖區中的至少一個字節,然后它將返回其具有的所有字節,直到指定長度的最大值。
這意味着它可能返回的金額少於要求的金額。
為了避免這個問題,您可以編寫與此類似的代碼(重要的一點是private blockingRead()
方法):
/// <summary>
/// Attempts to read <paramref name="count"/> bytes into <paramref name="buffer"/> starting at offset <paramref name="offset"/>.
/// If any individual port read times out, a <see cref="TimeoutException"/> will be thrown.
/// </summary>
/// <param name="buffer">The byte array to write the input to. </param>
/// <param name="offset">The offset in buffer at which to write the bytes. </param>
/// <param name="count">The number of bytes to read from the port.</param>
/// <param name="timeoutMilliseconds">
/// The timeout for each individual port read (several port reads may be issued to fulfil the total number of bytes required).
/// If this is -2 (the default) the current <see cref="ReadTimeout"/> value is used.
/// If this is -1 or <see cref="SerialPort.InfiniteTimeout"/>, an infinite timeout is used.
/// </param>
/// <exception cref="TimeoutException">Thrown if any individual port read times out.</exception>
public void BlockingRead(byte[] buffer, int offset, int count, int timeoutMilliseconds = SerialComPortTimeout.UseDefault)
{
if (timeoutMilliseconds < SerialComPortTimeout.UseDefault)
throw new ArgumentOutOfRangeException(nameof(timeoutMilliseconds),timeoutMilliseconds, $"{nameof(timeoutMilliseconds)} cannot be less than {SerialComPortTimeout.UseDefault}." );
int timeoutToRestore = setTimeoutAndReturnOriginal(timeoutMilliseconds);
try
{
blockingRead(buffer, offset, count);
}
finally
{
if (timeoutToRestore != SerialComPortTimeout.UseDefault)
this.ReadTimeout = timeoutToRestore;
}
}
private void blockingRead(byte[] buffer, int offset, int count)
{
while (count > 0)
{
// SerialPort.Read() blocks until at least one byte has been read, or SerialPort.ReadTimeout milliseconds
// have elapsed. If a timeout occurs a TimeoutException will be thrown.
// Because SerialPort.Read() blocks until some data is available this is not a busy loop,
// and we do NOT need to issue any calls to Thread.Sleep().
int bytesRead = _serialPort.Read(buffer, offset, count);
offset += bytesRead;
count -= bytesRead;
}
}
private int setTimeoutAndReturnOriginal(int timeoutMilliseconds)
{
int originalTimeout = this.ReadTimeout;
if ((timeoutMilliseconds != SerialComPortTimeout.UseDefault) && (originalTimeout != timeoutMilliseconds))
{
this.ReadTimeout = timeoutMilliseconds;
return originalTimeout;
}
return SerialComPortTimeout.UseDefault;
}
請注意, SerialPort
的.Net實現是flakey-有關詳細信息,請參閱本文 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.