Это в основном проблема производитель-потребитель , поэтому она должна быть основой для общего дизайна.
Вот несколько мыслей по этому поводу:
a) FIFO-буфер (очередь)
Прежде всего, у вас должен быть экземпляр поточно-ориентированной очереди (буфер FIFO) для каждого экземпляра вашего класса. Один поток получит данные и заполнит их, а другой прочитает данные потокобезопасным способом. Это только означает, что вам придется использовать блокировку при каждой операции постановки / снятия с очереди.
Очередь FIFO позволит вам одновременно обрабатывать данные в рабочем потоке, заполняя их из потока связи. Если вам необходимо получить много данных, вы можете удалить из очереди некоторые данные в рабочем потоке и проанализировать их до того, как все они будут получены. В противном случае вам нужно будет подождать, пока все данные не будут получены, чтобы проанализировать все сразу. В большинстве случаев вы не знаете, сколько данных вы должны получить, пока не начнете их анализировать.
b) Рабочий поток ожидает данных
Я бы создал рабочий поток, который будет ожидать сигнала о получении новых данных. Вы можете использовать ManualResetEvent.WaitOne(timeOut)
для тайм-аута, если какое-то время ничего не происходит. Когда данные получены, вам придется их анализировать, исходя из вашего текущего состояния - так что это будет реализация конечного автомата .
c) Абстракция порта
Для обработки различных типов портов вы можете заключить ваш последовательный порт в интерфейс, который может иметь по крайней мере следующие методы (я мог бы что-то забыть):
interface IPort
{
void Open();
void Close();
event EventHandler<DataEventArgs> DataReceived;
void Write(Data data);
}
Это поможет вам отделить конкретный код связи от конечного автомата.
Примечание:
(согласно Microsoft) Событие DataReceived не гарантируется для каждого полученного байта. Используйте свойство BytesToRead, чтобы определить, сколько данных осталось для чтения в буфере . Таким образом, вы можете создать свою собственную реализацию IPort, которая будет опрашивать SerialPort через регулярные промежутки времени, чтобы гарантировать, что вы не пропустите байт (есть вопрос SO, который уже решает эту проблему).
d) Получение данных
Чтобы получить данные, вам нужно будет прикрепить обработчик для события IPort.DataReceived
(или SerialPort.DataReceived
, если вы его не переносите) и поставить в очередь полученные данные в очередь внутри обработчика. В этом обработчике вы также установили бы упоминание ManualResetEvent
, чтобы уведомить рабочий поток о получении новых данных.