Russian Qt Forum
Октябрь 02, 2024, 07:18 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Qt + последовательный порт  (Прочитано 10906 раз)
vorotislav
Гость
« : Декабрь 24, 2014, 12:07 »

Добрый день. Осваиваю чтение данных из последовательного порта, и возник такой вопрос.
Вот есть метод, который считывает данные:

Код:
QByteArray data;
data.append(_port.readAll());

И на выходе я получаю массив байт.
С этим все понятно.
А в каком виде байты будут? Я без устройства, и поэтому мне приходится гадать, в каком виде каждый байт будет. Знаю точно, что пакет данных выглядит примерно так: (здесь и далее, в хексе)

00 22 11 F0 88 F0 F0 F0 F0 11 F0 77 EE F0 3C

Где первый байт, это байт синхронизации, и он всегда 00, а все остальное еще и кодированные байты, то есть их надо каким то образом декодировать. Подскажите, каким образом это делать? У меня есть таблица, где прописано, что байт 1 это допустим в кодированном виде байт 11. Из чего я могу сделать вывод, что операцию кодирования\декодирования надо будет делать через обычное сравнивание. А как мне каждый байт сравнить? Пробовал так:

Код:
if (data.at(0) == 0xF0)

На что мне сразу предупреждение, что результат проверки всегда будет false:
предупреждение: comparison is always false due to limited range of data type [-Wtype-limits]
if (data.at(0) == 0xF0)
^

Вот, и я вообще запутался. Более того, изначально я не знаю, какой длины пакет. Тот пример, что я привел, это минимальный пакет - заголовок + контрольная сумма. В таком случае, я не знаю, весь ли пакет пришел, пока не расшифрую заголовок пакета.
И еще вопрос, касаемо контрольной суммы. Она всегда находится в конце пакета и составляет 4 байта. И так же в кодировке. Контрольная сумма CRC16, табличная. Нашел (на этом форуме) для Qt метод:
Код:
quint16 crc16_modbus(const QByteArray &array)
{

    static const quint16 wCRCTable[] = {
    0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
    0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
    0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
    0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
    0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
    0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
    0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
    0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
    0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
    0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
    0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
    0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
    0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
    0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
    0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
    0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
    0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
    0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
    0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
    0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
    0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
    0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
    0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
    0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
    0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
    0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
    0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
    0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
    0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
    0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
    0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
    0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040 };

    quint8 nTemp;
    quint16 wCRCWord = 0xFFFF;


    for (int i=0; i < array.length();++i)
    {
        nTemp = (quint8)array.at(i) ^ wCRCWord;
        wCRCWord >>= 8;
        wCRCWord ^= wCRCTable[nTemp];
    }

    return wCRCWord;
}

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

Заранее извиняюсь за, возможно, столь примитивные вопросы, никогда прежде не занимался работой с байтами
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #1 : Декабрь 24, 2014, 12:14 »

Вот что тебе понадобится: QByteArray::constData, qFromLittleEndian, qFromBigEndian, reinterpret_cast.
А readAll плохо использовать. Нужно читать по мере прихода данных и, если не все пришло, хранить их в буфере, а когда все придет парсить.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #2 : Декабрь 24, 2014, 12:17 »

[offtop]
Так и хочется написать небольшую лекцию/хаутушку по данному вопросу, но времени нет. Грустный Все достаточно просто, главное, понять сам подход.
[/offtop]
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
vorotislav
Гость
« Ответ #3 : Декабрь 24, 2014, 12:27 »

Пантер, да, лекции бы не помешало. В теории, все понятно и элементарно. Как только дело дошло до реализации - запутался окончательно.
Парсить заголовок придется сразу, потому что, как я написал выше, длина пакета строится таким образом: 11 байт заголовок (в 10 и 11 байте - длина данных указана) + данные + 4 байта контрольная сумма. Итого, пакет может быть от 15 байт, до 525 байт.
Спасибо за наводку, буду изучать.
Кстати, Пантер, не подскажете такой момент:
Вот возьмем заголовок: 11 байт. В кодированном виде он так и занимает, 11 байт. Но в декодированном виде идет разделение на старший и младший полубайт. Вот например те же 10 и 11 байты, они в декодированном виде разделяются. 10 - младший полубайт, 11 - старший. Как мне с этим работать? И про контрольную сумму я задал вопрос в первом сообщении, не могли бы ответить?
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #4 : Декабрь 24, 2014, 12:31 »

Можно так считывать.
Код
C++ (Qt)
const quint16 dataSize = *reinterpret_cast<quint16*>(data.constData() + 10);
 
или так
Код
C++ (Qt)
const quint16 dataSize = qFromLittleEndian<quint16>(data.constData() + 10);
 
Второй способ предпочтительнее.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #5 : Декабрь 24, 2014, 12:33 »

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

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
8Observer8
Гость
« Ответ #6 : Декабрь 25, 2014, 11:51 »

Цитировать
А в каком виде байты будут?
Можно вывести принятые байты на экран и посмотреть, что посылалось и что пришло

Я так делаю. Пишу одну программу, которая посылает пакеты (эмулятор устройства), по этому примеру: https://github.com/8Observer8/SendingDataToComPort

Пишу вторую (целевую) программу, которая принимает данные, по этому примеру: https://github.com/8Observer8/ReceivingDataFromComPort

Создаю пару виртуальных портов с помощью бесплатной программы VSPE (Virtual Serial Ports Emulator) и соединяю эти программы, то есть, допустим, одна программа посылает в COM1, а другая - считывает из COM2
« Последнее редактирование: Декабрь 25, 2014, 11:54 от 8Observer8 » Записан
vorotislav
Гость
« Ответ #7 : Декабрь 26, 2014, 00:43 »

8Observer8, именно так и делаю Улыбающийся У меня есть две программы, соединенные по сом-порту, сами порты сделаны именно той программой, что вы посоветовали.
Господа, а не подскажите, по этой же теме такой вопрос.
Исходя из ТЗ, в программа должна одновременно прослушивать сразу 3 com-порта, данные на все три передаются одинаковые, сделано для того, чтоб исключить обрыв канала. И возник вопрос, имеет ли смысл прослушивание портов выносить в отдельный поток, или может быть даже в 3 потока, или хватит главного? С потоками дела не имел, только пробовал, благо примеров достаточно, в частности QSerialPort + thread, но постоянно возникали некие ошибки. ВОт прежде чем окунаться с головой в многопоточное программирование, решил у вас поинтересоваться. Заранее благодарен!
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #8 : Декабрь 26, 2014, 08:02 »

Если читать ассинхронно, то потоки не нужны.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Hellraiser
Бывалый
*****
Offline Offline

Сообщений: 451


Просмотр профиля
« Ответ #9 : Декабрь 26, 2014, 08:47 »

Почти так: в простейшем случае можно обойтись без отдельных потоков. Но как только появляется необходимость поддержания непрерывного обмена с девайсами при условии выдерживания миллисекундных таймаутов, особенно под виндой, то сразу появляется необходимость выноса обмена из гуи-потока в отдельный.
Записан
8Observer8
Гость
« Ответ #10 : Декабрь 26, 2014, 10:41 »

Исходя из ТЗ, в программа должна одновременно прослушивать сразу 3 com-порта, данные на все три передаются одинаковые, сделано для того, чтоб исключить обрыв канала. И возник вопрос, имеет ли смысл прослушивание портов выносить в отдельный поток, или может быть даже в 3 потока, или хватит главного?
Не представляю какие должны быть требования, чтобы вынудить использовать потоки для данной задачи. Данные приходят ассинхронно. Программа спокойно работает, пока не сработает очередной слот, куда пришли данные.  Мне видится такая проблема: как узнать, что на выбранном канале произошёл обрыв? Я когда-то несколько лет работал инженером в этом КБ, где делают системы управления для газотурбинных двигателей военных самолётов. В связи с этим конкретный пример. Допустим, нам нужно считывать показания температуры с элемента изделия. У нас есть прибор подсоединённый к компьютеру через три последовательных порта. Из прибора идут три провода с термопарами. Термопары закреплены на целевом элементе, допустим на транзисторе, который установлен на разрабатываемом изделии, а изделие помещено в камеру тепла. По ТУ на изделие при температуре окружающей среды +60 градусов температура этого транзистора не должна превышать столько то градусов. При этом само изделие гоняется в режимах, которые задаёт пульт по зашитой в него программе. Наша задача в течении трёх часов снимать показания температуры (пусть у нас три независимых прибора, присылают значения с интервалом 5 секунд) и сохранять значения в таблицу базы данных для последующего анализа. Три термопары - это для постраховки, на случай если будет случайный обрыв - всякое бывает, кто-то из персонала может подвинуть прибор, к которому подсоединены термопары и отойдёт контакт. Так вот. Как программа определит, что данный канал оборван и нужно сохранять уже из другого? Какой самый простой вариант?
Записан
torwig
Самовар
**
Offline Offline

Сообщений: 134



Просмотр профиля
« Ответ #11 : Декабрь 26, 2014, 10:51 »

Ну у Вас же написано, что присылают каждые 5 секунд данные. Если данных 5 секунд нет - значит обрыв.
А по поводу потоков, асинхронности, вы правы. Если принять, записать - возможно и не нужно отдельного потока на устройство. А если производятся тяжелый какие-то вычисления и идет интенсивный обмен данными (возникают проблемы, задержки и пр.), тогда можно рассмотреть потоки.
Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #12 : Декабрь 26, 2014, 10:54 »

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

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
Hellraiser
Бывалый
*****
Offline Offline

Сообщений: 451


Просмотр профиля
« Ответ #13 : Декабрь 26, 2014, 11:03 »

Повторюсь еще раз: при работе с устройствами, чувствительными к задержке приема/передачи (необходимость обеспечения минимальных пауз между запросом и ответом) и уж тем более не для секундных пауз, а как я выше написал, для миллисекундных, только работа в отдельном потоке может это обеспечить. Простейший тест - подключаем к линии логический анализатор, отправляем небольшие пакеты данных (3 - 12 байт), зажимаем заголовок окна приложения, таскаем окно по экрану, наблюдаем на анализаторе паузы между пакетами. При выносе приема/передачи в отдельный поток такого нет. Для приема пары байт раз в 5 секунд это явно лишнее.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #14 : Декабрь 26, 2014, 14:00 »

Цитата: Hellraiser
зажимаем заголовок окна приложения, таскаем окно по экрану, наблюдаем на анализаторе паузы между пакетами. При выносе приема/передачи в отдельный поток такого нет.

В Qt 5.5 это пофикшено. Это проблемы/фичи винды.
Записан

ArchLinux x86_64 / Win10 64 bit
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.229 секунд. Запросов: 22.