-
Задача казалась довольно простой... Но, на деле, оказалась что решение задачи независимой передачи и приема на одном порту - не так проста.
Проблема в том что функция ReadFile блокирует не только поток (ее ведь можно и вынести в отдельный) но и дескриптор открытого порта. И в это время из другого потока этого же процесса отправить данные через этот же СОМ-порт не получается (используется этот же дескриптор) пока не придет байт на вход для считывания.
Проблема сохраняется даже при использовании Wait-функций, когда принимающий поток находится в режиме ожидания и, по идее, не должен блокировать дескриптор порта. То есть функция ReadFile вызывается только тогда, когда функция WaitCommEvent возвращает маску события и маска анализируется на предмет возникновения именно этого события (пришел байт). Но получается что функция WaitCommEvent тоже блокирует не только свой поток, но и дескриптор открытого порта (наверное), что не позволяет отправлять информацию по этому же порту из другого потока.
Примеры кода пока постить не буду - я их тут уже сотню перепробовал, три дня бьюсь. Заранее спасибо за помощь.
-
из того, что знаю (фискальники,барпринтеры,весы,кассы), работающего через COM-порты, проблема "множественного взаимодействия" разных программ к ним решается COM/DCOM/TCP сервером... т.е. производитель предоставляет "драйвер" работающий в общем стиле, и являющийся "сервером"/объектом к которому все клиенты шлют запросы на чтение/запись, работают через него, а уж он "в одно рыло", с организацией очереди, работает непосредственно с COM-портом.
ну или предлагается "метод"/стиль работы - закрывать порт после операции, т.е. выполнил чек в кассе/1С-е обязательно закрой порты за собой, чтобы параллельно работающая программа переводов/платежей на телефоны тоже могла сделать чек... и тоже закрыть за собой.
естественно, если кто то не "закрывает за собой", или использует метод "прямого доступа" вместе с COM/DCOM объектом (т.е. одна программа правильно другая нет) то возникают проблемы. очень популярная в ЦТО... если контора работает(/взято на поддержку) с программами разных разработчиков.
-
> pihter © (19.02.13 06:11)
Ищем и Читаем по CreateFile - FILE_FLAG_OVERLAPPEВ и как вытекающее во всех функциях смотрим - а что там за параметр lpOverlapped и что такое OVERLAPPED структура.
Литература для изучения - MSDN нужные тебе функции, Рихтер Дж., Кларк Д.Д. - Программирование серверных приложений (работа с потоками, работа с вводом/выводом и пр.), Последовательные интерфейсы ПК. Практика программирования (П.Агуров)
-
> Вариант (19.02.13 09:44) [2]
FILE_FLAG_OVERLAPPEВ=FILE_FLAG_OVERLAPPED
-
> Примеры кода пока постить не буду
А зачем тогда вопрос задавать было?
-
> ну или предлагается "метод"/стиль работы - закрывать порт > после операции, т.е. выполнил чек в кассе/1С-е обязательно > закрой порты за собой, чтобы параллельно работающая программа > переводов/платежей на телефоны тоже могла сделать чек... > и тоже закрыть за собой.
У меня задача критичная ко времени. Нужна возможность постоянно отправлять и принимать данные, пока будешь открывать/закрывать после каждой буквы - данные потеряются.
Я вобщем-то уже разобрался, решил. Но решение мне не понравилось жутко непредскажуемостью работы. Решил через открытие порта на чтение/запись в основном потоке + создание двух потокув на чтение и запись + использование wait-функций в потоке чтения, чтоб не вызывало блокировку дескриптора порта, пока функция чтения ждет входящий байт, и функция записи в соседнем потоке в это время могла записывать. Плюс обращаю внимание, если кто столкнется с такой же проблемой, на правильную работу с OVERLAPPED-структурой. При открытии порта обязательно она должна упоминаться в параметрах CreateFile там в справке написано как, плюс, при любом вызове любой API-функции, связанной с этими портами, нужно создать OVERLAPPED-структуру, забить нулями, создать событие, потом только передавать ее в API-функцию.
В итоге у меня все заработало, но нестабильно (непонятно что с приоритетами на чтение запись в порт, хотя с приоритетами соответствующих потоков все понятно, плюс иногда куда-то теряется собитие прихода байта в порт, и, в результате, событие возникает только при приходе следующего байта, а считывается предыдущий, думаю это из-за игры приоритетов процессов). Вобщем - кошмар и на ктитически-важной боевой задаче применять такое никак нельзя.
Решено было использовать 2 COM-порта, по отдельному процессу-серверу на каждый (чтение/запись) и общение с главным приложением через сообщения windows. 2 СОМ-порта будут каждый подключаться к COM-USB-переходнику, которые будут подключаться к USB-хабу и от него один USB-кабель в машину. Немного костыльно, но очень надежно.
-
> > Примеры кода пока постить не буду > > А зачем тогда вопрос задавать было?
Мне нужно не чтоб все показали где у меня ошибка (еще раз - примеров перепробовал кучу, каждый постить?) а чтоб мне саму концепцию правильную описали, навроде той, что я выше написал.
> А зачем тогда вопрос задавать было?
А вот придет такой же парень, с такой же бедой, а тут уже написано решение - он раз, как прочитает, да как поймет в какую сторону копать, да каааак разберется сам и опыт приобретет и все такое прочее полезное. Если он точно будет знать что таким путем реализовать можно, я ведь реализовал, ему будет уверенее.
-
> пока будешь открывать/закрывать после каждой буквы - данные потеряются. значит тебе бы больше подошёл "общий сервер", тем более, по позже описанному, у тебя все в одной программе. чего стоит выделить 1 поток на работу с COM? остальные обращаются с запросами на запись, а сам поток "обработчик" "генерит наружу" события от чтения (если что прочиталось)...
OVERLAPPED это конечно хорошо, круто, но сам COM устройство последовательное.
> подключаться к COM-USB-переходнику, которые будут подключаться к USB-хабу и от него один USB-кабель в машину. ээээ... нафига тогда COM? раз, как отсюда понял, работа с USB, он то как раз может работать "параллельно". ну, должен. технология новее на десятки лет.
-
> pihter © (26.02.13 06:57) [5]
> Я вобщем-то уже разобрался, решил
Судя по тому, что пишешь дальше - не разобрался до конца. Если ты открыл порт с флагом FILE_FLAG_OVERLAPPED и правильно пользуешься последующими вызовами функций WriteFile, ReadFile,WaitCommEvent, то никакой блокировки на вызовах этих функций у тебя не будет. Все работу с портом в этом случае можно организовать в одном доп. потоке или даже в основном потоке. Единственные блокирующие в этом случае поток вызовы - это вызовы ожидания событий WaitForXXX или MsgWaitForXXX и т.п. И то блокируют только до возникновения ожидаемого события(ий), ошибки или истечения тайм-аута... Считать с порта ты действительно можешь и более одного байта, рассчитывать на получение только одного байта можно только при небольших скоростях порта и небольшим временем обработки полученного и то не факт, что твой поток получит немедленно управление, когда придет ожидаемое событие. Для нормальной работы с портом обычно хватает нормального приоритета процесса и потока. Концепцию работы с последовательными устройствами и работу с функциями ввода вывода можешь найти в той литературе, которую я давал выше в > Вариант (19.02.13 09:44) [2]
-
> Концепцию работы с последовательными устройствами и работу > с функциями ввода вывода можешь найти в той литературе, > которую я давал выше
До сих пор нигде ни разу не встречал в литературе подробное описание структуры OVERLAPPED. Что это такое, для чего она нужна, как она используется "внутре" Windows и какие особенности её применения в пользовательской программе. Насколько помню у Агурова по этому поводу вроде тоже почти ноль информации.
-
> Германн © (27.02.13 01:07) [9]
Рихтер Дж., Кларк Д.Д. - Программирование серверных приложений, есть глава с названием "Cтруктура OVERLAPPED"
-
> Германн © (27.02.13 01:07) [9]
Да и в MSDN даже с предупреждением о наиболее часто встречающейся ошибке дано описание. А примеры кода, варианты различных методик организации ввода вывода у Рихтера описаны очень хорошо, читается "легко", интересно. Легко - это не значит, что сразу все понятно (это кому как), это значит что не тянет спать.
-
> Вариант (27.02.13 07:53) [10] > > > > Германн © (27.02.13 01:07) [9] > > Рихтер Дж., Кларк Д.Д. - Программирование серверных приложений, > есть глава с названием "Cтруктура OVERLAPPED"
Спасибо, почитаю.
-
> OVERLAPPED это конечно хорошо, круто, но сам COM устройство последовательное. :)
-
> значит тебе бы больше подошёл "общий сервер"
Да, вне всякого сомнения. Я так и реализовал.
> тем более, по позже описанному, у тебя все в одной программе
Да. Но хотелось бы сотворить логическую прослойку по принципу: "прикладное приложение прислало моему серверу байт - он его отправил в СОМ-порт, в СОМ-порт пришел байт - сервер отправил его прикладному приложению" все организовать на технологии сообщений Windows.
> чего стоит выделить 1 поток на работу с COM?
Да мне не жалко даже процесс :) даже два процесса, как в итоге оказалось :)
> ээээ... нафига тогда COM?
Значит, я смотрю, чтоб вам было проще помочь, пора уже раскрыть мою задачу. Задача такая: своя открытая реализация телеграфного аппарата. Данные по нашей линии передаются со скоростью 50 бод. Это достаточно медленно даже для того чтоб у меня реле успевали переключать все. Данные передаются путем перемены полярности напряжения в линии с двумя стоп-битами и в пятибитной кодировке. Мне один толковый дядька подсказал что он, когда был молодым, делал эмуляцию работы телеграфа через СОМ-порт (правда там был DOS - прямая работа с контроллером) оказалось что СОМ-порт настроить на такую работу очень просто - это один из штатных режимов его работы. Таким образом моя задача свелась к реализации гальвонической развязки линии и компьютера (это я справился) и написанию вот такого сервера взаимодействия с СОМ-портом (это я тоже справился, но плохо, нужна помощь). Нужно еще написать программу-кодировшик и вообще с терминалом и возможностью хранить библиотеку телеграмм/поиска/печати на принтере и т. п. Но это все я осилю без труда, ибо уже сто раз много подобной фигни делал и имею неплохой опыт в этом. Короче - не беда. Я и с API-функциями не в первый раз встречаюсь, майкросовтовскую справку уже вдоль и поперек излазил, говорю же, сначала казалось вообще - раз плюнуть.
сейчас решил разделить задачу на еще более мелкие - сделать сервера приема и передачи отдельно и независимо (даже работают с разными портами - бог с ним, терпимо) и сервер передачи мне удалось сделать без проблем - все работает, никаких wait-функций не потребовалось, функцию очереди выполняют сообщения windows.
то же самое реализовал и на сервере приема, бог уже с ним, пусть плокирует хоть весь процесс, но, когда принял байт - отправляй сообщение главной программе. и все тоже работает, но через несколько байт (когда три, когда - десять) он просто перестает принимать данные на порт! примерами кода сейчас поделюсь
-
инициализация
var
com_port: string;
DCB : TDCB;
ctm1: TCommTimeouts;
hPort := CreateFile(PChar(com_port),
GENERIC_READ,
0, nil,
OPEN_EXISTING, 0, 0);
if hPort = INVALID_HANDLE_VALUE then
begin
WriteToRcvSrvLog(
'Ошибка: порт '+com_port+' открыть для чтения не удалось', 2);
critical_error := true;
end;
if not GetCommState(hPort, DCB) then
begin
WriteToRcvSrvLog(
'Ошибка: настройки порта '+com_port+' получить не удалось', 2);
critical_error := true;
end;
DCB.BaudRate := 50; DCB.ByteSize := 5; DCB.StopBits := 1; DCB.Parity := NoParity; if not SetCommState(hPort, DCB) then
begin
WriteToRcvSrvLog(
'Ошибка: настройки порта '+com_port+' установить не удалось', 2);
critical_error := true;
end;
if not SetupComm(hPort, 256, 16) then
begin
WriteToRcvSrvLog(
'Ошибка: настройки буферов порта '+com_port+' установить не удалось', 2);
critical_error := true;
end;
if PurgeComm(hPort, PURGE_TXABORT or PURGE_RXABORT or PURGE_TXCLEAR
or PURGE_RXCLEAR) then
begin
WriteToRcvSrvLog(
'Порт '+com_port+' успешно открыт для чтения', 1);
GetCommTimeouts(hport, ctm1);
ctm1.ReadIntervalTimeout := 500;
SetCommTimeouts(hport, ctm1);
PostMessage(StrToInt(ParamStr(1)),WMAT_RECIV_SERVER_REDY,Handle,0);
server_redy:= true;
end
else begin
WriteToRcvSrvLog(
'Ошибка: порт '+com_port+' открыть не удалось', 2);
critical_error := true;
end; чтение begin
FillChar(Buf, 1, 0);
FillChar(Ovr, SizeOf(ovr), 0);
i := 0;
res:=false;
if not Windows.ReadFile(hport, Buf, 1, i, @ovr) then
begin
WriteToRcvSrvLog('Ошибка чтения байта из СОМ-порта.', 1);
end
else begin
WriteToRcvSrvLog('Удачное чтение байта '+IntToStr(Buf)+' из СОМ-порта.', 0);
PostMessage(StrToInt(ParamStr(1)),WMAT_RECIV_BYTE, Buf, 0);
end;
end;
-
> Если ты открыл порт с флагом FILE_FLAG_OVERLAPPED и правильно > пользуешься последующими вызовами функций WriteFile, ReadFile, > WaitCommEvent, то никакой блокировки на вызовах этих функций > у тебя не будет
Значит где-то неправильно делал. На код выше в этом смысле не смотрите - тут уже попытался выпилить и структуры и вэит-функции, максимально упростить и организовать прием не взирая на блокировку. И даже это удалось не полностью :(
> рассчитывать на получение только одного байта можно только > при небольших скоростях порта и небольшим временем обработки > полученного и то не факт, что твой поток получит немедленно > управление, когда придет ожидаемое событие
байты приходят очень медленно - штуки 2 в секунду. обработка, как видите, только послать сообщение. все вполне успевается.
> можешь найти в той литературе, которую я давал выше
вот за литературу - огромное спасибо, очень ценные книги по теме
-
Нет желания использовать готовые библиотеки типа CPort (Dejan Crnila)? Если хочется сделать руками, то можно там по крайней мере посмотреть работу с WaitCommEvent c overlapped
-
Значится... докладываю вдогонку.. Нашел книгу, которую мне тут любезно присоветовали. Там все подробно описано, насчет программирования СОМ-портов. И насчет вэит-функций. И вообще - все что нужно. Дак вот, есть там, среди прочего, готовый пример. Где уже все написано. Все, как положено. Подключил я свое устройство, изменил в сорсах частоту на 50, он и отправляет и принимает через один порт - все вообще шикарно, кроме одного - событие прихода байта все равно куда-то теряется. То есть сами байты - все приходят, но байт отправленный ранее, может прочитаться только в момент прихода следующего и так далее - по цепочке. а для моей задачи критично чтоб байт немедленно читался. какие-нибудь идеи? может прямой доступ? кто пробовал?
-
> Нет желания использовать готовые библиотеки типа CPort (Dejan > Crnila)?
только за, если бы те поддерживали мою нестандартуню частоту и вообще всякие настройки стопбитности и четности. если есть возможность - я руками и ногами за! исходники открыты? можно подправить, если что?
> Если хочется сделать руками, то можно там по крайней мере > посмотреть работу с WaitCommEvent c overlapped
ох как я их уже насмотрелся, даже снились однажды - клянусь )
|