C # SerialPort - RS485 Как получить все данные в одной части - PullRequest
0 голосов
/ 29 августа 2018

У меня есть устройство, которое обменивается данными через 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, но без изменений.

1 Ответ

0 голосов
/ 29 августа 2018

Проблема здесь в том, что когда вы вызываете 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;
}

Имейте в виду, что реализация .Net SerialPort является ненормальной - подробности см. в этой статье .

...