|
|
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)){
вылетает ошибка деления на ноль.
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком приема данных(builder c+)
Про деление на ноль не знаю, а зачем ! при проверке результатов функций? Они BOOL, при успехе TRUE.
|
|
|
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 всегда. от чего так может быть?
|
|
|
Re: E14-140 работа с потоком приема данных(builder c+)
и ADC_Buf всегда пустой нулевой.
|
|
|
Re: E14-140 работа с потоком приема данных(builder c+)
|
|
- Сотрудник "Л Кард"
- Здесь с 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 ???
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)) ...
|
|
|
Re: E14-140 работа с потоком приема данных(builder c+)
Спасибо за замечания. Сделал как вы сказали. Но
if (!m->START_ADC())
не срабатывает, то есть переходит на return всегда и данные не читаются. Не могли бы вы посмотреть пожалуйста?
http://narod.ru/disk/18804972001/THREAD.zip.html
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком приема данных(builder c+)
1. CreateEvent() стоит вынести за условие (туда же, где создается объект ILE140), потому что так, как написано сейчас, CreateEvent() может не выполниться, а из FormClose() выполнится CloseHandle()
2. CloseHandle(hStopEvent);
SetEvent(hStopEvent);
Что это?
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(), я не возражаю, но тогда надо править код в нескольких местах -- пока лучше не гнаться за двумя зайцами.
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком приема данных(builder c+)
Вот вариант, только без борланда, я переделал в консольное приложение -
http://www.sendspace.com/file/ut3vwv
|
|
|
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
в чем касяк может быть с данными?
|
|
- Сотрудник "Л Кард"
- Здесь с 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яйте указатель, чтобы было видно, что он невалидный)
Касаемо графика - я не понял, сколько тут точек.
В цикле
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком приема данных(builder c+)
...в цикле надо поставить полный размер массива (см. выше), тогда данные будут без пропусков
|
|
|
Re: E14-140 работа с потоком приема данных(builder c+)
александр спасибо большое что помогаете
|
|
|
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();
Оно вообще зачем нужно? Это же просто остановка потока вроде мягкая.
|
|
|
Re: E14-140 работа с потоком приема данных(builder c+)
вот сделал чето http://narod.ru/disk/18918275001/THREAD.zip.html
короче новое исключение появилось, поставил его в try catch. Но один фиг.
Вообщем я чето перепутал наверно. В Событии закрытия формы чето
|
|
- Сотрудник "Л Кард"
- Здесь с 18.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком приема данных(builder c+)
Ну как можно сделать SetEvent() и сразу CloseHandle()? Кто будет ловить событие??
WaitFor() - это не остановка, а ожидание завершения работы потока, т.е. выхода из функции Execute().
Terminate() - это просто установка свойства Terminated. Его надо проверять в потоке руками, а мы уже говорили, что циклический опрос - это плохо, события лучше.
Кстати, нельзя путать TThread::Terminate() с WINAPI TerminateThread(). Последнее - это как раз принудительное убийство потока. Оно очень плохо тем, что происходит в произвольной точке выполнения потока, и тот не успеет привести свои дела в порядок, раздать долги, написать завещание и т.д.
В примере, на котором Вы основываетесь, завершение потока было реализовано мягко и без опроса типа 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.
А график-то, график правильный - получился?
|
|
|
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.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
Получилось, что после завершения 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.04.2014
- Сообщений: 810
|
Re: E14-140 работа с потоком приема данных(builder c+)
А насчет 7 вольт - это не вольты, а коды АЦП, и на графике амплитуда где-то около 7700 единиц. Коды АЦП принимают значения -8192..8191, причем номинальному диапазону измерений (10В, 2.5В и т.д.) соответствует 8000 (остальное - запас).
Так что это примерно 7700/8000 от шкалы, т.е. где-то около 9.6 В
Все это подробно описано в руководстве.
|
|
|
Re: E14-140 работа с потоком приема данных(builder c+)
Спасибо большое. Если что вернусь в эту тему ещё.
|