Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2007.02.25;
Скачать: [xml.tar.bz2];

Вниз

Запись в 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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.56 MB
Время: 0.049 c
3-1164903591
SoulLess
2006-11-30 19:19
2007.02.25
UpdateSql


9-1144843652
NightLord
2006-04-12 16:07
2007.02.25
Формулы


11-1148658085
Kealon
2006-05-26 19:41
2007.02.25
FontDialog


15-1170238103
Torry
2007-01-31 13:08
2007.02.25
User Interface


3-1164975601
Виктор1985
2006-12-01 15:20
2007.02.25
Добавись запись в талицу Acess





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский