Перейти к содержанию
    

Windows7: прием байтов через COM-порт без потерь

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

Читать лень, а пофлудить - не лень? Не в произвольном, а FIFO.

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

Да ладно! А если внимательнее прочитать описание WinAPI? ;)

Если почитаете, то, может, немного начнёте разбираться. Но я сомневаюсь и в этом.

А если надо отправить сообщение не главному окну? Или у Вас всегда приложения только с одним окном? :laughing:

Без разницы, какому окну. Любому.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Принимающий тред пишет байты в кольцевой буфер. После каждого обновления содержимого буфера (или не после каждого, а при достижении некоего уровня + по таймауту) посылает нотификацию (оконным сообщением) треду, в котором находится управляющее окно. Управляющее окно, получив нотификацию, читает кольцевой буфер из приёмного треда.
Вы не забывайте, что у ТС не микроконтроллер, а вполне взрослая ПС. Все кольцевые буфера уже сделаны в ядре ОС, зачем ему еще один у себя?

 

Естественно - парсинг потока байт на пакеты и уж тем более - обработку пакетов, желательно делать уже в треде управляющего окна. Не надо смешивать уровни обработки протокола.
И лишние тоже вводить не надо. Нарезку на пакеты вполне можно делать там, где идет прием с порта. Это же не прямая дыра в железный порт, там еще ОС и куча драйверов по дороге.

Конечно, нарезку на пакеты надо делать только в том случае если она не зависит от содержимого пакетов и/или текущего глобального состояния. Но это уже клинические случаи :biggrin:

 

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

Это почему вдруг? Обоснуйте :1111493779:

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Нет никаких проблем. Я сбрасываю событие, но считываю не байт, а буфер, в котором после сброса могут быть еще байты. Если между сбросом и чтением буфера придут еще байты (хотя тут уже огромные скорости нужны), породившие новое событие, то следующее чтение этих байтов не вернет - они уже считаны.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

А если другое не WM_MY_DATA?

 

Если почитаете, то, может, немного начнёте разбираться. Но я сомневаюсь и в этом.

Мне его за Вас читать недосуг. Я то прекрасно знаю, что если исходный тред не имеет окна и связанной с ним обработки очереди сообщений

(а так скорее всего и есть, так как треду, читающему байты из потока COM-порта, окно не нужно), то работа SendMessage() и PostMessage() похожа.

Но я сильно сомневаюсь, что Вам это чем-то поможет. Вы это не читайте, а то вдруг ещё поумнеете! :biggrin:

 

Без разницы, какому окну. Любому.

Ну да, так и думал - представления о работе WinAPI и взаимодействии окон, тредов, сообщений Вы не имеете.

Отсюда начинаем думать, что будет если отправите этому "любому" окну ваши сообщения с указателями на буферы, а это окно получит WM_CLOSE (либо будет закрыто иным образом) и обработает его раньше?

Будет утечка памяти. Что и является обычным явлением в "прогах" написанных подобными Вам деятелями.

 

Вы не забывайте, что у ТС не микроконтроллер, а вполне взрослая ПС. Все кольцевые буфера уже сделаны в ядре ОС, зачем ему еще один у себя?

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

Так что гораздо удобнее работать с этой инфой в пределах одного треда.

И зачем тогда выносить работу с кадрами в другой тред и поиметь кучу проблем с межпотоковым взаимодействием?

И я не говорю, что "нельзя". Я говорю, что "обычно удобнее" делать это в потоке управляющего окна. А уже байтики принимать/передавать в других тредах.

 

Но это уже клинические случаи :biggrin:

По Вашему - изменение например протокола обработки потока байт - это клинический случай?

Или изменение скажем каких-то параметров обработки потока (собственного адреса и т.п) - это клинический случай?

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

Проще сделать это всё в одном потоке окна управления, тем более никаких проблем с этим нет.

 

Это почему вдруг? Обоснуйте :1111493779:

Это чревато утечками памяти. И другими чудесами. См. выше - уже обосновал.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Мне его за Вас читать недосуг. Я то прекрасно знаю, что если исходный тред не имеет окна и связанной с ним обработки очереди сообщений (а так скорее всего и есть, так как треду, читающему байты из потока COM-порта, окно не нужно), то работа SendMessage() и PostMessage() похожа.

Неправда ваша. Поток, не имеющий окна и принимающий данные с com-порта, использует функцию PostMessage() окну, дескриптор которого имеется. Здесь никакого криминала, PostMessage возвращается сразу же после того, как поместит сообщение в очередь.

А вот функция SendMessage() из потока, не имеющего цикла обработки сообщений, является проблемной, т.к. должна дожидаться квитанции (ответного сообщения).

Это теоретические рассуждения, на практике (а я тоже использую PostMessage) программа с SendMessage для передачи сообщений из функции приема данных по com-поту не работает, проверено.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Затем, что обработка кадра - это не сферический конь в вакууме. Для её работы нужна как правило инфа, находящаяся в контролах управляющего окна.
Ой. Вот это и есть 'клинический случай'

 

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

Или изменение скажем каких-то параметров обработки потока (собственного адреса и т.п) - это клинический случай?
Нет, изменение адреса же не меняет формата пакета. Клинический случай это когда нарезка потока байтов на пакеты требует каких то знаний, не вытекающих непосредственно из самих байтов.

Например, если в пакете есть байт, отмечающий начало пакета (B1), конец пакета (B2), и эскейп байт (B3), которым заменяются B1/B2/B3 в потоке (B3 + 1/2/3), то такой пакет может (и должен быть) нарезан сразу в приемном потоке.

А если в пакете сначала идет байт, задающий его тип, а за ним структура, соотвествующая этому типу, то это и есть 'клиника'. Такое действительно лучше резать в основном потоке (а еще лучше такое не использовать)

 

Это чревато утечками памяти. И другими чудесами.
С кривыми руками все черевато :) Для работы с такими буферами нужен определенный порядок. Лучше всего это завернуть в какой нибудь класс.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А вот функция SendMessage() из потока, не имеющего цикла обработки сообщений, является проблемной, т.к. должна дожидаться квитанции (ответного сообщения).

Вот здесь неправда ваша. Никаких сообщений "в ответ" SendMessage не ждёт. Прочитайте MSDN.

 

Это теоретические рассуждения

Это не теоретические рассуждения. В MSDN нет запрета. А там всегда указывают в каком случае функцию нельзя использовать. И даже более того - указывается что и в тредах с окном некоторые сообщения будут обрабатываться даже во время SendMessage, т.е. - даже в этом случае поток полностью не блокируется. А блокируется только цикл выборки/обработки сообщений окна.

А то что у вас что-то не работает - так может надо лучше отлаживать? :laughing:

 

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

Вам повезло. Мне приходилось разрабатывать устройства, умеющие работать по нескольким протоколам. Причём даже одновременно в некоторых случаях. И переключаться между ними без перезагрузки. Ну и сервисное ПО приходилось писать для них.

 

Нет, изменение адреса же не меняет формата пакета. Клинический случай это когда нарезка потока байтов на пакеты требует каких то знаний, не вытекающих непосредственно из самих байтов.

Я вот не пойму - а в чём плюс-то такого разделения? Зачем нужно выделять пакеты именно в одном треде, а обрабатывать в другом (где находится вся нужная инфа для этого)? Какой от этого выигрыш? Почему Вы так за это топите???

Минусов я уже кучу перечислил, а плюсы где???

Если нет плюсов, а только минусы (пускай даже по вашему не очень важные), то зачем так делать????

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я вот не пойму - а в чём плюс-то такого разделения? Зачем нужно выделять пакеты именно в одном треде, а обрабатывать в другом (где находится вся нужная инфа для этого)? Какой от этого выигрыш?
Плюс в том, что для накопления целого пакета нужен 1 буфер, размером с максимальный пакет. Может быть (если очень повезет с алгоритмом нарезки пакетов) то даже не понадобится буфер для чтения собственно данных - их можно будет принимать прямо в буфер пакета (но это если очень повезет)

В любом случае нужно 1/2 буфера фиксированной длинны.

Результат такого приема можно отправлять в обрабатывающую нить в виде уже готовых пакетов в буферах фиксированной длинны. Это позволит реализовать своё управление памятью, гораздо более эффективное (и безопасное), чем malloc с сотоварищами.

 

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

 

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

 

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

 

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

 

Почему Вы так за это топите???

Я не топлю. Я просто обращаю внимание, что иногда сделать сборку пакетов может быть проще, чем кольцевой буфер. И в этом случае их надо собирать. Если же это не проще, и даже гораздо сложней, то действительно нужно делать буфер (не обязательно кольцевой, кстати) и собирать пакеты в главной нити

 

Мне приходилось разрабатывать устройства, умеющие работать по нескольким протоколам.
Сочувствую :cranky:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я вот не пойму - а в чём плюс-то такого разделения? Зачем нужно выделять пакеты именно в одном треде, а обрабатывать в другом (где находится вся нужная инфа для этого)? Какой от этого выигрыш?

выигрыша может и не быть. перефразируя ХВР - ориентироавться надо на простоту. как проще так и делать. и надо учитывать учитывать в это "проще" обработку ошибок и латентность и межпроцессное взаиможействие.

для меня основным критерием выступает то что главная нитка - которая обрабатывает пакеты протокола, если она более менее умная, оказывается нагруженой и завязаной на синхронизацию с другими нитками. тоесть банально может блокироваться на чемто и приемник потеряет данные. Если Ваша нитка завязана у ГУЙ - это верный вариант того что на эту нитку не стоит завязывать задачи реалтайма. (это вроде какраз ваш случай)

если такой опасности нету - то тогда нет проблем обрабатывать прием в главной нитке.

 

PostMessage - вкусная функция, но она отсутствует в линухе и вообще посих стандарте. поэтому я на нее криво смотрю, стараюсь обходиться классическими примитивами синхронизации - семафор, сигнал, мутех

 

иногда сделать сборку пакетов может быть проще, чем кольцевой буфер

а еще кольцевой буфер это засада для процессора х86. потому что побайтовая обработка и непредсказуемое положение следующего символа. вылезает сериализация структур из этого буфера, не то чтобы это сложная задача, но она сложнее чем тупое картирование адреса на буфер. если у вас интенсивность потока становится более менеее плотной, может удивить назгрузка процессора. у АРМ теже грабли ловил.

Изменено пользователем AlexRayne

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот здесь неправда ваша. Никаких сообщений "в ответ" SendMessage не ждёт. Прочитайте MSDN.

Да, извиняюсь, неправильно выразился. SendMessage ожидает окончания процедуры обработки сообщения

 

И даже более того - указывается что и в тредах с окном некоторые сообщения будут обрабатываться даже во время SendMessage, т.е. - даже в этом случае поток полностью не блокируется. А блокируется только цикл выборки/обработки сообщений окна.

А то что у вас что-то не работает - так может надо лучше отлаживать?

Речь ведь об отсылающих тредах без окна?

Цитата из msdn, английским по белому: "The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed"

Так что если отсылающий тред без окна использует SendMessage, он периодически блокируется, хоть заотлаживайся.

Почему я говорю о практике: у Вас реально работает прием по com-порту с SendMessage из треда без окна?

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А если другое не WM_MY_DATA?

А мозг включить никак? Хорошо, отвечу: тоже FIFO. Но ведь вы вещали про то, что порядок пакетов с данными может перепутаться. И как же это может произойти, а?

Мне его за Вас читать недосуг. Я то прекрасно знаю, что если исходный тред не имеет окна и связанной с ним обработки очереди сообщений

(а так скорее всего и есть, так как треду, читающему байты из потока COM-порта, окно не нужно), то работа SendMessage() и PostMessage() похожа.

Какая отборная чушь. И SendMessage и PostMessage работают совершенно независимо от того, имеет ли вызывающий тред окно. Вы бы перестали позориться, а?

Ну да, так и думал - представления о работе WinAPI и взаимодействии окон, тредов, сообщений Вы не имеете.

Отсюда начинаем думать, что будет если отправите этому "любому" окну ваши сообщения с указателями на буферы, а это окно получит WM_CLOSE (либо будет закрыто иным образом) и обработает его раньше?

Очередной перл. Вы что, даже не понимаете разницы между окном и оконной функцией? Ну это уже даже не смешно. Как можно быть настолько дилетантом, и при этом вещать с таким апломбом?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Речь ведь об отсылающих тредах без окна?
да, конечно.

Цитата из msdn, английским по белому: "The sending thread is blocked until the receiving thread processes the message. However, the sending thread will process incoming nonqueued messages while waiting for its message to be processed"

Так что если отсылающий тред без окна использует SendMessage, он периодически блокируется, хоть заотлаживайся.

Вот именно, что блокируется не сам тред, а обработка сообщений из его оконной очереди. Там же написано, что асинхронные сообщения (или какие-то - пишу по памяти) продолжают обрабатываться. Я как-то смотрел исходники цикла обработки соодщений окна, так вот там насколько помню просоо в это время выполнялся другой цикл, который не вытаскивал все сообщения из очереди окна. Но тред при этом продолжал периодически выполняться.

А для безоконного треда вообще такого цикла нет. Для безоконного треда основной цикл треда прописываете Вы сами. Там всё должно быть по другому.

Почему я говорю о практике: у Вас реально работает прием по com-порту с SendMessage из треда без окна?

Я сейчас нахожусь за границей и поэтому посмотреть не могу, пишу по памяти. Как вернусь - тогда посмотрю.

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поздравляю всех с началом новой рабочей недели!

(хотя кто-то уже, наверное, и пиво пьет в тенечке...)

 

В-общем, проблема вроде бы не в методах межпоточного взаимодействия.

На данный момент ситуация такова:

Сниффер (Free device monitoring Studio от HHD) показывает те же самые проблемы как и моя программа - то есть он уже не видит те же байты, которые я не вижу.

Подключенный ко входу логический анализатор показывает что все байты корректно долетают до разъема DB9 на компьютере.

Сейчас тестирую на моем Dell D620- у него ком порт есть прямо внутри. И, кстати, ошибки ловит чаще чем 1037U, на котором штатно должно крутиться.

 

Подключил переходник Serial-USB (FTDI) и пустил через него. Разница есть.

Пока что один раз что-то словил, но это может быть и реальный шум. Прямой ком-порт уже бы раз 10 поймал за это время.

 

Пойду еще раз покурю что у меня там за уровни в COM-порт подаются, может все-таки там какие-то просадки кратковременные.

Ведь ежели сниффер тоже не ловит, а на входе сигнал есть, и переход на USB помогает- то какие выводы из этого следуют?

 

потери выглядят вот так. Сместил байты принятые сниффером, где пусто- это сниффер ничего не принимал.

(смещение в корректном пакете, время анализатора, корректный байт из логического анализатора, байт из сниффера)

это один пакет, 2 байта плюс 1 байт не приняты.

 

98 44.301759 0x3F 3f

99 44.301846 0x4D 4d

100 44.301933 0x0B 0b

101 44.30202 0xC1 c1

102 44.302107 0x40 40

103 44.302194 0x86

104 44.302281 0x36

105 44.302367 0xC1 c1

106 44.302454 0x40 40

107 44.302541 0x9B 9b

...

120 44.303671 0x52 52

121 44.303757 0xC1 c1

122 44.303844 0x3B

123 44.303931 0x28 28

124 44.304018 0xAC ac

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ваш снифер умеет показывать ошибки от COM порта? Если да, то включите их показ. Возможно байт не принялся по причине Framing Error.

Еще попробуйте осцилографом замерить точную частоту (время битового интервала). Потом включите порт на передачу и замерьте то же самое от РС (например через какой нибудь терминал). Если разница будет более 5% - то это оно :) Если более 1% - то может быть оно.

 

Ну и уровни проверьте. Настоящий COM порт должен обеспечивать +/- 12В, но лично я таких давно не видел. Обычно все в районе 5-10В бегает.

У меня даже был один сервер с COM портом который отказался свои же собственные уровни воспринимать (выдавал на выход 5В, на входе хотел не менее 7-8В) :(

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну и уровни проверьте. Настоящий COM порт должен обеспечивать +/- 12В, но лично я таких давно не видел. Обычно все в районе 5-10В бегает.

Стандарт RS-232 предполагает уровни ±3...30 В, с этим диапазоном должны уметь работать все правильные порты. Как правило, периферия, в которой используются преобразователи уровня с удвоением питания, выдает ±6...10В, и это никак не должно сказываться на надежности передачи данных.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...