реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
hadrov
сообщение Nov 11 2008, 14:18
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 15
Регистрация: 11-11-08
Пользователь №: 41 540



Есть устройство, обменивающееся с компьютером по UART. Протокол вида: компьютер запросил - прибор померил и отдал результат. Для компьютера написана программа на Дельфи. Там в потоке принимается результат, и по факту приема выводится на экран в виде графика с помощью компонента TChart. Программа иногда зависает, иногда бывают аксес виалейшен.

Я хочу переписать программу на С, компилятор возьму MSVS C++.
Как следует организовывать работу с графикой и ком-портом? Мне видится, что нужно по событию от таймера проверять состояние флага приема информации, и тогда принимающий поток будет передавать точки графика тому, кто будет рисовать на форме...
Как я понял, программа часто зависала из-за того, что графика была "тяжелой", и пока что-то рисовалось в поток успевало придти несколько значений и данные терялись... Возможно, там дело обстояло несколько иначе, но мне кажется, что рисовать прямо из потока не очень хорошо.
Интересно было бы увидеть как поступают другие в подобных случаях.
И еще вопрос по графике. Есть ли что-то типа TChart в C++, уж очень там удобное масштабирование.
Читал, что рисование в DC медленное, но может кто-то пробовал его? По сути мне нужно несколько кривых выводить, правда шкала времени (ось Х) довольно длинная, около суток и более, т.е. ничего хитрого. Но с DC придется отказаться от удобностей типа масштабирования.
Go to the top of the page
 
+Quote Post
DpInRock
сообщение Nov 11 2008, 15:14
Сообщение #2


Гуру
******

Группа: Участник
Сообщений: 2 254
Регистрация: 4-05-07
Из: Moscow
Пользователь №: 27 515



В Си ошибок только прибавится. Причем, на порядок.
А всего делов - запихивать данные из УАРТа в кольцевой буфер. А рисующий поток из этого кольцевого буфера изымает по мере возможности. И никто не страдает. В смысле - не висит.


--------------------
On the road again (Canned Heat)
Go to the top of the page
 
+Quote Post
SysRq
сообщение Nov 11 2008, 19:45
Сообщение #3


Чайник, 1 литр
****

Группа: Свой
Сообщений: 655
Регистрация: 17-05-06
Из: Moscow
Пользователь №: 17 168



В Borland C++ Builder есть TChart.

Еще вариант видел: в дополнительном потоке идет работа с com-портом, а приложению постится (WM_USER + x) сообщение с принятыми данными (при этом выделялась память, передавался указатель на буфер с данными; обработчик сообщения данные обрабатывал и память обязательно освобождал).

Стандартный драйвер Windows умеет организовывать софтверные буферы большого объема на отправку\прием. Этого обычно хвататет чтобы ничего не терять.
Go to the top of the page
 
+Quote Post
hadrov
сообщение Nov 12 2008, 08:37
Сообщение #4


Участник
*

Группа: Участник
Сообщений: 15
Регистрация: 11-11-08
Пользователь №: 41 540



Цитата
В Си ошибок только прибавится. Причем, на порядок.

Программу хотят еще и на КПК перенести для работы в полевых условиях. Это одна из причин перехода на Си.

Цитата
А всего делов - запихивать данные из УАРТа в кольцевой буфер. А рисующий поток из этого кольцевого буфера изымает по мере возможности. И никто не страдает.

Надо было мне сразу кусочек кода дать.
Вот как у меня сейчас сделано:

Поток приема из УАРТа.
Код
    data := buf;
    SetEvent(Event_new_data);

В переменной buf (dword) содержится данные из УАРТа, которые мы копируем в промежуточный буфер.


Поток обработки информации.
Код
    WaitForSingleObject(MainForm.Event_new_data, INFINITE);
    ResetEvent(MainForm.Event_new_k);

    Array_data[counter] := MainForm.data;
    inc(counter);
    SetLength(Array_data, counter + 1);

    // далее сама обработка

Ждем новых данных, запихиваем их в массив и выделяем новую ячейку под следующие данные.
Массив не кольцевой по причине того, что для данной версии программы нужно хранить всю накопленные данные в памяти. Но это тестовый вариант, поэтому для конечного варианта программы будет сделано что-то типа буфера FIFO.
Вообще, предположение о потере пакетов это лишь догадка... Просто когда я отключаю рисование графики, то программа работает стабильно.


Цитата
В Borland C++ Builder есть TChart.
Был бы Билдер для КПК - не знал бы я проблем.

Цитата
Еще вариант видел: в дополнительном потоке идет работа с com-портом, а приложению постится (WM_USER + x) сообщение с принятыми данными (при этом выделялась память, передавался указатель на буфер с данными; обработчик сообщения данные обрабатывал и память обязательно освобождал).
Попробую разобраться с сообщениями в Windows.

Цитата
Стандартный драйвер Windows умеет организовывать софтверные буферы большого объема на отправку\прием. Этого обычно хвататет чтобы ничего не терять.
Забыл вчера написать, что чаще всего виснет сама графика, а обработка данных происходит нормально. Рисование происходит простым добавлением новой точки, например, MainForm.Series1.AddY(high_border[i]). И зависнуть это дело может как на 10 точке добавление, так и на 10000.
Go to the top of the page
 
+Quote Post
Demeny
сообщение Nov 12 2008, 10:04
Сообщение #5


Знающий
****

Группа: Свой
Сообщений: 648
Регистрация: 11-02-06
Из: Санкт-Петербург
Пользователь №: 14 237



Цитата(hadrov @ Nov 11 2008, 17:18) *
Как следует организовывать работу с графикой и ком-портом? Мне видится, что нужно по событию от таймера проверять состояние флага приема информации, и тогда принимающий поток будет передавать точки графика тому, кто будет рисовать на форме...

ИМХО, таймер здесь лишний. Также мне не нравится идея вывести рисование в отдельный поток для данной задачи. В потоки удобно и нужно разносить вычисления, работающие по отношению друг к другу асинхронно и независимо, либо обслуживающие внешние воздействия, иначе Вы намучаетесь с их синхронизацией, отлавливать ситуации, когда один поток уже удалил объект, а второй осуществляет к нему доступ (access violation) и т. п.
Здесь ситуация проста - потоку рисования нечего делать, пока нет новых данных с UART, значит они должны работать синхронно и последовательно - "пришли данные --> надо рисовать", тем более, что рисование происходит заведомо быстрее, чем приходят данные с порта. Поэтому не надо усложнять - я не вижу ничего зазорного в том, чтобы рисовать прямо из потока приёма информации. Для этого удобно хранить в классе, который работает с UART, указатель на объект класса, который всё это рисует, чтобы по факту прихода данных вызвать нужный метод рисования.


--------------------
Сделано в Китае. Упаковано в России.
Go to the top of the page
 
+Quote Post
SysRq
сообщение Nov 12 2008, 10:36
Сообщение #6


Чайник, 1 литр
****

Группа: Свой
Сообщений: 655
Регистрация: 17-05-06
Из: Moscow
Пользователь №: 17 168



Может стоит перейти на .NET? Оно относительно одинаково заводится на всех Win* платформах.
Go to the top of the page
 
+Quote Post
hadrov
сообщение Nov 12 2008, 10:46
Сообщение #7


Участник
*

Группа: Участник
Сообщений: 15
Регистрация: 11-11-08
Пользователь №: 41 540



С учетом того, что потребуется версия для КПК, то, как я понимаю, без .NET не обойтись.
Go to the top of the page
 
+Quote Post
XVR
сообщение Nov 12 2008, 11:31
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(hadrov @ Nov 12 2008, 11:37) *
Программу хотят еще и на КПК перенести для работы в полевых условиях. Это одна из причин перехода на Си.


Надо было мне сразу кусочек кода дать.
Вот как у меня сейчас сделано:

Поток приема из УАРТа.
Код
    data := buf;
    SetEvent(Event_new_data);

В переменной buf (dword) содержится данные из УАРТа, которые мы копируем в промежуточный буфер.


Поток обработки информации.
Код
    WaitForSingleObject(MainForm.Event_new_data, INFINITE);
    ResetEvent(MainForm.Event_new_k);

    Array_data[counter] := MainForm.data;
    inc(counter);
    SetLength(Array_data, counter + 1);

    // далее сама обработка

Ждем новых данных, запихиваем их в массив и выделяем новую ячейку под следующие данные.
Неправильно. Во первых необходим хотя бы небольшой буфер под принимаемый байт. Если поток обработки информации не успеет обработать байт перед тем, как будет готов следующий, то байт потеряется

Во вторых, ручной сброс event'а (ResetEvent) может привести к потере одного события - нужно пользовать event с автоматическим сбросом

В третих, если 'поток обработки информации' это действительно отдельный поток, и он напрямую добавляет точки в TChart, то это и является источником зависаний - с объектами VCL можно работать ТОЛЬКО из главного потока приложения

И в последних - занесение и чтение данных из буфера, разделяемого между 2мя потоками, должно быть синхронизированно (например, критической секцией)
Go to the top of the page
 
+Quote Post
hadrov
сообщение Nov 12 2008, 11:55
Сообщение #9


Участник
*

Группа: Участник
Сообщений: 15
Регистрация: 11-11-08
Пользователь №: 41 540



Цитата
необходим хотя бы небольшой буфер под принимаемый байт. Если поток обработки информации не успеет обработать байт перед тем, как будет готов следующий, то байт потеряется
Вы имеете ввиду сделать буфер типа FIFO?


Цитата
ручной сброс event'а (ResetEvent) может привести к потере одного события - нужно пользовать event с автоматическим сбросом
Это Вы за PulseEvent()? Делал и с ее помощью, но почему то автоматический сброс не происходил, вот я и сделал ручной. Попробую еще поковырять...


Цитата
если 'поток обработки информации' это действительно отдельный поток
Угу, данные обрабатываются в отдельном потоке.


Цитата
он напрямую добавляет точки в TChart, то это и является источником зависаний - с объектами VCL можно работать ТОЛЬКО из главного потока приложения
Вот где бы почитать о таких фичах, а то подобные вещи мелькают либо в FAQ'ах или приходят интуитивно, что так делать не стоит.
Тогда вопрос, каким образом TChart'у узнать, что я хочу на нем что-то нарисовать. Правильно ли будет ему ждать события от потока обработки данных WaitForSingleObject()?


Цитата
занесение и чтение данных из буфера, разделяемого между 2мя потоками, должно быть синхронизированно (например, критической секцией)
Дык вроде и синхронизировался с помощью event'ов.
Попробую с помощью CriticalSection.
Go to the top of the page
 
+Quote Post
Kopa
сообщение Nov 12 2008, 12:13
Сообщение #10


Знающий
****

Группа: Участник
Сообщений: 598
Регистрация: 22-08-05
Пользователь №: 7 861



Цитата(hadrov @ Nov 12 2008, 13:46) *
С учетом того, что потребуется версия для КПК, то, как я понимаю, без .NET не обойтись.


А почему не Java, или Forth ( Форт ), Tcl/Tk ... ?

Сообщение отредактировал Kopa - Nov 12 2008, 12:14
Go to the top of the page
 
+Quote Post
hadrov
сообщение Nov 12 2008, 13:30
Сообщение #11


Участник
*

Группа: Участник
Сообщений: 15
Регистрация: 11-11-08
Пользователь №: 41 540



С Си знаком лучше всего.
И я не знаю как быстро работают интерпретаторы Tcl/Tk или VM Java на КПК... имхо, лучше уж Python прикрутить.
Форт я весьма давно ковырял.
Для начала хотя бы на С# что-то написать.

Цитата
Был бы Билдер для КПК - не знал бы я проблем.

Хм, плохо я искал.
Нашел, что Borland Development Studio может под КПК делать программы.
Скачаю BDS2009 и попробую ее в первую очередь.
Go to the top of the page
 
+Quote Post
OlegH
сообщение Nov 12 2008, 18:44
Сообщение #12


Частый гость
**

Группа: Свой
Сообщений: 186
Регистрация: 14-01-06
Из: Украина, г.Харьков
Пользователь №: 13 168



Цитата(hadrov @ Nov 11 2008, 16:18) *
Есть устройство, обменивающееся с компьютером по UART. Протокол вида: компьютер запросил - прибор померил и отдал результат. Для компьютера написана программа на Дельфи. Там в потоке принимается результат, и по факту приема выводится на экран в виде графика с помощью компонента TChart. Программа иногда зависает, иногда бывают аксес виалейшен.
....
Как я понял, программа часто зависала из-за того, что графика была "тяжелой", и пока что-то рисовалось в поток успевало придти несколько значений и данные терялись... Возможно, там дело обстояло несколько иначе, но мне кажется, что рисовать прямо из потока не очень хорошо.
Интересно было бы увидеть как поступают другие в подобных случаях.
....


То ли я чего-то не понял.... То ли объяснения туманные.... Но это же весьма известный факт - из дополнительных потоков процесса ЗАПРЕЩЕНО непосредственно обращаться к экранным отображаемым объектам (компонентам-наследникам TComponent). Любые обращения к таким объектам обязаны происходить в констексте основного потока приложения или хотя бы синхронизировано с ним (большинство методов почти всех компонентов, кроме специально предназначенных, не являются thread-safe. Конфликт возникает при доступе/изменении одних и тех же данных из разных потоков).
Для более-менее удобного избегания конфликтов (если все-таки нужно из потока обращаться к компонентам) придуман метод Synchronize......
Go to the top of the page
 
+Quote Post
XVR
сообщение Nov 13 2008, 07:47
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847



Цитата(hadrov @ Nov 12 2008, 14:55) *
Вы имеете ввиду сделать буфер типа FIFO?
Да


Цитата
Это Вы за PulseEvent()? Делал и с ее помощью, но почему то автоматический сброс не происходил, вот я и сделал ручной. Попробую еще поковырять...
Нет, это я за 2й параметр в функции CreateEvent (ManualReset), он должен быть FALSE

Цитата
Тогда вопрос, каким образом TChart'у узнать, что я хочу на нем что-то нарисовать. Правильно ли будет ему ждать события от потока обработки данных WaitForSingleObject()?
Воспользоваться synchronized процедурой

Цитата
Дык вроде и синхронизировался с помощью event'ов.
Попробую с помощью CriticalSection.
Нужно и то и другое - с помощью event'ов сообщать что появились новые данные, с помощью CriticalSection защищать доступ к этим данным (FIFO) из 2х разных потоков
Go to the top of the page
 
+Quote Post
CSB
сообщение Nov 17 2008, 23:00
Сообщение #14


Частый гость
**

Группа: Новичок
Сообщений: 100
Регистрация: 9-03-06
Пользователь №: 15 088



...

Сообщение отредактировал CSB - Nov 17 2008, 23:08
Go to the top of the page
 
+Quote Post
hadrov
сообщение Nov 17 2008, 23:14
Сообщение #15


Участник
*

Группа: Участник
Сообщений: 15
Регистрация: 11-11-08
Пользователь №: 41 540



Цитата
Воспользоваться synchronized процедурой

Ага, теперь понял откуда ноги ростут. Эта процедура из TThread. При создании модуля с классом TThread и написано, что нельзя рисовать из потока, кроме как используя метод synchronize. Позже попробую написать его потомок, а пока я потоки реализованы с помощью API. В этом случае есть возможность рисовать из потока в главном окне?

Цитата
Нужно и то и другое - с помощью event'ов сообщать что появились новые данные, с помощью CriticalSection защищать доступ к этим данным (FIFO) из 2х разных потоков
Что бы не действовать методом научного тыка приведу краткий алгоритм, а то не уверен, что правильно Вас понял.

Поток приема из УАРТа.
Код
1. Ждем стартовый байт. (WaitCommEvent(), WaitForSingleObject())
2. Входим в критическую секцию. (EnterCriticalSection())
3. Записываем данные в буфер. (ReadFile())
4. Выходим из критической секции. (LeaveCriticalSection())
5. Устанавливаем событие в сигнальное состояние (событие с автосбросом) (SetEvent())
6. Переходим к п. 1.


Поток обработки данных
Код
1. Ждем наступления события. (WaitForSingleObject())
2. Входим в критическую секцию.
3. Извлекаем данные из буфера.
5. Выходим из критической секции.
6. Переходим к п. 1.


И как после извлечении данных из буфера и выхода из критической секции вставить пункт "Рисуем в главном окне", чтобы он отработал нормально?
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 21st June 2025 - 09:56
Рейтинг@Mail.ru


Страница сгенерированна за 0.01489 секунд с 7
ELECTRONIX ©2004-2016