У меня есть устройство UART, и я пишу ему команду (через System.IO.Ports.SerialPort), а затем устройство сразу же ответит.
Итак, в основном мой подход:
->Write to SerialPort->await Task.Delay->Read from the Port.
//The port is open all the time.
public async byte[] WriteAndRead(byte[] message){
port.Write(command, 0, command.Length);
await Task.Delay(timeout);
var msglen = port.BytesToRead;
if (msglen > 0)
{
byte[] message = new byte[msglen];
int readbytes = 0;
while (port.Read(message, readbytes, msglen - readbytes) <= 0)
;
return message;
}
Это отлично работает на моем компьютере. Но если я попробую, например, на другом компьютере, свойство bytesToRead иногда не соответствует. В нем пустые байты или ответ не завершен. (Например, я получаю два байта, если я ожидаю один байт: 0xBB, 0x00 или 0x00, 0xBB)
Я также изучил событие SerialPort.DataReceived, но оно срабатывает слишком часто и (насколько я понимаю) не очень полезно для этого подхода к записи и чтению. (Как и жду ответа сразу от аппарата).
Есть ли лучший подход к чтению и записи?
Никогда не теряйте возвращаемое значение Read (). И не забудьте удалить вызов Task.Delay (), который просто скрывает ошибки в вашем коде. Исправить их совершенно невозможно.
Я описываю подход к обработке входящих данных здесь stackoverflow.com/questions/15124132/… Вам просто нужно было бы построить другую абстракцию, чтобы превратить ее в последовательность, или использовать что-то вроде реактивных расширений
@HansPassant: Дело в том, что у устройства есть задержка (где-то 150-700 мс). Обычно это означает, что без ожидания SerialPort.BytesToRead равен 0. (Это означает, что данных нет). Как-то мне нужно ждать данных.
Read () уже ожидает данных, у вас есть гарантия, что он вернет хотя бы 1 байт. Так что откладывание в одиночестве не дает ничего полезного. Вам вообще не нужен BytesToRead, просто передайте msglen - totalbytes. Увеличьте общее количество байтов на возвращаемое значение Read ().





Внимательно прочтите примечания в https://msdn.microsoft.com/en-us/library/ms143549(v=vs.110).aspx Не следует полагаться на значение BytesToRead для указания длины сообщения. Вы должны знать, сколько данных вы ожидаете прочитать, чтобы разложить сообщение. Кроме того, как заметил @ itsme85, вы не обновляете байты чтения, и поэтому вы всегда записываете полученные байты в начало вашего массива. Правильный код с обновлением readbytes должен выглядеть так:
int r;
while ((r = port.Read(message, readbytes, msglen - readbytes)) <= 0){
readbytes += r;
}
Однако за время, пока вы будете читать данные, может появиться больше данных, и ваше «сообщение» может быть неполным. Подумайте, чего вы хотите достичь.
Я выполнил исправление и ваше предложение. Теперь msglen - это ожидаемая длина в байтах. Дело в том, что иногда операция будет отключаться по таймауту, потому что иногда устройство получает 9 байт вместо 10.
затем оберните время другим временем + некоторая задержка, когда вы будете ждать, пока массив сообщений не заполнится.
int readbytes = 0; while (port.Read(message, readbytes, msglen - readbytes) <= 0);Так где же здесь обновитьreadbytes?