Форум: "Основная";
Текущий архив: 2009.04.19;
Скачать: [xml.tar.bz2];
ВнизПараллельный порт. Асинхронная запись/чтение Найти похожие ветки
← →
nicestep (2008-05-20 20:12) [0]Необходимо провести асинхронную запись / асинхронное чтение параллельного порта COM.
Подключено устройство, работающее по принципу "запрос - ответ". Но дело в том, что ответ от него приходит с порядочной задержкой ввиду специфики работы самого устройства (до ~3 секунд).
Раньше все было реализовано в синхронном варианте (если WriteFile, то ReadFile и все), и для того, чтобы программа не "замирала", чтение с порта было вынесено в поток, а задержки регулировались через таймаут чтения порта (те самые 3 секунды). Но в итоге возникла проблема - иногда получалось так, что устройство начинало отвечать на, к примеру, 2,5 секунде, а на 3 секунде срабатывал таймаут ReadFile и входной пакет "обрубался". При дальнейшей обработке пакет естественно принимался неверным, после чего программа снова пыталась сделать запрос к устройству, которое на данный момент все еще отправляло "ошметки" предыдущего пакета... В результате кавардак. Увеличение таймаута - не выход, слишком много времени будет уходить на запрос к неотвечающему порту, а это ИМХО не дело.
Для этого решил перейти на асинхронный режим. Программа передает байты на порт. Ждет окончания передачи (EV_TXCLEAR) 3 секунды. Если отправка данных завершена успешно, запускается ожидание входных данных (EV_RXCHAR), тоже на 3 секунды. Если это событие происходит, то начинается чтение с порта. Вроде красиво... К сожалению в инете в основном приведены примеры только потоков, которые постоянно читаю с порта, а мне это не нужно.
Запись и чтение вынесены в отдельный поток, для того чтобы все эти ожидания не тормозили систему. Процедура потока приведена ниже:// Поток записи/чтения порта
procedure TPortThread.Execute;
const
TIMEOUT = 3000;
var
BuffStruct: ^TBuffStruct;
EventMask: Cardinal;
Count: Cardinal;
Error: Boolean;
begin
Error := False;
EventMask := 0;
if not WriteFile(FPortHandle, FOutBuff, FOutBuffCount, Count, @FOverlapped)
then
if not WaitCommEvent(FPortHandle, EventMask, @FOverlapped) then
if GetLastError = ERROR_IO_PENDING then
// Запись данных в процессе. Ожидание завершения
if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0
then
Error := True;
if Error and (EventMask <> EV_TXEMPTY) then begin
PostMessage(FWindowHandle, FMessageWriteEnd, 0, 0);
Exit;
end else begin
GetMem(BuffStruct, SizeOf(TBuffStruct));
BuffStruct^.Buff := FOutBuff;
BuffStruct^.Count := FOutBuffCount;
PostMessage(FWindowHandle, FMessageWriteEnd, 1, Integer(BuffStruct));
end;
Error := False;
EventMask := 0;
// Ожидание начала приема байт
if not WaitCommEvent(FPortHandle, EventMask, @FOverlapped) then
if GetLastError = ERROR_IO_PENDING then begin
if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0 then
Error := True;
end;
if not Error or (EventMask = EV_RXCHAR) then begin
GetMem(BuffStruct, SizeOf(TBuffStruct));
with BuffStruct^ do begin
ReadFile(FPortHandle, Buff, Length(Buff), Count, @Overlapped);
PostMessage(FWindowHandle, FMessageReadEnd, 1, Integer(BuffStruct))
end;
end else begin
PostMessage(WindowHandle, MessageReadEnd, 0, 0);
Exit;
end;
end;
Открытие порта и создание события hEvent:PortHandle := CreateFile(PChar(FName), GENERIC_READ or GENERIC_WRITE, 0, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED, 0);
...
FOverlapped.hEvent := CreateEvent(nil, True, False, nil);
В итоге по логу из PortMon.exe получаем, ЧТО программа все правильно пишет в порт, ожидает завершения записи, включает ожидание и... даже если ответные байты приходят, не запускает чтениеIRP_MJ_CREATE Serial0 SUCCESS Options: Open
IOCTL_SERIAL_GET_BAUD_RATE Serial0 SUCCESS
4IOCTL_SERIAL_GET_LINE_CONTROL Serial0 SUCCESS
5IOCTL_SERIAL_GET_CHARS Serial0 SUCCESS
6IOCTL_SERIAL_GET_HANDFLOW Serial0 SUCCESS
IOCTL_SERIAL_GET_BAUD_RATE Serial0 SUCCESS
IOCTL_SERIAL_GET_LINE_CONTROL Serial0 SUCCESS
IOCTL_SERIAL_GET_CHARS Serial0 SUCCESS
IOCTL_SERIAL_GET_HANDFLOW Serial0 SUCCESS
IOCTL_SERIAL_SET_BAUD_RATE Serial0 SUCCESS Rate: 1200
IOCTL_SERIAL_SET_RTS Serial0 SUCCESS
IOCTL_SERIAL_SET_DTR Serial0 SUCCESS
IOCTL_SERIAL_SET_LINE_CONTROL Serial0 SUCCESS StopBits: 1 Parity: NONE WordLength: 8
IOCTL_SERIAL_SET_CHAR Serial0 SUCCESS EOF:0 ERR:0 BRK:0 EVT:0 XON:11 XOFF:13
IOCTL_SERIAL_SET_HANDFLOW Serial0 SUCCESS Shake:1 Replace:40 XonLimit:2048 XoffLimit:512
IOCTL_SERIAL_SET_TIMEOUTS Serial0 SUCCESS RI:100 RM:10 RC:3000 WM:10 WC:10
IOCTL_SERIAL_SET_WAIT_MASK Serial0 SUCCESS Mask: RXCHAR TXEMPTY
IOCTL_SERIAL_PURGE Serial0 SUCCESS Purge: TXCLEAR RXCLEAR
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
IRP_MJ_WRITE Serial0 SUCCESS Length 3: 01 02 03
IOCTL_SERIAL_WAIT_ON_MASK Serial0 SUCCESS
IOCTL_SERIAL_WAIT_ON_MASK Serial0 CANCELLED
Если в код вставить сброс события hEvent:
// Ожидание начала приема байт
if not WaitCommEvent(FPortHandle, EventMask, @FOverlapped) then
if GetLastError = ERROR_IO_PENDING then begin
ResetEvent(FOverlapped.hEvent);
if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0 then
Error := True;
end;
Чтение запускаться будет, но при этом считывать будет всегда 0 байт.
Вообщем, я совсем запутался со всеми этими задержками, ожиданиями, сообщениями от том, что было сообщение о том, что было... Что я делаю неправильно. Вроде и SDK штудирую, не помогает. Что я делаю неправильно? Или может кто-нибудь делал асинхронную работу именно в режиме запрос-ответ, есть наметки?
← →
tesseract © (2008-05-20 20:18) [1]
> Необходимо провести асинхронную запись / асинхронное чтение
> параллельного порта COM.
Com или Rs232 - синхронный последовательный интерфейс. Параллельный LPT или IEEE-1284
> WaitCommEvent(FPortHandle, EventMask, @FOverlapped)
GetOverlappedResult ! Или толку в твоей "Асинхронности" ноль. Все операции с асинхронным чтением/записью делаються в отдельном потоке/потоках, иначе толку ноль.
Ну почитай же наконец документацию.
ЗЫ: Вообще конечно проблема с кадрами..........
← →
nicestep (2008-05-20 20:22) [2]> Com или Rs232 - синхронный последовательный интерфейс.
> Параллельный LPT или IEEE-1284
RS232
> GetOverlappedResult ! Или толку в твоей "Асинхронности"
> ноль. Все операции с асинхронным чтением/записью делаються
> в отдельном потоке/потоках, иначе толку ноль.
Так ведь оно и сделано в отдельном потоке вроде
← →
tesseract © (2008-05-20 20:43) [3]
> Так ведь оно и сделано в отдельном потоке вроде
Рекомендую книгу Павла Агурова. Не код, а каша. ERROR_IO_PENDING совершенно другая функция возвращает. WaitCommEvent тут вообще не к месту.
← →
Сергей М. © (2008-05-20 20:45) [4]
> Так ведь оно и сделано в отдельном потоке вроде
так а нафига тогда асинхронный режим ?
У тебя что - отдельный поток еще чем-то важным занят, нежели транспортом ?
← →
nicestep (2008-05-20 20:58) [5]
WaitCommEvent
...
If the overlapped operation cannot be completed immediately, the function returns FALSE and the GetLastError function returns ERROR_IO_PENDING, indicating that the operation is executing in the background. When this happens, the system sets the hEvent member of the OVERLAPPED structure to the not-signaled state before WaitCommEvent returns, and then it sets it to the signaled state when one of the specified events or an error occurs. The calling process can use one of the wait functions to determine the event object"s state and then use the GetOverlappedResult function to determine the results of the WaitCommEvent operation. GetOverlappedResult reports the success or failure of the operation, and the variable pointed to by the lpEvtMask parameter is set to indicate the event that occurred.
Что я поидее и делаю... Функция вовращает Ложь, GetLastError = ERROR_IO_PENDING, т.е. операция выполняется в фоне. Запускаю одну из "wait functions" - WaitForSingleObject - и ожидаю событие. Т.к. на регистрацию указано их 2:SetCommMask(PortHandle, EV_RXCHAR or EV_TXEMPTY)
то это либо "конец записи на порт", либо "на порт приходят данные".
Да, там сказано, что надо еще использовать и GetOverlappedResult, я почитал SDK, но ничего не понял, для чего она вообще нужна. Примеров с ней тоже нет. Вроде бы эти должны уже справляться с поставленной целью. Может подскажешь, как же мне ее применить правильно тогда?
← →
nicestep (2008-05-20 21:04) [6]> так а нафига тогда асинхронный режим ?
> У тебя что - отдельный поток еще чем-то важным занят, нежели
> транспортом ?
Да ну нафиг не нужен. Все раньше и без него работало прекрасно. Просто надо, чтобы пакеты входные по таймауту не обрубались, при этом не перебарщиваяя с самим значением таймаута. А выход я нашел только один - этот. Да и пора бы уже научиться, ибо с портами работаю уже больше года.
← →
nicestep (2008-05-20 22:55) [7]Эх. Разобрался. Почему то здесь мне не указали на этот промах. Я просто не дожидался конца чтения с порта
> WaitCommEvent тут вообще не к месту.
К месту. Но только при ожидании входящих данных на порт. При записи он был не нужен
> GetOverlappedResult !
Тоже нужна, но только при чтении и только для того, чтобы определить количество присланных байт
Вообщем, вот переписаная рабочая процедура потокаprocedure TPortThread.Execute;
const
TIMEOUT = 3000;
var
BuffStruct: ^TBuffStruct;
EventMask: Cardinal;
Count: Cardinal;
Error: Boolean;
begin
PurgeComm(PortHandle, PURGE_RXCLEAR or PURGE_TXCLEAR);
Error := False;
if not WriteFile(FPortHandle, FOutBuff, FOutBuffCount, Count, @FOverlapped)
then
if GetLastError = ERROR_IO_PENDING then
// Запись данных в процессе. Ожидание завершения
if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0
then
Error := True;
if Error then begin
PostMessage(FWindowHandle, FMessageWriteEnd, 0, 0);
Exit;
end else begin
GetMem(BuffStruct, SizeOf(TBuffStruct));
BuffStruct^.Buff := FOutBuff;
BuffStruct^.Count := FOutBuffCount;
PostMessage(FWindowHandle, FMessageWriteEnd, 1, Integer(BuffStruct));
end;
Error := False;
EventMask := 0;
SetCommMask(FPortHandle, EV_RXCHAR);
// Ожидание начала приема байт
if not WaitCommEvent(FPortHandle, EventMask, @FOverlapped) then
if GetLastError = ERROR_IO_PENDING then begin
if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) <> WAIT_OBJECT_0 then
Error := True;
end;
if not Error or (EventMask = EV_RXCHAR) then begin
// Прием байт начался. Чтение
GetMem(BuffStruct, SizeOf(TBuffStruct));
ReadFile(FPortHandle, BuffStruct^.Buff, Length(BuffStruct^.Buff), Count,
@Overlapped);
if WaitForSingleObject(FOverlapped.hEvent, TIMEOUT) = WAIT_OBJECT_0 then
GetOverlappedResult(FPortHandle, FOverlapped, BuffStruct^.Count, False);
PostMessage(FWindowHandle, FMessageReadEnd, 1, Integer(BuffStruct))
end else begin
PostMessage(WindowHandle, MessageReadEnd, 0, 0);
Exit;
end;
end;
Спасибо Олегу Титову за статью "Работа с коммуникационными портами (COM и LPT) в программах для Win32". И всем вам!
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2009.04.19;
Скачать: [xml.tar.bz2];
Память: 0.5 MB
Время: 0.053 c