Российский производитель и разработчик сертифицированного измерительного оборудования с 1987 года


E14-140 работа с потоком приема данных(builder c+)

Вы не вошли.

 Поиск | Регистрация | Вход 

УЩЕ
11.07.2011 12:53:55
#1

Гость

E14-140 работа с потоком приема данных(builder c+)

Здравствуйте! Сделал пример принятия данных и вывод их на график на билдере2010 для E14-140.

http://narod.ru/disk/18712726001/THREAD.zip.html

Можете проверить в чем касяк? Просто ничего не происходит , более того ни в тему на
if(!m->GET_MODULE_DESCRIPTION(&descr) && !m->GET_ADC_PARS(&adc_pars)){
вылетает ошибка деления на ноль.

11.07.2011 13:38:48
#2

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком приема данных(builder c+)

Про деление на ноль не знаю, а зачем ! при проверке результатов функций? Они BOOL, при успехе TRUE.

УЩЕ
11.07.2011 13:57:25
#3

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

деление на ноль было из -за того что dll фаил не был в папке дебаг. С этим я разобрался.

вот функция приема данных и вывод их на экран
void __fastcall TGetDataThread::Execute()
{
FreeOnTerminate=true;

while (1)  {
int idx;
HANDLE hEvent[2];
OVERLAPPED ov[2];
DWORD TransferSize;
HANDLE WaitList[2];

hEvent[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
hEvent[1] = CreateEvent(NULL, TRUE, FALSE, NULL);

/* Ставим в очередь первый запрос чтения данных */
ZeroMemory(&ov[0], sizeof(OVERLAPPED));
ov[0].hEvent = hEvent[0];
if (!ReadFile(hDevice, ADC_Buf[0], sizeof(ADC_Buf[0]), NULL, &ov[0])
&& (GetLastError() != ERROR_IO_PENDING))
{
/* Ошибка */
return ;
}
idx = 1;

for (;;)
{
/* Ставим в очередь следующий буфер */
ZeroMemory(&ov[idx], sizeof(OVERLAPPED));
ov[idx].hEvent = hEvent[idx];
if (!ReadFile(hDevice, ADC_Buf[idx], sizeof(ADC_Buf[idx]), NULL, &ov[idx])
&& (GetLastError() != ERROR_IO_PENDING))
{
/* Ошибка */
break;
}

idx ^= 1;
/* Ждем окончания предыдущего чтения или прихода команды stop */
WaitList[0] = hStopEvent;
WaitList[1] = hEvent[idx];
if (WaitForMultipleObjects(2, WaitList, FALSE, INFINITE) != WAIT_OBJECT_0 + 1)
{
/* Пришла команда stop */
break;
}

if (!GetOverlappedResult(hDevice, &ov[idx], &TransferSize, TRUE))
{
/* Ошибка */
break;
}
Synchronize(pb);

} /* for (;;) */

CancelIo(hDevice);
GetOverlappedResult(hDevice, &ov[idx], &TransferSize, TRUE);
GetOverlappedResult(hDevice, &ov[idx ^ 1], &TransferSize, TRUE);
CloseHandle(hEvent[1]);
CloseHandle(hEvent[0]);

if(Terminated)break;

}
    //---- Place thread code here ----
}

в ней переходит на break после ReadFile всегда. от чего так может быть?

УЩЕ
11.07.2011 13:58:10
#4

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

и ADC_Buf всегда пустой нулевой.

УЩЕ
11.07.2011 14:46:41
#5

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

http://narod.ru/disk/18724829001/THREAD.rar.html

вот переделал немного, посмотрите пожалуйста.

11.07.2011 15:06:04
#6

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком приема данных(builder c+)

В основном замечания те же, что в старой ветке
http://www.lcard.ru/forumthreads/9977

1. В условии !m->GET_MODULE_DESCRIPTION... убрать логическую инверсию - значение при успехе TRUE
2. Опять забыли создать hStopEvent
Перед запуском потока
    hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
После останова потока
    CloseHandle(hStopEvent);
3. Переменные runflag, breakflag можно выкинуть (или использовать для внутренних нужд, если надо)
4. Нормальное завершение потока не надо делать через TerminateThread.  Например, FormClose может быть таким:
   SetEvent(hStopEvent);
      WaitForSingleObject(hThread, INFINITE);
      m->ReleaseLInstance();
5. Где запуск START_ADC ??? smile
6. В потоке не нужен цикл while(1)
7.    Form1->Chart1->Series[0]->AddY((double &)ADC_Buf[i]);
Тут две ошибки: а) массив двумерный, первый индекс 0 или 1
б) преобразование значения short в double надо делать без ссылки
Например:   Form1->Chart1->Series[0]->AddY((double)ADC_Buf[0][i]);
8. Кто присвоит значение hDevice?
hDevice = m->GetModuleHandle();
if ((INVALID_HANDLE_VALUE != hDevice) && m->GET_MODULE_DESCRIPTION(&descr) && m->GET_ADC_PARS(&adc_pars)) ...

УЩЕ
12.07.2011 08:56:14
#7

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

Спасибо за замечания. Сделал как вы сказали. Но
if (!m->START_ADC())
не срабатывает, то есть переходит на return всегда и данные не читаются. Не могли бы вы посмотреть пожалуйста?
http://narod.ru/disk/18804972001/THREAD.zip.html

12.07.2011 11:36:36
#8

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком приема данных(builder c+)

1. CreateEvent() стоит вынести за условие (туда же, где создается объект ILE140), потому что так, как написано сейчас, CreateEvent() может не выполниться, а из FormClose() выполнится CloseHandle()

2. CloseHandle(hStopEvent);
   SetEvent(hStopEvent);
Что это? smile

3. Не присваивается значение переменной thread. Для борланда (класс TThread) вместо WaitForSingleObject() можно написать thr->WaitFor();
Можно написать код останова, например, так:
   SetEvent(hStopEvent);
   thr->WaitFor();
   m->ReleaseLInstance();
   CloseHandle(hStopEvent);

4. Зачем убили создание hEvent[], которые потом используются ?

5. Добавленный код (thread.cpp строки 73-83) непонятен и не будет работать. Это наброски какого-то нового варианта? Тогда пока лучше это убрать в комментарий или в #if 0 .. #endif
Кстати, задумка функции WaitingForIoRequestCompleted(), если я правильно ее прочитал, идеологически неправильна в корне: в многозадачной системе не делают ожидание фонового процесса в стиле while (!гром_прогремел()) {}. Это будет выглядеть как зависшее приложение, пожирающее 100% CPU, и называется жужжанием в коротком цикле (buzzing).
События (events) придуманы специально для того, чтобы ждать их корректно - через функции WaitForSingleObject(), WaitForMultipleObjects(), GetOverlappedResult() с параметром bWait = TRUE и т.д. или соответствующие методы объектных языков, инкапсулирующие эти вызовы (типа TThread::WaitFor())
Могут быть и другие средства. Ключевым здесь является то, что приложение, вызывая функцию ожидания, дает планировщику ОС возможность понять, что она бездействует, и не отдавать ей процессорное время, пока не наступит событие или таймаут. Напротив, короткий цикл с точки зрения ОС
- это не бездействие, а МАКСИМАЛЬНАЯ нагрузка (программе есть что делать, она ничего не ждет; наверное, занята сложными математическими вычислениями - думает планировщик)

6. START_ADC() и STOP_ADC() можно поставить либо в поток - перед и после цикла for(;;), либо в FormCreate() и FormClose() соответственно перед или после thr->Resume() и после thr->WaitFor().

7. Замечание по моему примеру, на котором Вы основываетесь: я в отступление от документации lusbapi написал чтение данных не через IO_REQUEST_LUSBAPI, а напрямую через winapi ReadFile() и т.д. В принципе это то же самое (см. исходники lusbapi), просто мне не нравится реализация overlapped I/O в ReadData() в некоторых мелочах, да и для понимания логики программы так прозрачнее - все можно посмотреть в msdn.
Можете перейти на ReadData(), я не возражаю, но тогда надо править код в нескольких местах -- пока лучше не гнаться за двумя зайцами.

12.07.2011 11:57:52
#9

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком приема данных(builder c+)

Вот вариант, только без борланда, я переделал в консольное приложение -
http://www.sendspace.com/file/ut3vwv

УЩЕ
12.07.2011 13:14:54
#10

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

ага спасибо посмотрю. http://narod.ru/disk/18828733001/THREAD.zip.html

вот опять исправил вроде бы последние касяки , вроде сигнал даже появился.
после выхода из программы только:
http://s2.ipicture.ru/uploads/20110712/0i2b1VR1.png
а сама синусоида выглядит так, но это же неверно
http://s2.ipicture.ru/uploads/20110712/gm2Sez5W.png
в чем касяк может быть с данными?

12.07.2011 15:00:35
#11

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком приема данных(builder c+)

Отключили ControlTable[1] и не уменьшили ChannelsQuantity

Exception ловите под отладчиком - но я думаю, что это из-за того, что for (int i = 0; i < 1000; i++)
при размерности массива 512. Не забывайте про размерности! А лучше написать
for (int i = 0; i < BLOCK_SIZE; i++)
или даже
for (int i = 0; i < sizeof(ADC_Buf[0]) / sizeof(ADC_Buf[0][0]); i++)

Или что-то с потоком; может быть, не там создаете thr и не удаляете его после завершения? Не знаю, я не спец по билдеру.
(попробуйте убрать Resume и создавать thr прямо там, где было Resume, без флага suspended; после WaitFor() поставьте delete thr; thr = NULL;
И тогда сама переменная (объявить с инициализацией TGetDataThread *thr=NULL) будет флагом того, что поток запущен. В FormClose() можно проверить if (thr && m), а иначе ничего не делать
(Вообще совет на будущее: когда функция работает с указателями, всегда проверяйте их на NULL. И после удаления динамических объектов обNULLяйте указатель, чтобы было видно, что он невалидный)

Касаемо графика - я не понял, сколько тут точек.
В цикле

12.07.2011 15:01:36
#12

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком приема данных(builder c+)

...в цикле надо поставить полный размер массива (см. выше), тогда данные будут без пропусков

УЩЕ
13.07.2011 08:46:12
#13

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

александр спасибо большое что помогаете

УЩЕ
13.07.2011 10:25:54
#14

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

Сделал так :


void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
   //завершение работы программы
SetEvent(hStopEvent);
   //WaitForSingleObject(thread , INFINITE);
//thr->WaitFor();
m->ReleaseLInstance();
CloseHandle(hStopEvent);

}
//-- -------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
thr->Suspend();
thr->Terminate();
}

исключение пропало после закрытия формы.

Может проблема была в thr->WaitFor();
Оно вообще зачем нужно? Это же просто остановка потока вроде мягкая.

УЩЕ
13.07.2011 10:52:52
#15

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

вот сделал чето http://narod.ru/disk/18918275001/THREAD.zip.html

короче новое исключение появилось, поставил его в try catch. Но один фиг.
Вообщем я чето перепутал наверно. В Событии закрытия формы чето

14.07.2011 13:30:51
#16

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком приема данных(builder c+)

Ну как можно сделать SetEvent() и сразу CloseHandle()? Кто будет ловить событие??

WaitFor() - это не остановка, а ожидание завершения работы потока, т.е. выхода из функции Execute().

Terminate() - это просто установка свойства Terminated. Его надо проверять в потоке руками, а мы уже говорили, что циклический опрос - это плохо, события лучше.
Кстати, нельзя путать TThread::Terminate() с WINAPI TerminateThread(). Последнее - это как раз принудительное убийство потока. Оно очень плохо тем, что происходит в произвольной точке выполнения потока, и тот не успеет привести свои дела в порядок, раздать долги, написать завещание и т.д. smile

В примере, на котором Вы основываетесь, завершение потока было реализовано мягко и без опроса типа while (!Terminated), а исключительно на событии hStopEvent - вот для чего оно нужно.
Если Вы заметили, когда поток ждет завершения асинхронного чтения данных (ReadFile), то он ждет сразу на двух событиях
WaitList[0] = hStopEvent;
WaitList[1] = hEvent[idx];
По любому первому из них WaitForMultipleObjects() возвращает управление, по вернувшемуся значению определяется, какое событие сработало:
if (WaitForMultipleObjects(2, WaitList, FALSE, INFINITE) != WAIT_OBJECT_0 + 1)
{
/* Пришла команда stop */
break;
}
А этот break выходит из тела цикла, после чего все корректно останваливается и происходит выход из Execute.
Чего и ждет WaitFor.
Только после этого можно CloseHandle(hStopEvent), а никак не выдергивать хендл из-под потока, который большую часть времени сидит в WaitForMultipleObjects и как раз использует этот хендл.

Все должно работать. И не надо никакого Suspend.

А график-то, график правильный - получился?

УЩЕ
18.07.2011 09:54:06
#17

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

Вообщем я не знаю.
Просто скажите
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
   //завершение работы программы
SetEvent(hStopEvent);
   //WaitForSingleObject(thread , INFINITE);
thr->WaitFor();
m->ReleaseLInstance();
CloseHandle(hStopEvent);

}

правильно же?

Останавливаю поток
//-- -------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
thr->Suspend();
}

Пытаюсь возобновить так:

void __fastcall TForm1::Button2Click(TObject *Sender)
{
thr->Resume();
}

Правильно?

вот у меня такой касяк вылетал
http://www.cryer.co.uk/brian/delphi/error_te_thii.htm
исключение.
Потом прочитал тут http://www.sql.ru/Forum/actualthread.as … 552841&hl=

Вроде понял то что когда я закрывал форму поток память чистил благодаря FreeOnTerminate=true ;

ЦИТАТА:"
Чтобы занятая потоком память освобождалась при завершении потока используйте в Execute() FreeOnTerminate = true;
Однако, возможны ситуации, когда завершение потока должно быть скоординировано с другим потоком. Например, Вы должны ждать возвращения значения из одного потока, чтобы возвратить это значение в другой поток. Чтобы сделать это, Вы не освобождаете первый поток, пока второй поток не получит возвращаемое значение. Вы можете обработать эту ситуацию, установив FreeOnTerminate=false и затем явно освободив первый поток из второго."
http://rxlib.ru/WinLesson/bles2_1.htm

Сделал тупо FreeOnTerminate=false; и нормально стало закрываться


График выглядит так: http://s2.ipicture.ru/uploads/20110718/tfh2GnI7.png
в принципе нормально, но я не вводил коэффициентов лкарда А,В,С, поэтому 7 вольт кажет, а так в реале 10 должно быть.

18.07.2011 11:30:17
#18

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком приема данных(builder c+)

>Просто скажите
>SetEvent(hStopEvent);
>thr->WaitFor();
>m->ReleaseLInstance();
>CloseHandle(hStopEvent);
правильно же?
Правильно - при условии, что поток отслеживает заранее созданное и изначально неактивное событие hStopEvent и при обнаружении, что событие выставлено, корректно останавливает свою работу, освобождает все занятые ресурсы и завершается.

Suspend() и Resume() (насколько я понял, просто инкапсуляция WINAPI SuspendThread()/ResumeThread()) - это малополезная штука, потому что поток приостанавливается на произвольной машинной инструкции. Абсолютно нельзя гарантировать, что именно в этот момент нет обращения к какому-то общему ресурсу, т.е. функция SuspendThread не годится как средство синхронизации процессов.
В частности, приостановленный поток нельзя корректно завершить (будучи приостановленным, он не сможет достигнуть конца функции Execute())

>Сделал тупо FreeOnTerminate=false; и нормально стало закрываться
Да, я не обратил внимания на добавленную Вами строчку, это борландовская надстройка и по умолчанию false.
Просто не следовало нарочно ставить ее в true smile
Получилось, что после завершения Execute() автоматически делалось delete thr, и тот же thr->WaitFor() мог оказаться обращением к несуществующему объекту.

По-хорошему, как я говорил выше, на каждый new должен быть свой delete, т.е. поток лучше создавать динамически и освобождать после завершения.
Например, в инициализации

TGetDataThread *thr = NULL;

Вместо Resume создаем поток сразу в запущенном состоянии:
(также я здесь намечаю, что надо проверять все возможные ошибки и освобождать уже созданные объекты при аварийном выходе)
if (thr) { ошибка_поток_уже_запущен; return; }
hStopEvent = CreateEvent(...);
if (!hStopEvent) { ошибка; return; }
m = static_cast...;
if (!m) { ошибка; CloseHandle(hStopEvent); return; }
открыть устройство
if (не_открылось)
    { ошибка; m->ReleaseLInstance(); CloseHandle(hStopEvent); return; }
настроить АЦП
if (что-то не получилось)
    { ошибка; m->ReleaseLInstance(); CloseHandle(hStopEvent); return; }

thr = new TGetDataThread(true);

И код останова
if (!thr) { ошибка_поток_не_запущен; return; }
SetEvent(hStopEvent);
thr->WaitFor();
delete thr;
thr = NULL;
m->ReleaseLInstance();
CloseHandle(hStopEvent);

В таком примере переменная thr по совместительству служит флагом, что все ресурсы созданы и поток запущен, т.е. когда thr == NULL, гарантируется, что m и hStopEvent не существуют, а когда thr != NULL, они точно существуют.

Совсем по-хорошему можно было бы проверять каждый объект перед обращением к нему или обернуть их в классы с деструкторами, но это уже другая история.

18.07.2011 11:43:13
#19

Сотрудник "Л Кард"
Здесь с 18.04.2014
Сообщений: 810

Re: E14-140 работа с потоком приема данных(builder c+)

А насчет 7 вольт  - это не вольты, а коды АЦП, и на графике амплитуда где-то около 7700 единиц. Коды АЦП принимают значения -8192..8191, причем номинальному диапазону измерений (10В, 2.5В и т.д.) соответствует 8000 (остальное - запас).
Так что это примерно 7700/8000 от шкалы, т.е. где-то около 9.6 В
Все это подробно описано в руководстве.

УЩЕ
20.07.2011 08:20:50
#20

Гость

Re: E14-140 работа с потоком приема данных(builder c+)

Спасибо большое. Если что вернусь в эту тему ещё.