|
279 / 156 / 52
Регистрация: 30.06.2011
Сообщений: 1,712
|
|||||||||||
И снова работа с COM портом16.11.2016, 14:03. Показов 12839. Ответов 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
|
|
|
Модератор
3410 / 2182 / 354
Регистрация: 13.01.2012
Сообщений: 8,476
|
|
| 16.11.2016, 16:39 | |
|
d7d1cd
-создаем поток -в потоке бесконечный цикл в котором Sleep(200) -так же в потоке проверка флага "запросить устройство" -программа устанавливает этот флаг -когда поток видит что флаг установлен он делает WriteFile посылая данные которые программа любезно поместила во входной буфер доступный из потока и входит в бесконечный цикл где делает ReadFile читающий данные в выходной буфер потока -в этом самом бесконечном цикле анализируется флаг "прервать запрос" - если программа установила этот флаг, то флаги "прервать запрос" и "запросить устройство" сбрасывается, устанавливается флаги "запрос завершен" и "прервано" -если были ошибки WriteFile или ReadFile мы реагируем на них так как этого требует задача (наверное, эти ошибки фатальны и мы должны установить флаги "запрос завершен" и "ошибка") -ReadFile может вернуть ноль если был таймаут - помним об этом -ReadFile может вернуть не все данные которые послало устройство, а лишь очередную порцию - после каждого ReadFile анализируем выходной буфер потока что бы понять какого рода данные нам пришли распознавая различные типы ответа устройства опираясь на документацию
0
|
|
|
Модератор
3410 / 2182 / 354
Регистрация: 13.01.2012
Сообщений: 8,476
|
|
| 16.11.2016, 16:43 | |
|
-когда мы логически фиксируем окончание ответа выходим из цикла и устанавливаем флаг "запрос завершен" - в выходном буфере потока будут наши данные
0
|
|
|
Модератор
3410 / 2182 / 354
Регистрация: 13.01.2012
Сообщений: 8,476
|
|
| 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
|
||
|
Модератор
3410 / 2182 / 354
Регистрация: 13.01.2012
Сообщений: 8,476
|
|
| 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
|
||
|
Модератор
3410 / 2182 / 354
Регистрация: 13.01.2012
Сообщений: 8,476
|
|
| 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
|
||
|
Модератор
3410 / 2182 / 354
Регистрация: 13.01.2012
Сообщений: 8,476
|
|
| 17.11.2016, 12:52 | |
|
d7d1cd, да, ваш протокол не позволяет однозначно различить ответы, придется либо принять допущение о паузе в конце ответа либо проверять являются ли байты 80 71 контрольной суммой для первых двух с вероятностью ложной фиксации ошибки если первые два байта данных будут совпадать с контрольной суммой, печальный протокол. тогда я бы на вашем месте настроил тайм-ауты и засчитывал конец передачи если ReadFile выходит по тайм-ауту (возвращает ноль)
Добавлено через 42 секунды никаких Sleep в этом случае не нужно - все сделает сама система Добавлено через 51 секунду сверхточного вычисления тайм-аута тоже можно не делать - заложить с запасом, наверняка в течение 200 мс устройство не может молчать
0
|
|
| 17.11.2016, 12:52 | |
|
Работа с COM портом Работа с Com портом Работа с COM портом в Builder C++ 6.0 Работа с COM портом на С++ Builder 6 Работа с USB портом. Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
Генератор паролей (вариант №3)
Maks 30.10.2026
Финальная версия генератора паролей 1С.
Доработка решения в этот раз коснулась только визуальной части, а именно, добавлен реквизит ДопПараметры (тип Булево), который скрывает/ отображает реквизиты:. . .
|
Магическое мышление и магия, современная наука не отличима от магии (говорит ИИ Джемени)
zorxor 30.10.2026
Джемени, расскажи, пожалуйста, что такое магическое мышление, что такое магия, почему современная наука чем-то напоминает магию?
Фраза «Любая достаточно развитая технология неотличима от магии». . .
|
За какое время ИИ может подобрать пароль по хешу (рассказывает Джемени ИИ)
zorxor 30.10.2026
Расскажи, Джемени, если известен хэш МД5, например, за сколько ИИ сможет подобрать соответствующий пароль?
Искусственный интеллект (ИИ) может подобрать простой или средний по сложности пароль по. . .
|
Кто убил Suno? Почему наше солнышко погасло? (диалог с ИИ Джемени)
zorxor 30.10.2026
Как убивали Suno. Хронология.
«Убийство» музыкальной нейросети Suno — это затяжной процесс, в котором технологический хайп стартапа разбился о жесткую реальность судебных исков от гигантов. . .
|
|
Генератор паролей (вариант №2)
Maks 30.10.2026
Обновленная версия ранее созданной обработки.
Добавлены следующие реквизиты типа Булево:
- Буквы;
- Цифры;
- Символы.
Изменена логика команды "Сгенерировать" и процедуры "ПриОткрытии":. . .
|
Генератор паролей (вариант №1)
Maks 29.10.2026
Реализация из решения ниже выполнена исключительно по моей инициативе, дабы облегчить себе работу в создании паролей для администрируемых мною сервисов и пользователей.
Самым главным критерием для. . .
|
Установка ChatGPT (Codex) на Windows без использования Microsoft Store
Fabien159 28.10.2026
Здравствуйте всем читающим! Опишу процесс установки ChatGPT (Codex) на Windows, если по каким-то причинам у вас не получается устанавить его через Microsoft Store.
И так:
1. Скачиваем с. . .
|
Программное добавление префикса в нумерацию документов
Maks 27.10.2026
Реализация из решения ниже выполнена в самописной конфигурации на БСП (помощь начинающему коллеге).
Задача:
Программно добавить префикс в нумерацию документов.
Решение:
В модуле объекта. . .
|