LINUX.ORG.RU

Конвертация 3 байт в int, я правильно делаю?

 , ,


0

1

В спеках пакетов с данными от некоторой железки написано «Первые 8 байт отбрасываются, а далее идёт 150 чисел. Числа записаны как беззнаковые целые по 3 беззнаковых байта в big endian (старший, средний, младший). Значение отсчёта умножаем на цену деления (2.5v/0xFFFFFF) и вычитаем середину шкалы (1.25v)». Я получаю массив char'ов (QByteArray) и далее для получения всех чисел делаю следующее:

static double SomeClass::getOneReading(QByteArray::ConstIterator &it)
{
    const unsigned char b1 = *(it++);
    const unsigned char b2 = *(it++);
    const unsigned char b3 = *(it++);

    // Не преобразованное показание АЦП
    const quint32 block =
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
            (b1 << 24) +
            (b2 << 16) +
            (b3 << 8);
#else
            b3 +
            (b2 << 8) +
            (b1 << 16);
#endif

    /// Цена деления (коэффициент перевода показаний АЦП в напряжение)
    static const double delta = 2.5 / 0xFFFFFF;
    /// Середина шкалы
    static const double Vcm = 1.25;

    return double(block) * delta - Vcm;
}

void SomeClass::processRecord(const QByteArray &recordDatagram)
{
    QList<double> readings;
    readings.reserve(150);

    // Первые 8 байт нас интересуют
    QByteArray::ConstIterator it = recordDatagram.constBegin();
    for (int i = 0; i < 8; ++i) {
        ++it;
    }

    // Нам интересны 450 байт из пакета с данными
    QByteArray::ConstIterator end = it;
    for (int i = 0; i < 450; ++i) {
        ++end;
    }

    while (it != end) {
        Q_ASSERT(it < end);
        readings << getOneReading(it);
    }

    emit gotRecord(readings);
}

Здесь всё правильно, или же вкралась некая ошибка? Я спрашиваю потому, что производитель железки, данные с которой я обрабатываю, катит на меня бочку, что я, мол, неправильно обрабатываю данные, а потому с некоторой периодичностью получается чушь вместо правильных данных.

Косяк заключается в том, что раз в N пакетов (N~=20-30) вместо нормальных данных получается какой-то непонятный забор, будто от переполнения буфера.

Я почти уверен, что проблема не в моём коде, а в железке, но вдруг ЛОР заметит здесь некий невидимый моим привыкшим к высокоуровневому программированию глазам косяк?

★★★★★

Последнее исправление: Obey-Kun (всего исправлений: 2)
Ответ на: комментарий от tailgunner

Я бы лучше делал так:

class Answer: public QByteArray
{
    public:
        QList<Answer *> sendCommandFactory(QUdpSocket *socket, const IrkutCommand &command);
    protected:
        Answer();
        void markAsInvalid();
};

И несколько классов, наследованных от Answer, которые будут делать markAsInvalid(), если ответ фиговый.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от Obey-Kun

хочешь сказать, что в стандартных библиотеках сокеты кроссплатформенные?

Почему именно в стандартных? Qt не стандартная. И кроссплатформенные библиотеки работы с сокетами таки есть - как минимум boost и libev.

Ну и если не делать ничего сложного, я подозреваю нормальную переносимость и BSD sockets.

tailgunner ★★★★★
()
Ответ на: комментарий от Obey-Kun

Причем тут Irkut? Я говорю о дизайне. Если объект класса Answer представляет из себя ответ устройства, то наследование от QByteArray - это фейл, причем фейл самого дешевого пошиба.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Почему именно в стандартных? Qt не стандартная. И кроссплатформенные библиотеки работы с сокетами таки есть - как минимум boost и libev.

Ну если я привык к сокетам Qt + Qt уже используется в проекте, то смысл?

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от Obey-Kun

Ну если я привык к сокетам Qt + Qt уже используется в проекте

Как показывает этот топик, у тебя Qt головного мозга.

смысл?

Как минимум - сломать стереотипы.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Ты прав, если там будут методы типа QList<double> data() (для DataIrkutAnswer), то QByteArray можно делать защищённым полем. Тогда ок?

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

Как минимум - сломать стереотипы.

Я в других проектах и тот же Boost использую, если он нужен (скажем, Boost::Geometry), но зачем в этом конкретном проекте зоопарк?

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от Obey-Kun

QByteArray можно делать защищённым полем. Тогда ок?

Нет.

0) забудь о QByteArray - он сделан не для того, для чего ты думаешь, а для краткосрочного хранения сырых данных на этапе их валидации (условно говоря, тебе пришел QByteArray из сокета, ты распарсил его в какой-то из ответов устройства, создал нормальный структурированный объект и забыл о QByteArray)

1) на каком-то уровне у тебя должно быть ровно такое описание, какое я дал - Си-структура, описывающая формат пакета от устройства; это необходимо для избежания лишнего байтоебства, которое ты показал в хедпосте

2) для этой низкоуровневой Си-структуры может существовать C++ counterpart, а может и не существовать; если counterpart существует, в ней можно хранить разобранное сообщение (например, массив double), но уж точно не QByteArray

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

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

Obey-Kun ★★★★★
() автор топика
Последнее исправление: Obey-Kun (всего исправлений: 2)
Ответ на: комментарий от Obey-Kun

Вот кусок спека:

Числа идут по 3 беззнаковых байта в big endian (старший, средний, младший). Числа положительные беззнаковые. Преобразуется так: делаем из этих 3 байтов unsigned int (последний байт - 0x00). Значение отсчёта умножаем на цену деления (2.5v/0xFFFFFF) и вычитаем середину шкалы (1.25v).

Всё ещё считаешь, что код неверен?
const quint32 block = (b1 << 16U) | (b2 << 8U) | b3;

Если я правильно понял спеку, то из 0x12, 0x34, 0x56 должно получиться ЗНАЧЕНИЕ 0x12345600 (последний байт 0 в big endian — это младший байт), тогда
const quint32 block = (b1 << 24) | (b2 << 16) | (b3 << 8);

Если в спеке имелся в виду последний байт после конвертации в little-endian (это старший байт) и получить нужно ЗНАЧЕНИЕ 0x00123456, то сдвиги 16/8/0.

В стартовом посте темы BE и LE фрагменты кода были не синхронны между собой, поэтому в своих примерах в отсутствие цитаты из спеки брал за основу LE вариант.

bormant ★★★★★
()
Последнее исправление: bormant (всего исправлений: 1)
Ответ на: комментарий от bormant

Считает оно правильно. Просто иногда имеются странные всплески, но тут (как я уже установил) проблема была в железке.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

Си-структура, описывающая формат пакета от устройства

Так неспортивно. Лучше класс «элемент данных» с конструкторами на все случаи жизни и набором операторов. А уж его можно и в QList, и в QDataStream,...

Suigintou ★★★★★
()
Ответ на: комментарий от Suigintou

Лучше несколько классов, унаследованных от одного + factory тогда уж.

Obey-Kun ★★★★★
() автор топика
Последнее исправление: Obey-Kun (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.