Форум программистов, компьютерный форум, киберфорум
C# Windows Forms
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.99/103: Рейтинг темы: голосов - 103, средняя оценка - 4.99
6 / 5 / 3
Регистрация: 04.06.2015
Сообщений: 250
Записей в блоге: 1

Правильный подход обмена данных с устройствами через COM-порт. Целостность пакетов и производительность обмена

26.02.2018, 23:52. Показов 21340. Ответов 71
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Приветствую вас, коллеги!
Хочу с вами посоветоваться, ибо мне кажется, что я делаю это не совсем правильно, либо что-то не учёл...
Делаю обмен данными с устройством через COM-порт (RS-232) по алгоритму: послал команду в порт (через .Write(...)), поставил задержку (Threading.Sleep(200)), читаю ответ (через .Read(...)). И вот тут меня смущает эта самая задержка. Если ответные пакеты приходят большие (ну например более 60 байтов), то что бы вычитать весь буфер, этой задержки как буд-то не хватает, пакет получаю не весь. Если увеличиваю задержку, то буфер весь вычитывается, однако, во-первых, буфера могут быть разной длины, и для каждого так подбирать задержки - не реально, во-вторых, если с устройством есть возможность обмениваться на разных скоростях (бод), это вообще дур-дом и всякая актуальность, например, выбора более высокой скорости обмена теряется, т.к. задержки будут одинаковыми, если их так железно задавать... В-третьих, когда такая функция обмена, с железно заданным Sleep'ом (подобранным на вскидку, чтоб не потерять большие пакеты), в цикле, например, ведёт обмен, то для более мелких пакетов, такая задержка, опять же, становится не актуальной, т.к. увеличивается драгоценное время простоя, и обмен происходит дольше, чем мог бы быть... Вот по этому я и задаюсь вопросом: как в таком случае правильно следует поступать? Не ужели чем длиннее ответный пакет, тем дольше его нужно ждать, чтоб он вычитался? Как избежать таких диких задержек? У кого был подобный опыт с обменом по СОМ-порту с устройствами, подскажите, пожалуйста, или поделитесь идеями, кто хотя бы понял суть вопроса.

P.S. слышал что-то про подход асинхронного чтения данных из порта, но пока не понял что это и с чем это едят, и мой ли это вообще случай. Если да, то расскажите про такой подход, кто в курсе вообще. Только по понятнее пожалуйста, с примерами, если можно, а то про асинхронные приёмы работы пока вообще не в теме... Особо не доводилось использовать.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
26.02.2018, 23:52
Ответы с готовыми решениями:

Организация обмена данными через LPT-порт
разработайте программу определения адреса LPT - порта и вывода информации через LPT - порт, При этом светодиоды стенда должны отображать: ...

Функция для обмена HEX данными через COM порт
Помогите реализовать функцию работы с COM портом в консольном приложении. Алгоритм такой: Слушаем порт и при появлении HEX команды,...

Синхронизация обмена данных через LiFi
Здравствуйте. Никак не могу придумать способ реализации обмена через свет. Алгоритмом все вроде бы хорошо: "отмигиваем"...

71
Эксперт .NETАвтор FAQ
 Аватар для Storm23
10428 / 5158 / 1825
Регистрация: 11.01.2015
Сообщений: 6,226
Записей в блоге: 34
19.04.2018, 13:31
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Cha1000000 Посмотреть сообщение
каким образом передавать управление основному потоку из потока чтения
Для передачи данных в основной поток используйте BeginInvoke.
0
6 / 5 / 3
Регистрация: 04.06.2015
Сообщений: 250
Записей в блоге: 1
21.04.2018, 14:46  [ТС]
Цитата Сообщение от Storm23 Посмотреть сообщение
используйте BeginInvoke
За всё время, что пишу на C# мне пока почти не приходилось использовать асинхронные методы, за исключением тасков с async и await, по этому с применением Invoke (во всех его вариациях) пока не сталкивался и не знаком. От слова "совсем". Покурил в сети описания этих методов, но пока как-то не совсем догоняю, как в данном случае (на примере моего кода выше) эти инвоки применять ... Буду признателен, если сможете наглядно это показать.

Я вот тут так поразмыслил, а что, если преобразовать метод static void com_OnReceiving(object sender, RTSerialCom.DataStreamEventArgs e) в асинхроныый, добавив ему ключевое слово async, а потом в функции SendCommand, после Transmit, вызывать его через await? В таком случае, по идее, можно будет вообще избавиться от Thread.Sleep между посылкой и ожиданием получения ответа... Т.к. await должен сам по себе заставить вызывающий поток подождать, пока отработает вызываемый метод, а за тем уже продолжить дальше свои действия. Что думаете на этот счёт?

Добавлено через 15 часов 34 минуты
Если я правильно разобрался в применении BeginInvoke/EndInvoke, то мой код должен измениться так (наверное):

C#
1
2
3
4
5
6
7
8
//создал делегат для события чтения
public delegate void ReceiveHandler (object sender, RTSerialCom.DataStreamEventArgs e);
. . .
//в функции SendCommand:
ReceiveHandler handler = new ReceiveHandler (com_OnReceiving);
IAsyncResult resultObj = handler.BeginInvoke(null, new RTSerialCom.DataStreamEventArgs(new byte[255]), null, null);
com.Transmit(TxBuf);  // Посылка пакета
handler.EndInvoke(resultObj);
Судя по отладчику, событие отрабатывает до конца и после Transmit, а потом выполнение идёт дальше по коду функции SendCommand, но на сколько всё верно будет работать, проверить смогу только в понедельник. Ибо дома нет самого устройства для обмена. Надеюсь я всё правильно понял...
0
6 / 5 / 3
Регистрация: 04.06.2015
Сообщений: 250
Записей в блоге: 1
23.04.2018, 10:52  [ТС]
Storm23, нет, всё же что-то я таки не так сделал, или не дописал... Сдаётся мне, что в параметры BeginInvoke нужно что-то другое а не null передать, как минимум первый параметр типа object мне кажется нужно передать, но я не знаю как правильно это сделать, ибо this не передаётся т.к. эти функции у меня находятся в основном статическом классе Program.cs
Или же я в принципе не правильно понял подход?
0
6 / 5 / 3
Регистрация: 04.06.2015
Сообщений: 250
Записей в блоге: 1
24.04.2018, 18:59  [ТС]
В общем вся затея псу под хвост. Видимо моя задумка утопична.
Как ни пробовал добиться передать уведомление основному потоку, что событие случилось и обработка в нём отработала, всё тщетно. То ли я не правильно эти механизмы синхронизации применяю, то ли в принципе подход не верный, либо действительно, от слипов между посылкой пакета и перехватом ответа не обойтись (чего я собственно и пытаюсь отчаянно добиться).
0
Эксперт .NET
 Аватар для Rius
13379 / 7833 / 1696
Регистрация: 25.05.2015
Сообщений: 23,835
Записей в блоге: 14
24.04.2018, 19:53
Цитата Сообщение от Cha1000000 Посмотреть сообщение
Как ни пробовал добиться передать уведомление основному потоку
Вы на форуме-то пробовали искать? Тема избитая.
0
6 / 5 / 3
Регистрация: 04.06.2015
Сообщений: 250
Записей в блоге: 1
24.04.2018, 20:13  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
Вы на форуме-то пробовали искать?
Ну вот пока то, что мне подошло бы не нашёл.
0
Эксперт .NET
 Аватар для Rius
13379 / 7833 / 1696
Регистрация: 25.05.2015
Сообщений: 23,835
Записей в блоге: 14
24.04.2018, 20:21
Процент загрузки e.ProgressPercentage строка 109
Я такой применяю способ. Но он не единственный существующий и не единственно верный.
0
6 / 5 / 3
Регистрация: 04.06.2015
Сообщений: 250
Записей в блоге: 1
24.04.2018, 20:42  [ТС]
Rius, Чуть позже ознакомлюсь с этой ссылкой. Спасибо.
А пока поделюсь своими наблюдениями в этой проблеме... Такое чувство, что решение у меня под носом, но я чего-то не доделываю... Или недопонимаю...
Я покажу что имею. И объясню где что пытаюсь получить, но не получается))
Есть класс с реализацией методов по обмену данных СОМ-порта. В нём есть событие, возникающее в момент получения данных в порт и метод этого события.
classA.cs
C#
1
2
3
4
5
6
7
8
9
public event EventHandler<DataStreamEventArgs> OnReceiving;
 
private void OnSerialReceiving(byte[] res)
{
   if (OnReceiving != null)
   {
      OnReceiving(this, new DataStreamEventArgs(res));
   }
}
В основном же своём классе, Program.cs, где у меня находится моя глобально использующаяся в проекте функция обмена,
я в этой функции подписываюсь на это событие, а в методе подписанном на него, делаю обработку полученных данных.

Program.cs
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private static classA com;
 
com.OnReceiving += com_OnReceiving; //подписываю свой обработчик на событие
 
//сам обработчик
private static int RespLen = 0; // длина ответного пакета
        //_____________Обработчик события чтения данных из COM-порта _______//
        static void com_OnReceiving(object sender, classA.DataStreamEventArgs e)
        {
            ///  Структура ответа:   ///
            // № байта |   Описание
            /*   0     -  STX 0x02    *
             *   1     -  длина       *
             *   2     -  код_команды *
             *   3     -  код_ошибки  *
             *   4     -  № буфера    *
             *   5     -  Данные      *
             * RespLen -  СRC         *
             */
            ///***********************///                        
            RxBuf = new byte[e.Response.Length];
            Array.Copy(e.Response, RxBuf, RxBuf.Length);
            if (RxBuf.Length > 1)
            {
                // обработка, полученного буфера . . .                
            }
        }
Далее в этом же классе в самой функции обмена, после отправки пакета, это событие срабатывает, но получается так, что оно срабатывает в фоне, а код функции обмена при этом независимо идёт дальше. В результате бывает так, что он отрабатывает быстрее, чем успевает дёрнуться в флаг внутри обработчика, сообщающий об успешно обработанном ответе. Тем более, что возникновение этого события происходит с разной задержкой, в зависимости от размера вычитываемого пакета. По этому раньше мне приходилось для ответных пакетов разного размера, подбирать свои слипы между посылкой и получением данных. От этого я хочу уйти! По этому, если бы можно было бы как-то приостанавливать основной поток ровно на то время, пока ответ придёт в порт и событие сработает, и соответственно обработчик тоже разберёт полученное... Вот этого я добиваюсь...
0
Эксперт .NET
 Аватар для Rius
13379 / 7833 / 1696
Регистрация: 25.05.2015
Сообщений: 23,835
Записей в блоге: 14
24.04.2018, 20:44
Цитата Сообщение от Cha1000000 Посмотреть сообщение
он отрабатывает быстрее, чем успевает дёрнуться в флаг внутри обработчика, сообщающий об успешно обработанном ответе
А зачем вам этот флаг необходим?
0
6 / 5 / 3
Регистрация: 04.06.2015
Сообщений: 250
Записей в блоге: 1
24.04.2018, 23:34  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
А зачем вам этот флаг необходим?
Ну, чтоб как-то указать, что буфер получен, и успешно обработан. Типа признак успешного обмена. В дальнейшем я по этому флагу проверяю выполнять следующие команды или нет. Т.е. например если есть несколько команд, посылаемых по очереди, одна за другой, то если, допустим, уже первая команда не отработала (получила битый ответ, или неправильная контрольная сумма ответа, или вообще ответ не пришёл), то я устанавливаю флаг признака ошибки в true, проверяю его, после каждого посыла команды, и если он true, значит делаю return и не посылаю следующие команды... Ну и для других подобных проверок...

Добавлено через 16 минут
Кстати, var task = Task.Factory.StartNew(...); я тоже пробовал. Пробовал даже через task.Wait(); чтоб дождаться, как я надеялся, срабатывания события и выполнения обработчика... Но почему-то так всё равно не сработало...
0
Эксперт .NET
 Аватар для Rius
13379 / 7833 / 1696
Регистрация: 25.05.2015
Сообщений: 23,835
Записей в блоге: 14
25.04.2018, 07:29
Cha1000000,
одно дело передать в поток UI что-то принятое;
а другое - удостовериться в принятии ответа и послать новый запрос.
Эти вещи не обязаны выполняться в одном месте.
Оставьте поток UI в покое и в качестве флага применяйте что-то типа AutoResetEvent. Его и из потока можно установить, и ожидать установки можно.
0
6 / 5 / 3
Регистрация: 04.06.2015
Сообщений: 250
Записей в блоге: 1
25.04.2018, 19:07  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
в качестве флага применяйте что-то типа AutoResetEvent
ООО да!!! Это сработало! Большое спасибо! Теперь обмен просто летает (ну по крайней мере по сравнению с предыдущими версиями)! Нет не слипов между отправкой и приёмом, и тайм-аутов тоже нет.

Storm23, Вам ещё раз отдельное спасибо за предложенные примеры рабочих проектов!
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
25.04.2018, 19:07

Виртуальный COM-порт на STM32, скорость обмена.
Всем привет. Прошу помощи :) Экспериментирую с USB - CDC-класс, стоит задача передать данные в хост на максимально возможной скорости....

Копирование через буфер обмена из 2х полей формы и вставка данных в другую форму
Всем очень добрый день! Подскажите пожалуйста, можно ли реализовать одновременное копирование данных из 2-х полей формы и вставку этих...

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

Запретить вставку текста в TEdit из буфера обмена через Ctrl+V или через контекстное меню
Добрый день! Есть такой вопрос: Что нужно сделать, чтобы в TEdit не было возможности вставить текст из буфера обмена при помощи CTRL+V...

Обмена информации через сокеты
Здравствуйте, уважаемые форумчани. Пожалуйста, помоги сделать эту задачу: В среде Visual C++ написать клиентскую и серверную программы...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
72
Ответ Создать тему
Новые блоги и статьи
Установка нескольких штампов электронной подписи в строго определенных местах файла docx
ВладимирСамохин 19.07.2026
(В!) Работа с Электронной подписью - это неотъемлемая часть современного документооборота. Но что делать, если нужно поставить несколько штампов электронной подписи в строго определенных местах. . .
сукцессия 35. Научная статья о проделанной работе
anaschu 19.07.2026
Написал в формате латекс и пдф
Вангую, что это не пройдёт модерацию, и на неделе я запущу свой сервер.
Hrethgir 19.07.2026
Эта публикация сейчас в песочнице и ждёт приглашения. https:/ / habr. com/ ru/ sandbox/ 295048/ начало и оглавление - Как «пернатого» заставить осваивать новые горизонты опыта через масштабирование. . .
сукцессия 33. открытые вопросы от клауде
anaschu 19.07.2026
"Что накопилось за эту часть А — тринадцать правок, из которых шесть пришли из ваших вопросов и каждая оказалась реальной ошибкой, а не калибровкой: односторонний симбиоз, отсутствующий листопад,. . .
32 сукцессия
anaschu 19.07.2026
сукцессия 28‑мерное ядро стабилизировано Коллеги, фиксирую разбор инженерных правок и их изоморфную проекцию на экономику, меметику и половой отбор. Модель теперь не «подкручивает» сходимость —. . .
сукцессия 31: модель микоризы - это модель ещё нескольких явлений, социальных и экономических
anaschu 18.07.2026
Теория «Всего»: апдейт v1. 1. 2 — 28‑мерное ядро стабилизировано Коллеги, фиксирую разбор инженерных правок и их изоморфную проекцию на экономику, меметику и половой отбор. Модель теперь не. . .
сукцессия 30. Массив проверяющих друг друга моделей
anaschu 18.07.2026
Архитектура сети взаимопроверяющих моделей микоризной сукцессии (v2. 0) Развитие тензорного ОДУ-ядра и создание кросс-платформенного калибровочного полигона Уважаемые коллеги! В продолжение. . .
Грибы - это женщины, деревья - это мужчины. Анти инь янь для союза мужчины и женщины.
anaschu 18.07.2026
ГЛАВНЫЙ НАУЧНО-ФИЛОСОФСКИЙ ВЫВОД: Сексуально-Репродуктивный Капитализм против Государства Моногамии Коллеги, мы вышли на финишную прямую 20-мерного ОДУ-моделирования вековой сукцессии (ветка. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru