Данные от USB устройства в UI в реальном времени

 
0
 
.NET
ava
Aldegid | 24.08.2012, 09:57
Всем привет.

У меня есть USB устройство и мне нужно отображать данные от него в окне. Работает, но медленно.

Тему "отображение данных в окне в реальном времени" http://forum.vingrad.ru/topic-332264.html читал, но ответа не нашел.

У меня есть некое USB устройство, с которым я общаюсь через класс SerialPort. При открытии порту назначается скорость 115200. Мне нужно получать от него данные и отображать их в двух TextBox'ах в реальном времени. Я подписываюсь на событие DataReceived

_port.DataReceived += OnRunningDataReceived;

И прослушиваю устройство
      
// класс, общающийся с утройством
public partial class DeviceCommunicator
{
private void OnRunningDataReceived( Object sender, SerialDataReceivedEventArgs e )
{
// пока есть данные в буфере чтения устройства
while( _port.BytesToRead >= PacketSize )
{

// выделим буфер
Byte[] buffer = new Byte[PacketSize]; /* todo: оптимизировать выделение new каждый раз */
// попробуем прочитаем наш пакет измерений
if( !Read( buffer, 0, WeightPacketSize, _runningPacketErrorCallback ) )
// ...

// проверим маркеры и счетчик
// ... операции, которые не должны занимать много времени

// декодируем значения
// ... еще операции, которые не должны занимать много времени

// вызовем отдадим данные наружу
_runningPacketReceivedCallback( ws, pc ); // Callback, описанный ниже
}
}
}

Окно должно отображать данные

// класс окна
public partial class MainWindow : Window
{
private void OnMeasuresReceived( Ws ws, PC pc )
{
// выставим control'ы в соответствии с полученными значениями
Dispatcher.Invoke( (Action)( () =>
{
_xTextBox.Text = pc.x.ToString();
_yTextBox.Text = pc.y.ToString();
} ), null );
}
}


Так вот, несмотря на то, что за секунду приходит около 50 паектов данных, отображаются примерно 2-3 (определено на глаз), отображаются несинхронно (сначала меняется значение _xTextBox, потом, через долю секунды _yTextBox), неравномерно (между сменой значений проходят разные интервалы времени: от трети секунды до секунды), входной буфер устройства забивается.

Насколько я понимаю, тормозит вызов Dispatcher.Invoke. Сейчас уже понял, что мне придется делать какое-то сглаживание, даже если удастся добиться, чтобы данные отображались в реальном времени в textBox'ах, но все равно,
подскажите, как лечить.
Comments (10)
ava
erm0l0v | 24.08.2012, 10:03 #
Просто Dispatcher.Invoke так работает. Вы передаете ему Action, но это не значит что он сразу побежит его выполнять. Он поставит ваш Action в очередь и когда не будет других более приоритетных заданий Action будет выполнен. Попробуйте изменить приоритет вашего Dispatcher.
ava
Aldegid | 24.08.2012, 10:49 #
Попробовал повысить приоритет до максимального. Стало немногим быстрее. Но это меня не устраивает. Как-то это для меня очень странно. У меня есть приложение с UI, но этот UI я обновлять не могу в том темпе, который мне нужен... Хоть переписывай под Windows Forms.
ava
erm0l0v | 24.08.2012, 12:05 #
Ну в этом есть определеная логика. Зачем обновлять данные быстрее чем пользователь сможет их прочитать.
На крайний случай вы можете не использовать Dispatcher, а вычислять данные в потоке формы, но скорее всего из-за этих дейсвий интерфейс перестанет реагировать на пользователя.
Еще можно использовать DispatcherTimer, и показывать актуальное значение в момент срабатывания таймера, но в таком случае некоторые значения могут так и не оказаться на форме.
ava
Aldegid | 24.08.2012, 12:35 #
erm0l0v, согласен. Но дело, кажется в другом.

Еще на одном форуме я жаловался, что VS как-то странно работает с этим WPF. Сначала я переименовал созданный control, попробовал написать его идентификатор в тексте - и не IntelliSense, ни компилятор его не нашли. А потом через несколько минут нашли.

Потом я добавил обработчик нажатия кнопки, поставил туда точку останова, запустил, нажал на кнопку - точка останова не сработала.

Насколько я понимаю, в студии с этим WPF намудрен какой-то не для простых смертных механизм синхронизации. И компилятор и IntelliSense узнают об изменениях, внесенных в XAML далеко не сразу >:((((((((

Теперь вот подергал XAML еще немного - TextBox'ы стали обновляться быстро. ###.
ava
erm0l0v | 24.08.2012, 13:15 #
Видимо вы используете при разработке на WPF тот же подход, что и при разработке в WF.

В этом случае конечно будут незаметны все прелести WPF и технологии XAML по сравнению с разработкой через события в WF. Я при разработке в WPF использую шаблон MVVM, и в лучших традициях этого шаблона код в .xaml.cs вообще не трогаю.

У меня с IntelliSense все нормально, правда нужно постоянно делать ребилд чтобы студия узнала о новых изменениях, но это нормально.

В вложении кинул свою реализацию задачи с помощью MVVM.
ava
Aldegid | 27.08.2012, 11:06 #
erm0l0v, наконец-то добрался до Вашего примера. Посмотрел на бегущие счетчики, посмотрел, что совсем нет привычного C# кода, и не понял, как предложенный Вами подход работает, чего он позволяет достичь, и в чем его преимущества.

Поясните или дайте ссылку или запрос для поисковика, пожалуйста.
ava
erm0l0v | 27.08.2012, 15:07 #
Даже не знаю не какой хорошей ссылке где это все было просто и понятно написано, хотя статей очень много по теме, ключевые слова WPF Binding MVVM.

В примере я разделил код на представление(MainWindow.xaml) и модель(MainViewModel.cs).
MainWindow.xaml отвечает только за отображение
в MainViewModel.cs находится вся логика. Традиционно модель наследуется от интерфейса INotifyPropertyChanged, который содержит одно единственно событие, сигнализирующее о изменении свойства модели. У меня модель унаследована от BaseViewModel.cs там реализованы дополнительные синтаксические плюшки, но можно обойтись и без них.

И так в нашей модели есть свойства X Y которые будут отображаться, с вызовом метода OnPropertyChanged в сетере, который в свою очередь вызывает событие интерфейса INotifyPropertyChanged. То есть как только мы меняем например свойство X событие PropertyChanged сигнализируют нашему представлению что нужно перерисовать все элементы в которых задействовано X.

Элементы X Y у меня меняются в потоке thUpdate который запускается при нажатии на кнопку Start.

Далее идем к нашему представлению. Во первых в представлении необходимо указать модель, за это отвечает свойство DataContext. Я унаследовал BaseViewModel от MarkupExtension который возвращает сам себя, благодаря этому я проициализировал DataContext одной строчкой:

<Grid DataContext="{viewmodel:MainViewModel}">

В реальных проектах, конечно присваивать DataContext нужно не напрямую а с использованием некоторой фабрики или механизма внедрения зависимостей.

Можно было и без этого, например создать MainViewModel в ресурсах окна, или в коде в файле MainWindow.xaml.cs, но мне кажется так удобнее.
Далее у нас идет обычная разметка страницы, и там где необходимо мы привязываем свойства нашей модели с помощью Binding. Например код TextBlock который будет отображать значение X выглядит так:

<TextBlock Text="{Binding X}" />

Здесь мы говорим что хотим чтобы в поле Text отображалось значение X, и при этом при изменении значения X Text будет автоматически обновляться.

С кнопкой все аналогично, только мы биндимся на свойство Start типа ICommand.

В принципе все.

По поводу преимуществ, самое главное это то что мы разделяем представление и реализацию, в результате мы можем заменить без проблем и то и другое, использовать UnitTest, да я сам код выглядит чище и понятнее.
Плюс там есть еще очень много плюшек, такие как валидация, DataTemplate и другое, что делает нашу жизнь слаще)))

Если вы решите использовать такой же подход, то после месячной ломки вы в него влюбитесь и будете смотреть на программистов которые разрабатывают UI через событие как на пещерных людей.
ava
Aldegid | 28.08.2012, 14:43 #
Ну да, в свое время я немножко затронул шаблон MVC. Было в нем что-то. Но как-то пока это для меня грузновато. Ни одного класса из перечисленных не знаю. Наверняка Вы написали этот пример раньше и не для меня одного. Ну или большую часть.

Я думал, какой же компонент выбрать для отображения графика данных, постапающих в реальном времени. Теперь еще нужно думать, как его всунуть в этот шаблон. smile smile2
ava
erm0l0v | 28.08.2012, 15:57 #
Про графики можно посмотреть здесь.

Если вы в качестве коллекции данных для графика будите использовать ObservableCollection<T>, то график будет обновляться после каждого изменения коллекции.
ava
Aldegid | 29.08.2012, 12:43 #
При всем моем уважении: 17:40
Цитата
Процессор используется сейчас на 30%. Все из-за того, что у нас такое приложение (три графика на 15 точек каждый, обновляеся раз в полсекунды). Надо опрашивать пореже, наверно. Не раз в полсекунды. Ну это всего лишь пример. Таким образом Вы можете сделать красивое удобное приложение.
Please register or login to write.
Firm of day
Вы также можете добавить свою фирму в каталог IT-фирм, и публиковать статьи, новости, вакансии и другую информацию от имени фирмы.
Подробнее
Contributors
advanced
Submit