|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|||||||||||
И снова работа с COM портом16.11.2016, 14:03. Показов 12046. Ответов 67
Метки нет (Все метки)
Приветствую всех. Уже измучился и измаялся весь пытаясь написать функцию. Помогите, пожалуйста...
Описание задачи Создается программа для работы с прибором. Соединение с прибором осуществляется посредством преобразователей, которые при подключении к компьютеру создают виртуальный COM порт. Преобразователь может подключаться к прибору посредством проводного (RS-485) или беспроводного (инфракрасный канал) соединения. Прибор всегда является ведущим, то есть информацию выдает только по запросу от компьютера (получил корректный запрос - выдал ответ). Целостность запросов и ответов определяется циклическим избыточным кодом в конце запроса или ответа. Текущая реализация На данный момент я создал класс для работы с прибором. Экземпляр этого класса будет создаваться и работать в отдельном потоке. В классе будут реализованы конкретные функции для работы с прибором (например - прочитать серийный номер прибора). Каждая из этих функций подготавливает соответствующий запрос прибору и вызывает внутреннюю функцию обмена данными SwapData. Указанная функция, по сути, выполняет основную работу класса: отправляет созданный запрос в COM порт и получает ответ из COM порта. Именно данную функцию я не могу написать. Начало функции SwapData выглядит примерно так:
Описание проблемы Прием ответа от прибора сопряжен с несколькими трудностями. Во первых, ответ необходимо принять максимально быстро. То есть, не должно быть простоев в работе функции SwapData. Во вторых, на один и тот же запрос прибор может ответить разным количеством байт (либо вообще не ответить). Это связано с тем, что прибор может получить некорректный запрос (например, ошибка при передаче). Тогда он, скорее всего, вообще не ответит. Так же в каком-то конкретном приборе может быть отключена какая-то функция и прибор на запрос, связанный с этой функцией, выдаст не ожидаемое количество байт, а другое, которое сигнализирует об ошибке. В связи со всем этим появилась попытка решения задачи приема данных. Попытка реализации чтения данных В нижеприведенном коде я реализовал алгоритм, когдаПривожу код, который отвечает за прием данных:
Помогите советом как реализовать прием данных максимально быстро и полно.
0
|
|||||||||||
| 16.11.2016, 14:03 | |
|
Ответы с готовыми решениями:
67
Работа с COM портом работа с COM портом Работа с COM-портом |
|
86 / 86 / 6
Регистрация: 14.01.2011
Сообщений: 265
|
|
| 16.11.2016, 14:25 | |
|
На сколько я понимаю, для подобных задач, удобней использовать асинхронный режим работы с компортом. Хорошее описание во вложенной pdf
2
|
|
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
| 16.11.2016, 16:16 [ТС] | |
|
ITDeveloper, спасибо за документ. Я его уже читал ранее. Там запись и чтение сделаны асинхронно и в отдельных потоках. И мне уже давно советовали использовать асинхронный режим. Но его использование не решает моей проблемы. Попробую объяснить.
Отправка данных в COM порт Допустим я реализую чтение и запись асинхронно и даже, возможно, в отдельных потоках. По отправке данных в порт вопросов нет. Хотя для этого придется создавать сигнальный объект и после отправки данных ждать установки его в сигнальное состояние. В синхронном же режиме надо просто отправить данные в порт. Функция WriteFile не вернет управление пока все данные не будут отправлены. То есть все то же самое, но в случае асинхронного режима реализация сложнее. Таким образом выигрыша от использования асинхронного режима при отправке данных я пока не вижу. Вижу проигрыш.Чтение данных из COM порта А вот чтение в асинхронном режиме вызывает вопрос: как программа определит, что все данные ответа прибора из COM порта прочитаны? Причем определить это желательно максимально близко к тому моменту, когда в порт поступил последний байт ответа на запрос. Как уже говорилось выше, нужно учитывать, что ответ на запрос может не поступить или поступить не в таком количестве, которое ожидается. В общем, для синхронного и асинхронного режима надо реализовать такой алгоритм: 1. После отправки запроса начать отсчет времени ожидания ответа. 2. Если в течении времени Х в порт не поступило ни одного байта, сообщить об отсутствии ответа. 3. Если в порт поступил хотя бы один байт, значит прибор начал отправлять ответ и между поступлением байт в порт (в теории) будет одинаковое время Т. 4. Если после приема байта прошло время большее, чем Т, значит прибор передал весь ответ.
0
|
|
|
Модератор
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
|
|
| 16.11.2016, 16:39 | |
|
d7d1cd
-создаем поток -в потоке бесконечный цикл в котором Sleep(200) -так же в потоке проверка флага "запросить устройство" -программа устанавливает этот флаг -когда поток видит что флаг установлен он делает WriteFile посылая данные которые программа любезно поместила во входной буфер доступный из потока и входит в бесконечный цикл где делает ReadFile читающий данные в выходной буфер потока -в этом самом бесконечном цикле анализируется флаг "прервать запрос" - если программа установила этот флаг, то флаги "прервать запрос" и "запросить устройство" сбрасывается, устанавливается флаги "запрос завершен" и "прервано" -если были ошибки WriteFile или ReadFile мы реагируем на них так как этого требует задача (наверное, эти ошибки фатальны и мы должны установить флаги "запрос завершен" и "ошибка") -ReadFile может вернуть ноль если был таймаут - помним об этом -ReadFile может вернуть не все данные которые послало устройство, а лишь очередную порцию - после каждого ReadFile анализируем выходной буфер потока что бы понять какого рода данные нам пришли распознавая различные типы ответа устройства опираясь на документацию
0
|
|
|
Модератор
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
|
|
| 16.11.2016, 16:43 | |
|
-когда мы логически фиксируем окончание ответа выходим из цикла и устанавливаем флаг "запрос завершен" - в выходном буфере потока будут наши данные
0
|
|
|
Модератор
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
|
|
| 16.11.2016, 16:44 | |
|
-при желании программа контролирует время в течение которого поток пытается обработать запрос - если оно превышено программа устанавливает флаг "прервать запрос"
0
|
|
|
188 / 41 / 12
Регистрация: 22.02.2016
Сообщений: 149
|
|||||||
| 16.11.2016, 20:00 | |||||||
0
|
|||||||
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
| 16.11.2016, 20:04 [ТС] | |
|
vxg, спасибо за алгоритм. Вот только есть вопрос: чем Ваш алгоритм ожидания ответа лучше моего кода, описанного в первом посте в разделе Попытка реализации чтения данных? Если в цикл добавить функцию
ReadFile, которая "читает данные в выходной буфер потока" и "анализировать выходной буфер потока чтобы понять какого рода данные нам пришли, распознавая различные типы ответа устройства опираясь на документацию", то получится то же самое, что и у Вас, только без потоков и асинхронного режима. Иначе говоря, получится проще, но столь же эффективно.Анализ выходного буфера потока не так прост как кажется, причем даже опираясь на документацию. Простой пример. У прибора запросили какие-то данные размером 10 байт, которые он начал передавать. Программа, например, получила первые 5 байт: 00 02 00 70 A0. Анализируя их программа вычислила, что последние 2 байта 70 A0 - это циклический избыточный код первых трех байт 00 02 00. Кроме этого второй байт 02 подходит под код ошибки. Программа делает вывод, что прибор сообщил об ошибке номер 02 и прекращает прием, сообщив о ней. А прибор то не сообщал об ошибке. Он после этого прислал еще 5 байт, но его уже никто не слышал...Я рассуждаю следующим образом. Если прибор начал присылать данные в порт, то каждый последующий байт будет поступать в него практически с одной и той же задержкой Т, которая является временем передачи одного байта и которая зависит от скорости обмена. Соответственно, "увидев" первый байт в порту, программа "поймет", что прибор начал присылать данные и будет "ждать" следующий байт время Т. Если по истечении времени Т (или, для надежности, по истечении времени 2Т) новый байт не поступил, то программа "считает", что прибор передал все данные. Далее эти данные за один раз считываются в буфер. В этом алгоритме отсутствует необходимость анализировать пришедшие данные во время приема. Кроме этого ожидание поступления всех отправленных данных прибором осуществляется максимально эффективно: программа будет "впустую" ждать только время 2Т.
0
|
|
|
188 / 41 / 12
Регистрация: 22.02.2016
Сообщений: 149
|
||||||
| 16.11.2016, 20:05 | ||||||
|
На всякий случай функции открытия и закрытия порта и создания/удаления потока
0
|
||||||
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|
| 16.11.2016, 20:13 [ТС] | |
|
dstar, я отправил запрос в порт, а Ваш поток прослушивания начал получать ответ. Как он поймет, что все данные поступили и больше данных не будет? Как я уже много раз говорил, определить это надо как можно быстрее.
0
|
|
|
188 / 41 / 12
Регистрация: 22.02.2016
Сообщений: 149
|
|
| 16.11.2016, 20:27 | |
|
Если данных больше нет, он остановится на строке 10 и будет ждать новых данных
Добавлено через 3 минуты В противном случае будет их постоянно читать, пока все не получит и о каждом прочитанном блоке уведомит Вас в с строке 42
0
|
|
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
||
| 16.11.2016, 20:40 [ТС] | ||
|
0
|
||
|
188 / 41 / 12
Регистрация: 22.02.2016
Сообщений: 149
|
||
| 16.11.2016, 20:45 | ||
|
0
|
||
|
86 / 86 / 6
Регистрация: 14.01.2011
Сообщений: 265
|
||
| 17.11.2016, 06:08 | ||
|
1
|
||
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
||
| 17.11.2016, 08:45 [ТС] | ||
|
Добавлено после более внимательного чтения протокола обмена Нашел в протоколе обмена следующее: "Критерием окончания запроса или ответа является гарантированный тайм-аут, длительность которого зависит от выбранной скорости обмена. Стандартные длительности тайм-аута: ...около 5 мс для скорости 9600 бод". Из этого следует, что если байты перестали поступать порт в течении более чем 5 мс, то прибор точно больше ничего не пришлет (кстати, прибор, видимо, точно так же определят, что компьютер больше ничего не пришлет). Из всего этого делаю вывод: метод dstar в этом посте мне подходит. Применение метода dstar к моей функции Начну с того, что мне не очень хочется делать отдельный поток для чтения данных. Конечно, его использование вполне оправдано с точки зрения использования времени процессора: поток не занимает процессорное время пока в порт не поступит байт. Однако, согласно методу dstar, одновременно с этим должен существовать бесконечный цикл, который будет постоянно проверять сколько прошло времени с момента приема последнего байта. А уж если использовать сигналы при работе с портом, то нельзя ли использовать их и тут, чтобы процессор не крутил впустую этот цикл? И еще вопрос: как использовать асинхронный режим, чтобы для функций чтения и записи не создавать отдельных потоков? То есть мне хочется реализовать всю работу по отправке запроса и получению ответа в единственной функции SwapData.
0
|
||
|
Модератор
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
|
|
| 17.11.2016, 08:57 | |
|
d7d1cd, в моем алгоритме нет никакого асинхронного чтения и он лучше вашего предложения в плане производительности так как ничего не ждёт и несравненно более терпим к паузам в передаче будучи ориентирован именно на содержимое сообщения, а не на неверное в общем случае допущение о равномерной передаче. Ваш первый код оборвёт приём при тайм-ауте - просто задайте его равным нужному вам значению и не потребуется анализировать паузы однако в общем случае это неверно. Правильный анализ сообщения не может привести к описанной вами ложной реакции - адекватные протоколы либо содержат длину сообщения в заголовке либо делят сообщения некой заданной уникальной начальной и/или конечной последовательностью либо по заголовку пакета позволяют однозначно определить его тип и длину по документации. Покажите пример запроса.
0
|
|
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
||
| 17.11.2016, 09:22 [ТС] | ||
00 06 03 A0 70 0A 2D BAВ памяти может хранится все, что угодно. Поэтому ответ может быть таким (сами данные расположены со 2 по 11 байты, последние 2 байта - циклический избыточный код CRC16 предыдущих 11 байт): 00 02 80 71 A0 14 DA 07 EE 89 10 FD D0Хочу обратить внимание, что первые 4 байта подходят под ответ прибора об ошибке: код ошибки 02, 3 и 4 байты - это циклический избыточный код CRC16 первого и второго байта. Хотя тут ошибки нет, а просто так "сложились
0
|
||
|
Модератор
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
|
|
| 17.11.2016, 09:55 | |
|
d7d1cd, а можно пожалуйста по байтно описать что должно идти в запросе и что присылается в ответе в случае успеха и в случае ошибки? а то я не разгадал что такое 00 06 в запросе (следующие за ними 03 - это наверное номер (!?) памяти, а A0 - это наверное размер читаемых байт) и что такое 00 в ответе
0
|
|
|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
||
| 17.11.2016, 11:13 [ТС] | ||
00 06 03 A0 70 0A 2D BA, где по порядку:00 - сетевой адрес прибора. У каждого прибора есть свой сетевой адрес для обращения к конкретному прибору, когда на линии RS-485 приборов несколько. Если прибор один, то к нему можно обращаться через сетевой адрес 0.06 - код запроса на чтение памяти.03 - номер памяти, откуда необходимо произвести чтение.A0 70 - адрес в памяти, с которого будет производится чтение.0A - количество байт, которое необходимо прочитать.2D BA - циклический избыточный код CRC16 запросаПолучаем ответ: 00 02 80 71 A0 14 DA 07 EE 89 10 FD D0, где по порядку:00 - сетевой адрес прибора. Если запрос был с нулевым адресом, то прибор тоже отвечает нулем в этом байте.02 80 71 A0 14 DA 07 EE 89 10 - запрошенные 10 байт информации, расположенные в памяти №3 по адресу 0xA070.FD D0 - циклический избыточный код CRC16 ответа.При ошибке (если память №3 отсутствует в приборе) получаем ответ: 00 02 80 71, где:00 - сетевой адрес прибора. Если запрос был с нулевым адресом, то прибор тоже отвечает нулем в этом байте.02 - код ошибки о недопустимости запроса.80 71 - циклический избыточный код CRC16 ответа.P.S. Сообщение об ошибке всегда состоит из 4-х байт.
0
|
||
|
Модератор
3409 / 2180 / 354
Регистрация: 13.01.2012
Сообщений: 8,449
|
|
| 17.11.2016, 12:52 | |
|
d7d1cd, да, ваш протокол не позволяет однозначно различить ответы, придется либо принять допущение о паузе в конце ответа либо проверять являются ли байты 80 71 контрольной суммой для первых двух с вероятностью ложной фиксации ошибки если первые два байта данных будут совпадать с контрольной суммой, печальный протокол. тогда я бы на вашем месте настроил тайм-ауты и засчитывал конец передачи если ReadFile выходит по тайм-ауту (возвращает ноль)
Добавлено через 42 секунды никаких Sleep в этом случае не нужно - все сделает сама система Добавлено через 51 секунду сверхточного вычисления тайм-аута тоже можно не делать - заложить с запасом, наверняка в течение 200 мс устройство не может молчать
0
|
|
| 17.11.2016, 12:52 | |
|
Помогаю со студенческими работами здесь
20
Работа с COM портом Работа с Com портом Работа с COM портом в Builder C++ 6.0 Работа с COM портом на С++ Builder 6 Работа с USB портом. Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога
Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
|
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога
Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
|
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
|
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога
В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
|
|
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога
Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
|
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога
Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
|
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога
Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
|
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования.
Часть библиотеки BedvitCOM
Использованы. . .
|