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

 
 
> Чтение данных из ком-порта в WINXP, избитая тема, но решения для Builder 6 нигде не нашел
skopus
сообщение May 4 2006, 11:27
Сообщение #1


Участник
*

Группа: Свой
Сообщений: 65
Регистрация: 31-08-05
Из: Moscow
Пользователь №: 8 124



Задача стоит такая:
в устройство отправляется последовательность байт - команда на исполнение. После этого компьютер ждет кода ответа от устройства. Необходимо сделалать так, чтобы пока он ждет, можно было отправить код экстренной остановки, нажав на программную кнопку и, соответственно перестать ждать ответа.

Application->ProcessMessages(); не помогает :(
как же это делается?

нашел несколько статей по поводу того как это делается через потоки на Visual C++ 6.0
но на практике код не переносится, да и в многопоточном программировании у меня никакого опыта нет.
Всякие хитрые компоненты применять запрещено.

Помогите пожалуйста! Скоро уже защита :((

На микроконтроллере я бы сделал отправку байта по внешнему прерыванию от кнопки. Но на программном уровне в виндоус так похоже нельзя, да?

Сообщение отредактировал skopus - May 4 2006, 11:31
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов (1 - 13)
_artem_
сообщение May 4 2006, 12:23
Сообщение #2


учащийся
*****

Группа: Свой
Сообщений: 1 065
Регистрация: 29-10-05
Из: города контрастов
Пользователь №: 10 249



rezim overlapped IO ne podxodit ?


--------------------
Зачем лаять на караван , когда на него можно плюнуть?

Go to the top of the page
 
+Quote Post
Aleks17
сообщение May 4 2006, 12:24
Сообщение #3


Местный
***

Группа: Свой
Сообщений: 238
Регистрация: 17-01-05
Из: Новосибирск
Пользователь №: 2 003



Я правда в Билдере нифига не понимаю - на Visual C++ пишу, но думаю принципы те же:

Вариант 1) Многопоточное приложение.

Вариант 2) Открываешь порт для асинхронных операций (FILE_FLAG_OVERLAPPED по-моему)

и организуешь процедуру таймера, внутри которой (а не в процедуре) проверяешь на получение данных.
Go to the top of the page
 
+Quote Post
skopus
сообщение May 4 2006, 12:33
Сообщение #4


Участник
*

Группа: Свой
Сообщений: 65
Регистрация: 31-08-05
Из: Moscow
Пользователь №: 8 124



Цитата(Aleks17 @ May 4 2006, 16:24) *
Я правда в Билдере нифига не понимаю - на Visual C++ пишу, но думаю принципы те же:

Вариант 1) Многопоточное приложение.

Вариант 2) Открываешь порт для асинхронных операций (FILE_FLAG_OVERLAPPED по-моему)

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


а как тогда организовать многопоточное приложение? сам алгоритм?
1. Отправил строку в порт
2. Создал поток для чтения
а в первом потоке будет событие OnClick отрабатываться?

Цитата(_artem_ @ May 4 2006, 16:23) *
rezim overlapped IO ne podxodit ?


возможно и подоходит. Но в Msdn как обычно примеры для Visual C++
пробовал как-то на билдер перенести.. нудачно. Ща посмотрю еще раз
Go to the top of the page
 
+Quote Post
_artem_
сообщение May 4 2006, 13:22
Сообщение #5


учащийся
*****

Группа: Свой
Сообщений: 1 065
Регистрация: 29-10-05
Из: города контрастов
Пользователь №: 10 249



Eto standartniy API - builder ili visualC ne dolzen imet znacheniya .


--------------------
Зачем лаять на караван , когда на него можно плюнуть?

Go to the top of the page
 
+Quote Post
one_man_show
сообщение May 4 2006, 14:02
Сообщение #6


Помогу, чем смогу
******

Группа: Админы
Сообщений: 2 786
Регистрация: 28-05-04
Из: Москва
Пользователь №: 25



Уже писалось не раз: ищешь на torry.net TComPort (автор Dejan Crnila), у него и под Дельфи и под Билдер. Если не хочешь использовать компонент, то просто смотришь исходник, это один из "очень правильно написанных" компонентов для СОМ-порта


--------------------
С уважением,
Ваган Саруханов
Проекты|Форум|Facebook|Linkedin
Go to the top of the page
 
+Quote Post
skopus
сообщение May 4 2006, 14:07
Сообщение #7


Участник
*

Группа: Свой
Сообщений: 65
Регистрация: 31-08-05
Из: Moscow
Пользователь №: 8 124



Цитата(Aleks17 @ May 4 2006, 16:24) *
Я правда в Билдере нифига не понимаю - на Visual C++ пишу, но думаю принципы те же:

Вариант 1) Многопоточное приложение.

Вариант 2) Открываешь порт для асинхронных операций (FILE_FLAG_OVERLAPPED по-моему)

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

вот посмотрите

Код
MComPort.Write(&lpBuf,1); // отправили байт в устройство
Timer1->Enabled=true; //включили таймер

           memset(&MComPort.Overlap,0,sizeof(MComPort.Overlap));                   //это для overlap
           MComPort.Overlap.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);  //MComPort.Overlap - сама структура оверлап
          
           MComPort.Read(SystemAnswer,1); //читаем байт в переменную SystemAnswer

//обработчик события таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
        if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0)
             {
              GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE);
             }
        else {
              Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK);
             }
}


мне кажется, что я сделал что-то не так...
Go to the top of the page
 
+Quote Post
skopus
сообщение May 4 2006, 14:28
Сообщение #8


Участник
*

Группа: Свой
Сообщений: 65
Регистрация: 31-08-05
Из: Moscow
Пользователь №: 8 124



Цитата(one_man_show @ May 4 2006, 18:02) *
Уже писалось не раз: ищешь на torry.net TComPort (автор Dejan Crnila), у него и под Дельфи и под Билдер. Если не хочешь использовать компонент, то просто смотришь исходник, это один из "очень правильно написанных" компонентов для СОМ-порта


только исходники на паскале.. а я не знаком с ним абсолютно
Go to the top of the page
 
+Quote Post
Aleks17
сообщение May 5 2006, 05:25
Сообщение #9


Местный
***

Группа: Свой
Сообщений: 238
Регистрация: 17-01-05
Из: Новосибирск
Пользователь №: 2 003



Цитата(skopus @ May 4 2006, 21:07) *
вот посмотрите

Код
MComPort.Write(&lpBuf,1); // отправили байт в устройство
Timer1->Enabled=true; //включили таймер

           memset(&MComPort.Overlap,0,sizeof(MComPort.Overlap));                   //это для overlap
           MComPort.Overlap.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);  //MComPort.Overlap - сама структура оверлап
          
           MComPort.Read(SystemAnswer,1); //читаем байт в переменную SystemAnswer

//обработчик события таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
        if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0)
             {
              GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE);
             }
        else {
              Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK);
             }
}


мне кажется, что я сделал что-то не так...


Ну в целом похоже. Только место для структуры помнится надо задавать до открытия порта (не помню точно). И Event смотреть не обязательно (да и создавать тоже) если GetOverlappedResult пользуешь, функция же возвращает ссостояние.
Go to the top of the page
 
+Quote Post
skopus
сообщение May 5 2006, 06:39
Сообщение #10


Участник
*

Группа: Свой
Сообщений: 65
Регистрация: 31-08-05
Из: Moscow
Пользователь №: 8 124



Цитата(Aleks17 @ May 5 2006, 09:25) *
Цитата(skopus @ May 4 2006, 21:07) *
вот посмотрите

Код
MComPort.Write(&lpBuf,1); // отправили байт в устройство
Timer1->Enabled=true; //включили таймер

           memset(&MComPort.Overlap,0,sizeof(MComPort.Overlap));                   //это для overlap
           MComPort.Overlap.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);  //MComPort.Overlap - сама структура оверлап
          
           MComPort.Read(SystemAnswer,1); //читаем байт в переменную SystemAnswer

//обработчик события таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
        if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0)
             {
              GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE);
             }
        else {
              Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK);
             }
}


мне кажется, что я сделал что-то не так...


Ну в целом похоже. Только место для структуры помнится надо задавать до открытия порта (не помню точно). И Event смотреть не обязательно (да и создавать тоже) если GetOverlappedResult пользуешь, функция же возвращает ссостояние.


но че-то это так и не заработало. Кнопку остановки так и нельзя нажать, пока программа ждет приема из КОМ-порта.
Ща попробую ожидание приема в отдельный поток вынести
Go to the top of the page
 
+Quote Post
Aleks17
сообщение May 5 2006, 07:35
Сообщение #11


Местный
***

Группа: Свой
Сообщений: 238
Регистрация: 17-01-05
Из: Новосибирск
Пользователь №: 2 003



Так ты, сдается мне, открыл порт без надлежащего флага
Go to the top of the page
 
+Quote Post
Old1
сообщение May 5 2006, 08:14
Сообщение #12


Знающий
****

Группа: Свой
Сообщений: 697
Регистрация: 26-07-05
Из: Могилев
Пользователь №: 7 095



Цитата(skopus @ May 5 2006, 09:39) *
Цитата(Aleks17 @ May 5 2006, 09:25) *

Цитата(skopus @ May 4 2006, 21:07) *
вот посмотрите

Код
MComPort.Write(&lpBuf,1); // отправили байт в устройство
Timer1->Enabled=true; //включили таймер

           memset(&MComPort.Overlap,0,sizeof(MComPort.Overlap));                   //это для overlap
           MComPort.Overlap.hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);  //MComPort.Overlap - сама структура оверлап
          
           MComPort.Read(SystemAnswer,1); //читаем байт в переменную SystemAnswer

//обработчик события таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
        if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0)
             {
              GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE);
             }
        else {
              Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK);
             }
}


мне кажется, что я сделал что-то не так...


Ну в целом похоже. Только место для структуры помнится надо задавать до открытия порта (не помню точно). И Event смотреть не обязательно (да и создавать тоже) если GetOverlappedResult пользуешь, функция же возвращает ссостояние.


но че-то это так и не заработало. Кнопку остановки так и нельзя нажать, пока программа ждет приема из КОМ-порта.
Ща попробую ожидание приема в отдельный поток вынести

Естественно через таймер не заработает, так как процедура-обработчик стоит на месте пока не сработает WaitForSingleObject, а так как таймер работает в главном потоке - то главный поток стоит.
Выход - организовать процедуру чтения в отдельном потоке - это позволит управлять главным потоком приложения (нажимать на кнопки), но надо иметь в виду, что пока таймаут у WaitForSingleObject не кончится (если еще байты от устройства не пришли во входной буфер ком-порта) поток чтения все равно не убъешь, только если закрыть приложение.
На C++Builder поток реализуется следующим образом:
Код
DWORD WINAPI ReadBites()//процедура потока
{
     if(WaitForSingleObject(MComPort.Overlap.hEvent,10000)==WAIT_OBJECT_0)
             {
                 GetOverlappedResult(MComPort.handle,&MComPort.Overlap,&byte_counter,FALSE);
             }
        else {
              Application->MessageBoxA("Нет отзыва о выполнении движения","Внимание",MB_OK);
             }
}

HANDLE ht=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ReadBites, NULL, 0, &ind);//запускаем поток
CloseHandle(ht);
Go to the top of the page
 
+Quote Post
javalenok
сообщение May 7 2006, 18:58
Сообщение #13


Местный
***

Группа: Участник
Сообщений: 290
Регистрация: 18-02-06
Пользователь №: 14 469



Вообще, потоки для коммуникации нафиг не нужны. Потоки для исполнения процессорных команд нужны. Ведь не зря они ещё виртуальными процессорами называются. Это дибилизм создавать поток, который только и делает, что ждёт окончания ввода-вывода. Более того, если посылать/принимать более одного блока сразу, то overlapped надо по-любому, даже для одного потока.

Если не хочешь с потоками связываться и рулить вводом-выводом прямо из GUI потока, есть два метода. Опробовано на Дельфи.

Метод первый, идентичен модальному диалогу, типа MessageBox. Т.е. процедура не возвращается пока IO не состоиться (либо юзер кликнет cancel). При этом сообщения (отрисовка, нажатия кнопок) доходят до всех окон.

Код
    //Открываешь порт с overlapped:
hFile := CreateFile(PChar(OpenDialog1.FileName), GENERIC_READ, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
Win32Check(hFile <> INVALID_HANDLE_VALUE, 'Opening file "' + OpenDialog1.FileName + '"');

try // close file

   //Инициализируешь OL структуру:
  FillChar(ol, SizeOf(ol), 0);
  ol.hEvent := CreateEvent(nil, true, false, nil);
  Win32Check(ol.hEvent, 'Creating event');

  try // finalize event

        можешь ещё кнопки ненужные тут пов(ы)ключать.

        //зачинаешь запись:
        Win32Check(WriteFile(hFile, buf, Size, Size, @ol)or (GetLastError() = ERROR_IO_PENDING), 'ReadFile');

        ещё можно зафлашить выходной буффер, на всякий случай.

        и ждёшь окончания в цикле:

        repeat

            dw := MsgWaitForMultipleObjects(1, ol.hEvent, False, INFINITE, QS_ALLINPUT);

            Win32Check(dw <> $FFFFFFFF, 'MsgWaitForMultipleObjects returns ' + IntToStr(Size));

            if dw = WAIT_OBJECT_0 then begin

                  // get number of bytes read
                  Win32Check(GetOverlappedResult(hFile, ol, dw, False), 'GetOverlappedResult');
                  //ol.Offset := ol.Offset + Size;
                  if dw <> Size then
                    raise Exception.Create('Written ' + IntToStr(dw) + ' instead of ' + IntToStr(Size))
                 else
                   Break; // а можно следующий блок данных в порт вывести или перейти в режим чтения
            end;

            // если мы тут, то выход из ожидания произощёл из-за сообщения в очереди потока
            Application.ProcessMessages;


        until false;

    finally

      // разрешить кнопки

     CloseHandle(ol.hEvent);

    end;
finally
  CloseHandle(hfile);
end;


Если теперь надо послать ещё одну команду (блок данных), она встанет в очередь драйвера порта.

OverlappedIO нужно применять в любом случае, дабы не блокировать поток, обслуживающий пользовательский интерфейс. Из описания проблемы я не понял, почему следущюю команду нужно посылать до окончания предидущей, отчего такая большая задержка? Команда длинная, устройство не быстрое? Если из-за длинной команды, выж понимаете, что из-за очереди в драйвере, ваше "прерывание" дойдёт только после первого блока. Тут может потребываться разрыв связи (closefile), перед отсылкой новой команды.



Другой подход - асинхронные процедуры.

Создаёшь файл и OL точно так же. Инициацию ввода вывода снабжаешь указателем на
Код
procedure FileIOCompletionRoutine(dwErrorCode: DWORD; dwNumberOfBytesTransfered: DWORD; lpOverlapp: POVERLAPPED) stdcall;


Тут сам соорентируешься, что в ней делать. Когда всё записано/прочитано не забудь порт и событие прикрыть.

Трудность этого метода в том, что цикл обработки сообщений Дельфи, реализованный в модуле Forms.TApplication.Idle(Msg), использует для ожидания процедуру WaitMessage. Это значит, что от выходит из неё, когда приходит сообщение, игнорируя окончания ВВ. А нам нужно, чтобы запенить её на MsgWaitForMultipleObjectsEx:

Код
    Done := False;
//  if Done then WaitMessage;
  if Done then MsgWaitForMultipleObjectsEx(0, Control {fake parameter}, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);


Перекрыть метод по ООП-шному не удалось, пришлось "хакать" по-гразному: делать локальную копию системного модуля Forms.



Вообще эти методы OverlappedIO всем хороши, еслибы билли и тут не напакостил. Если дрыгать окно мышой, или ресайзить управление переходит модальной процедуре виндов, реализованной по типу нашего первого метода. Поэтому, пока пользователь не отпустит мышку, управление будет бегать в системном цикле, а не в нашем, не давая нам возможности проверить OverlappedResult ВВ. Более того, эта нелепая системная процедура виндов, как и борландовская, по какой-то непонятной причине ожидает сообщений (отпусти мышку) через WaitForMessage, вместо MsgWaitForMultipleObjectsEx. Поэтому, асинхронные IOCompletion события до нас также не доходят. Полагаю, эта та самая причина, по которой Windows Explorer приостанавливает копирование файлов, если прогресс-бар схватить мышой. То есть, окна рисуются, кнопки реагируют, а IO не идёт. Если это терпимо, то можно использовать. Я же, когда прогу на замену галимому Windows-терминалу делал, отдельный многобуфферный IO-поток по этой причине создавал и обменивался с ним сообщениями-буфферами. Могу выслать, если интересно.

И главное, никаких левых T-компонент не надо.
Go to the top of the page
 
+Quote Post
unichorn
сообщение May 8 2006, 10:33
Сообщение #14


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

Группа: Участник
Сообщений: 96
Регистрация: 24-09-05
Пользователь №: 8 901



Кстати неплохой пример работы с COM портами есть у Texas Instruments (slaa096b.pdf)
Я на этом примере хорошую DLL для себя в билдере сделал smile.gif


Сам файл прицепить неполучается angry.gif

Сообщение отредактировал unichorn - May 8 2006, 10:31
Go to the top of the page
 
+Quote Post

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

 


RSS Текстовая версия Сейчас: 27th July 2025 - 23:27
Рейтинг@Mail.ru


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