Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2007.02.25;
Скачать: CL | DM;

Вниз

Запись в COM-порт - WriteFile   Найти похожие ветки 

 
Mr. D.   (2007-01-29 21:40) [0]

Мне нужно асинхронно записывать в COM-порт, то есть послать данные и чтобы управление сразу вернулась, а Windows там уже сама решила когда эти данные можно отправить.

В интернете встречается такой код:

OverWrite: TOverlapped;
WriteBytes: array of Byte;
...

OverWrite.hEvent := CreateEvent(nil, True, False, nil);
WriteFile(hPort, WriteBytes, SizeOf(WriteBytes),  dwWrite, @OverWrite);


В общем то, что нужно... Но ведь каждому CreateEvent нужно делать соответственно CloseHandle. А когда его делать?

Я же не могу написать так:

OverWrite: TOverlapped;
WriteBytes: array of Byte;
...

OverWrite.hEvent := CreateEvent(nil, True, False, nil);
WriteFile(hPort, WriteBytes, SizeOf(WriteBytes),  dwWrite, @OverWrite);
CloseHandle(OverWrite.hEvent);


Созданноемной событие может использоваться? А тогда когда мне делать CloseHandle?

Я конечно могу сделать аля:

if not WaitCommEvent(hPort, dwMask, @OverWrite) then
 WaitForSingleObject(OverWrite.hEvent, INFINITE);
CloseHandle(OverWrite.hEvent);


но это глупо, тем самым повторю синхронную запись...


 
Kolan ©   (2007-01-29 21:45) [1]

«[0] Mr. D.   (29.01.07 21:40)


Мыло дай я тебе кину то что я делал&#133


 
Mr. D.   (2007-01-29 21:47) [2]

roast_84|а тута вот собачка должна быть точно|mail.ru

Но мне огромный компонент и не нужен, мне бы только то место асинхронной записи с CreateEvent и что с ним делать...


 
Kolan ©   (2007-01-29 21:55) [3]

«Но мне огромный компонент и не нужен»

Ну тогда:
function TAnisochronousComm.Write(const Buffer: array of Byte;
 NumberOfBytesToWrite: Cardinal): Cardinal;
begin
 Result := 0;
 if FPortHandle <> INVALID_HANDLE_VALUE then
 begin
   WriteFile(FPortHandle, Buffer, NumberOfBytesToWrite, Cardinal(Result),
     @FOverlappedWrite);
 end;
end;


Помогло?

Класс выглядит так:
 TAnisochronousComm = class
 private
   FPortHandle: THandle;
   FDcb: TDCB;
   FCommTimeOuts: TCommTimeouts;
   FCommStat: TComStat;

   FOverlappedRead: TOverlapped;
   FOverlappedWrite: TOverlapped;

   FPortName: string;
   FBaudRate: Cardinal;
   FPartity: Byte;
   FStopBits: Byte;
   FByteSize: Byte;

   FReadIntervalTimeout: Cardinal;
   FReadTotalTimeoutMultiplier: Cardinal;
   FReadTotalTimeoutConstant: Cardinal;
   FWriteTotalTimeoutMultiplier: Cardinal;
   FWriteTotalTimeoutConstant: Cardinal;

   FInQueueSize, FOutQueueSize: Cardinal;

   FComOpenEvent: THandle;
   function GetPortName: string;
   procedure SetPortName(const Value: string);
   function GetBaudRate: Cardinal;
   procedure SetBaudRate(const Value: Cardinal);
   function GetPartity: Byte;
   procedure SetPartity(const Value: Byte);
   function GetStopBits: Byte;
   procedure SetStopBits(const Value: Byte);
   procedure SetReadIntervalTimeout(const Value: Cardinal);
   procedure SetReadTotalTimeoutMultiplier(const Value: Cardinal);
   procedure SetReadTotalTimeoutConstant(const Value: Cardinal);
   procedure SetWriteTotalTimeoutConstant(const Value: Cardinal);
   procedure SetWriteTotalTimeoutMultiplier(const Value: Cardinal);
   function GetByteSize: Byte;
   procedure SetByteSize(const Value: Byte);
   function GetIsPortOpenned: Boolean;
 protected

 public
   procedure SetUserComState;
   procedure SetUserComTimeOuts;
   function Open: Boolean;
   procedure Close;
   procedure Purge;
   procedure ShowSettingsWindow(WindowHandle: THandle);
   procedure SetRTSSignal;
   procedure ClearRTSSignal;
   function WriteByte(B: Byte): Boolean;
   function ReadByte(var B: Byte; TimeOut: Cardinal):Boolean;
   function GetInQueueSize: Cardinal;
   function Read(var Buffer: array of Byte; NumberOfBytesToRead: Cardinal;
     TimeOut: Cardinal; var IsTimeOutExpire: Boolean): Cardinal;

   function ReadAll(var Buffer: array of Byte; TimeOut: Cardinal;
     var IsTimeOutExpire: Boolean): Cardinal;

   function WaitAndRead(var Buffer: array of Byte; Count: Word): Cardinal;

   function Write(const Buffer: array of Byte; NumberOfBytesToWrite: Cardinal): Cardinal;
   procedure SetCommQueueSize(InQueue, OutQueue: Cardinal);

   constructor Create;
   destructor Destroy; override;
   property PortName: string read GetPortName write SetPortName;
   property BaudRate: Cardinal read GetBaudRate write SetBaudRate;
   property Partity: Byte read GetPartity write SetPartity;
   property StopBits: Byte read GetStopBits write SetStopBits;
   property ByteSize: Byte read GetByteSize write SetByteSize;

   property ReadIntervalTimeout: Cardinal
     read FReadIntervalTimeout write SetReadIntervalTimeout;

   property ReadTotalTimeoutMultiplier: Cardinal
     read FReadTotalTimeoutMultiplier write SetReadTotalTimeoutMultiplier;

   property ReadTotalTimeoutConstant: Cardinal
     read FReadTotalTimeoutConstant write SetReadTotalTimeoutConstant;

   property WriteTotalTimeoutMultiplier: Cardinal
     read FWriteTotalTimeoutMultiplier write SetWriteTotalTimeoutMultiplier;

   property WriteTotalTimeoutConstant: Cardinal
     read FWriteTotalTimeoutConstant write SetWriteTotalTimeoutConstant;
   property PortHandle: THandle read FPortHandle;
   property IsPortOpened: Boolean read GetIsPortOpenned;
   property ComOpenEvent: THandle read FComOpenEvent;
 end;


 
Mr. D.   (2007-01-29 22:03) [4]

Kolan, то есть, ты во всех операция записи используешь одну и ту же структуру FOverlappedWrite...
Закрываешь FOverlappedWrite.hEvent видать только при уничтожении компонента?

Все нормально работает? Компонент абсолютно корректно отработает две подряд идущие комманды:

AnisochronousComm1.Write([$00, $01, $02], 3);
AnisochronousComm1.Write([$00, $02, $03], 3);


Это я чисто для примера... Все ок будет?

P.S. Кстати, интересно зачем ты передаешь число байтов для чтения, у динамических массивов и так выяснить можно: Length(Buffer) ...


 
Kolan ©   (2007-01-29 22:07) [5]

constructor TAnisochronousComm.Create;
begin

 FPortHandle := INVALID_HANDLE_VALUE;
 FOverlappedRead.hEvent := INVALID_HANDLE_VALUE;
 FOverlappedWrite.hEvent := INVALID_HANDLE_VALUE;

 FPortName := "COM1";
 FBaudRate := CBR_115200;
 FPartity := NOPARITY;
 FStopBits := ONESTOPBIT;
 FByteSize := 8;

 FReadIntervalTimeout := 0;

 FReadTotalTimeoutConstant := 0;

 FComOpenEvent := CreateEvent(nil, True, False, nil);
end;

procedure TAnisochronousComm.Close;
begin
 if FPortHandle <> INVALID_HANDLE_VALUE then
 begin
   //Purge;
   CloseHandle(FPortHandle);
   FPortHandle := INVALID_HANDLE_VALUE;
   if FOverlappedRead.hEvent <> INVALID_HANDLE_VALUE then
     CloseHandle(FOverlappedRead.hEvent);
   if FOverlappedWrite.hEvent <> INVALID_HANDLE_VALUE then
     CloseHandle(FOverlappedWrite.hEvent);
   ResetEvent(FComOpenEvent);
 end;
end;


Если честно код этот в 90% моих приложений. Я его год назад написал. уже не помню что там да как, по статье разбирался&#133
Нужно скажу что за статья&#133


 
Kolan ©   (2007-01-29 22:07) [6]

function TAnisochronousComm.Open: Boolean;
begin
 Result := False;
 if FPortHandle <> INVALID_HANDLE_VALUE then
   Close;
 FPortHandle := CreateFile(PAnsiChar(FPortName), GENERIC_READ or GENERIC_WRITE,
   0, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
 if FPortHandle = INVALID_HANDLE_VALUE then
   Exit;
   
 ZeroMemory(@FOverlappedRead, SizeOf(@FOverlappedRead));
 ZeroMemory(@FOverlappedWrite, SizeOf(@FOverlappedWrite));
 FOverlappedRead.hEvent := CreateEvent(nil, False, False, nil);
 FOverlappedWrite.hEvent := CreateEvent(nil, False, False, nil);

 SetUserComState;
 SetUserComTimeOuts;
 Purge;
 Result := True;
 SetEvent(FComOpenEvent);
end;


 
Kolan ©   (2007-01-29 22:07) [7]

Грил давай на мыло :)


 
Mr. D.   (2007-01-29 22:31) [8]

Да в общем все понятно... Только, имхо, у тебя один косяк - все завязано на таймаутах при чтении. А я хочу по другому реализовать - пришла порция данных, вызывается определенное событие.

За код спасибо!


 
Mr. D.   (2007-01-29 22:35) [9]

Все таки меня терзают смутные сомнения, что будет если два раза подряд вызвать:

AnisochronousComm1.Write([$00, $01, $02], 3);
AnisochronousComm1.Write([$00, $02, $03], 3);

Ведь во WriteFile будет передана одна и таже структура FOverlappedWrite.

Тогда что вернет

GetOverlappedResult(..., @FOverlappedWrite, Len, FALSE);

сколько запислось в первый раз или во второй? Или как?


 
tesseract ©   (2007-01-29 22:50) [10]


> Ведь во WriteFile будет передана одна и таже структура FOverlappedWrite.


Про очереди не слышал ? Подобное творчество не допусимо.

Записывай данные в очередь и посылай в порт в отдельном потоке.


 
Mr. D.   (2007-01-30 02:06) [11]

то есть, при работе с COM нужно два потока, один на запись, другое на чтение?


 
Германн ©   (2007-01-30 02:39) [12]


> r. D.   (30.01.07 02:06) [11]
>
> то есть, при работе с COM нужно два потока, один на запись,
>  другое на чтение?

Хочешь почувствовать все прелести "нахождения в ж@@е у "негра"" - добро пожаловать в потоки:) !!!


 
tesseract ©   (2007-01-30 10:17) [13]


> Mr. D.   (30.01.07 02:06) [11]
> то есть, при работе с COM нужно два потока, один на запись,
>  другое на чтение?


Нет! Поток на запись/чтение один. Просто в нём надо реализовать очередь записи/чтения. Например функция "записать" помещает данные в буфер,а поток потом запишет сам, когда время будет.


 
Mr. D.   (2007-01-30 12:30) [14]

tesseract, но как это сделать? Смотри, например, мы ждем чтения, соответственно в асинхронном режиме вызвали ReadFile, потом сделали WaitForSingleObject на событие... ВСЕ, поток то завис в ожидании чтения.

А если в это время нужно что-то записать? Ну будут помещены в буфер данные, но они не отправятся пока на порт не придет какая-нибудь информаця, то есть не произойдет чтения. Нелогично.

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

Странно, что это не реализовано в самой Windows, как это сделано с сокетами, например.


 
Mr. D.   (2007-01-30 12:31) [15]

И еще странно, ведь Kolan утверждает, что его компонент во многих программах, наверняка бы всплыли косяки, значит все работает...?


 
tesseract ©   (2007-01-30 14:56) [16]


>  ВСЕ, поток то завис в ожидании чтения.

Угу, а как процесс завершать-то будешь с таким параметром?
Без знания  про CancelIO  ?
Да и Запись и чтение одновременно нечасто выполняються.

> аверняка бы всплыли косяки, значит все работает...?


И у меня всё работает.

В одном потоке, два можно - но их нужно синхронизировать, а это запросто гемор можно накопить при частом обмене. И не понятно, что может произойти при одновременной попытке считать/записать. в 90% случаях сначала пишем - потом ждём ответа, причем строго ограниченное время. Потом считываем очередь и смотрим, что делать дальше.

И WaitForSingleObject нужно не на Read  ставить, а на WaitComEvent.


 
matt ©   (2007-01-30 15:12) [17]

>>Kolan ©   (29.01.07 22:07) [7]

Можешь кинуть модуль на yogmurand<at>bk.ru?


 
Mr. D.   (2007-01-30 21:45) [18]

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

С одной стороны, мне нужно все время читать из порта, чтобы как только поступили данные - выдать их на консоль. И в тоже время нужно быть готовым отправить данные с консоли.

Как такое реализовать? Как только читающий поток зависнет на WaitForSingleObject - записать нельзя получится, пока он не освободится.


 
Kolan ©   (2007-01-30 22:06) [19]

&laquo;А я хочу по другому реализовать &#151; пришла порция данных,
вызывается определенное событие.&raquo;

Так и работает для этого я использую доп. поток чтения.
Может это и не правильно, но работает нормально&#133


 
Kolan ©   (2007-01-30 22:09) [20]

&laquo;Можешь кинуть модуль на yogmurand<at>bk.ru?&raquo;

Могу :)


 
Kolan ©   (2007-01-30 22:10) [21]

&laquo;Странно, что это не реализовано в самой Windows, как это
сделано с сокетами, например.&raquo;

Ты еще с RS485 не работал :)


 
koha ©   (2007-01-30 22:14) [22]

Я думаю - это зависит от конечного устройства способно ли оно работать в дуплексе и от контролера устройства. И что вообще под COM понимать в данном случае?  Физический порт сис. платы или порт виндовс?


 
koha ©   (2007-01-30 22:25) [23]

> Mr. D.   (30.01.07 12:30) [14]
> Странно, что это не реализовано в самой Windows, как это сделано с сокетами, например.

Что странного сокеты могут, как известноработать в двух режимах : в блокирующем и не блокирующем, А COM как? Я так полагаю он инициализируется как файл и думаю, что это и есть блокирующий режим. И думаю, что на API это не удастся.


 
Kolan ©   (2007-01-30 22:38) [24]

&laquo;Можешь кинуть модуль на yogmurand<at>bk.ru?&raquo;

yogmurand@ukr.net
   SMTP error from remote mailer after RCPT TO:<yogmurand@ukr.net>:
   host mxs.ukr.net [195.214.192.100]: 550 mailbox is not used more than 2 months, mail rejected


 
tesseract ©   (2007-01-30 22:52) [25]

> Как такое реализовать? Как только читающий поток зависнет
> на WaitForSingleObject - записать нельзя получится, пока
> он не освободится.


Ещё раз - а как ты собираешься ЗАВЕРШИТЬ поток зависший на чтении, будешь ждать пока что нибудь не придёт? А если связи нет? Имей в виду RS232 может работать, как в полнодуплексном, так и в полудуплексном режиме. А для переключения режима чтения/записи и проверки наличия подключения существуют CTS/RTS и DTR/DTS.  

ЗЫ : Как правило используется WaitForMultipleObjects для того стобы завести ещё одно событие, на выруб потока.  


>
> С одной стороны, мне нужно все время читать из порта, чтобы
> как только поступили данные - выдать их на консоль. И в
> тоже время нужно быть готовым отправить данные с консоли.


Ну и ?  Надо отправить данные - отправляем, потом переключаемся в чтение, не волнуйся данные пришедшие за это время осядут в буфере. Зато не придёться развлекаться с синхронизацией данных.


 
Kolan ©   (2007-01-30 23:00) [26]

&laquo;Ещё раз &#151; а как ты собираешься ЗАВЕРШИТЬ поток зависший
на чтении, будешь ждать пока что нибудь не придёт? А если
связи нет? &raquo;

Я знаю что это ты не мне :)

Но вот как я делаю. Все мои приложения работают в режиме запрос-ответ(хотя делал и НЕ запрос ответ, это не важно). При записи я указываю сколько ждать ответ, если его нет, то &laquo;таймаут&raquo;.

Связь обеспечивает целая система &#151; объектов 10 наверно.
1 поток постоянно читает из порта. При обнаружении 1 байта он вычитывает все что пришло и отсылает это потоку &#151; который выделяет пакеты.
Поток &laquo;выделения пакетов&raquo; найдя пакет отсылает его дальше.

А запись &#151;  непроблемма имхо. Отправил, а куда там что делось какая разница. Проблема прочитать.


 
tesseract ©   (2007-01-30 23:02) [27]

> Что странного сокеты могут, как известноработать в двух
> режимах : в блокирующем и не блокирующем,


Это только для Windows справедливо, *nix только блокирующие. И редко кто серьёзно пользуется неблокирующими сокетами.


> А COM как?


Попробуй открыть его второй раз и посмотри GetLastError.


> Физический порт сис. платы или порт виндовс?


Устройство Windwos, Ты же не через I/O к нему обращаешься?
Есть ведь и USB-RS232  и Ehternet-Rs232 конвертеры, они через виртуальные порты работают.


> Ты еще с RS485 не работал :)


Да не сильно они отличаються,  API то же.


 
tesseract ©   (2007-01-30 23:05) [28]

>
> Но вот как я делаю. Все мои приложения работают в режиме
> запрос-ответ(хотя делал и НЕ запрос ответ, это не важно)
> . При записи я указываю сколько ждать ответ, если его нет,
> то «таймаут».


Лучше CancelIO при выходе из потока, или сбросить второе событие в WaitForMultipleObject.


> 1 поток постоянно читает из порта.


Маску поставить проще, меньше проблем, выше производительность.


 
matt ©   (2007-01-31 00:56) [29]


> Kolan ©   (30.01.07 22:38) [24]
> 550 mailbox is not
> used more than 2 months, mail rejected
>


это мейл ру неадекватен... мейл получил, спасибо огромное!


 
Германн ©   (2007-01-31 01:12) [30]


> tesseract ©   (30.01.07 23:02) [27]
>
> > Что странного сокеты могут, как известноработать в двух
> > режимах : в блокирующем и не блокирующем,
>
>
> Это только для Windows справедливо, *nix только блокирующие.
>  И редко кто серьёзно пользуется неблокирующими сокетами.
>

Последнюю строку прокомментируй пож-ста.

P.S. И ещё раз, моё имхо. Работать с COM-портом в синхронном (aka блокирующем) режиме - есть очень большая ошибка в подавляющем большинстве случаев! Да, имхо это касается не только COM-порта, но и любого устройства, которое использует аппаратные прерывания!


 
Kolan ©   (2007-02-01 14:25) [31]

> Маску поставить проще, меньше проблем, выше производительность.

Я просто не умел :). Стал искать, нашел вот что:
http://kladovka.net.ru/delphibase/?action=viewfunc&topic=hardsystem&id=10452

Код настолько похож на мой, что без изменений(кроме возможности изменения имени порта) подошел к моим проектам. :)


 
Prohodil Mimo ©   (2007-02-01 15:13) [32]

Kolan ©   (07.02.01 14:25) [31]
Код настолько похож на мой, что без изменений(кроме возможности изменения имени порта) подошел к моим проектам. :)


Код VCL на мой совсем не похож, но к моим проектам подходит вообще без изменений :о)


 
tesseract ©   (2007-02-01 15:21) [33]


> Kolan ©   (01.02.07 14:25) [31]


Мой код тут светился столько раз, что уже лень  копировать :-)

Я там ещё и прямое обращение к драфверам использую.

> Да, имхо это касается не только COM-порта, но и любого устройства,
>  которое использует аппаратные прерывания!


Почитай про IRP - не фига это не ошибка. Он всё равно прерывание не захватывает.  

ЗЫ: А где у сокетов прерывание ?



Страницы: 1 вся ветка

Текущий архив: 2007.02.25;
Скачать: CL | DM;

Наверх




Память: 0.58 MB
Время: 0.029 c
2-1170896851
Vemer
2007-02-08 04:07
2007.02.25
Подобие TLabel


2-1170673904
bagos
2007-02-05 14:11
2007.02.25
speech


2-1170918311
fd979
2007-02-08 10:05
2007.02.25
Проблема вставки в MS Access


3-1164963356
zdm
2006-12-01 11:55
2007.02.25
DEL FROM DBF


15-1170451847
votija
2007-02-03 00:30
2007.02.25
SQL файла и PHP