Planned maintenance has been done but it did not solve the problem. So work will continue on this and a new time for trying updates will be announced asap.

QDataStream прошу помощи



  • Добрый день!
    Получаю данные из последовательного порта через QSerialPort, данные как и в примерах складываются в QByteArray, далее необходимо парсить эти данные и вот тут я натыкаюсь на грабли. Данные представляют собой последовательность CV (Current Value) и далее 3 32х битных числа, таким образом посылка представляет собой последовательность из 14 байт. Разбираю вот так:

    void Pribor::Read_Data_in_Port()
    {
        const QByteArray data = Port->readAll();
        if ( data.at(0) == 'C' && data.at(1) == 'V' )
        {
        QDataStream ds( data );
        int32_t  A,B,C ;
        QChar Char_1, Char_2;
        ds >> Char_1 >> Char_2 >> A >> B >> C;
        ui->Lab_Ch_A->setText( "Значение A: " + QString::number(A) );
        if ( A == 0 ) ui->Lab_Ch_A->setText("Значение A: Нет сигнала");
    
        ui->Lab_Ch_B->setText( "Значение B: " + QString::number(B) );
        if ( B == 0 ) ui->Lab_Ch_B->setText("Значение B: Нет сигнала");
    
        ui->Lab_Ch_C->setText( "Значение C: " + QString::number(C) );
        if ( C == 0 ) ui->Lab_Ch_C->setText("Значение C: Нет сигнала");
        }
    }
    

    На выходи имею нули, т.е. нет сигнала, а сигнал то есть, если делаю вот так:

    void Pribor::Read_Data_in_Port()
    {
        const QByteArray data = Port->readAll();
        if ( data.at(0) == 'C' && data.at(1) == 'V' )
        {
    
        int32_t  A,B,C ;
        QByteArray Arr1;
    	 
    	 Arr1[0] = data[2];
    	 Arr1[1] = data[3];
    	 Arr1[2] = data[4];
    	 Arr1[3] = data[5];
    	 Arr1[4] = data[6];
    	 Arr1[5] = data[7];
    	 Arr1[6] = data[8];
    	 Arr1[7] = data[9];
    	 Arr1[8] = data[10];
    	 Arr1[9] = data[11];
    	 Arr1[10] = data[12];
    	 Arr1[11] = data[13];
         
    	QDataStream ds (Arr1);	
    	ds >> A >> B >> C;
    	ui->Lab_Ch_A->setText( "Значение A: " + QString::number(A) );
    	if ( A == 0 ) ui->Lab_Ch_A->setText("Значение A: Нет сигнала");
    
    
    	ui->Lab_Ch_B->setText( "Значение B: " + QString::number(B) );
    	if ( B == 0 ) ui->Lab_Ch_B->setText("Значение B: Нет сигнала");
    
    	ui->Lab_Ch_C->setText( "Значение C: " + QString::number(C) );
    	if ( C == 0 ) ui->Lab_Ch_C->setText("Значение C: Нет сигнала");
        }
    }
    

    То все отлично, но это как то неправильно. Прошу подсказать почему QDataStream не распознает символы, пытался сначала распознать 2 символа как строку, результат так же плачевный.



  • Насколько я понимаю, здесь входные данные не были сформированы с помощью QDataStream. QDataStream нельзя использовать для парсинга произвольных бинарных данных, это формат сериализации со своим заголовком и метаданными



  • Да, данные шлет контроллер STM32 из состава устройства. Контроллер формирует данные следующим образом:

    USB_TX_Buff[0] = 'C';
    USB_TX_Buff[1] = 'V';
    
    USB_TX_Buff[2] = ( Value1 >> 24 ) & 0xFF;
    USB_TX_Buff[3] = ( Value1 >> 16 ) & 0xFF;
    USB_TX_Buff[4] = ( Value1 >> 8 ) & 0xFF;
    USB_TX_Buff[5] = Value1 & 0xFF;
    
    USB_TX_Buff[6] = ( Value2 >> 24 ) & 0xFF;
    USB_TX_Buff[7] = ( Value2 >> 16 ) & 0xFF;
    USB_TX_Buff[8] = ( Value2 >> 8 ) & 0xFF;
    USB_TX_Buff[9] = Value2 & 0xFF;
    
    USB_TX_Buff[10] = ( Value3 >> 24 ) & 0xFF;
    USB_TX_Buff[11] = ( Value3 >> 16 ) & 0xFF;
    USB_TX_Buff[12] = ( Value3 >> 8 ) & 0xFF;
    USB_TX_Buff[13] = Value3 & 0xFF;
    
    CDC_Transmit_FS (USB_TX_Buff, 14) ;
    

    Попытка разбора принятых данных изначально была следующей

    void Pribor::Read_Data_in_Port()
    {
        const QByteArray data = Port->readAll();
    
        if ( data.at(0) == 'C' && data.at(1) == 'V' )
        {
    
    		int32_t Number;
    		Number = data[2];
    		Number = Number << 8;
    		Number = Number + data[3];
    		Number = Number << 8;
    		Number = Number + data[4];
    		Number = Number << 8;
    		Number = Number + data[5];
    
    		ui->Lab_Ch_A->setText( "Значение A: " + QString::number(Number) );
    		if ( Number == 0 ) ui->Lab_Ch_A->setText("Значение A: Нет сигнала");
    
    		Number = data[6];
    		Number = Number << 8;
    		Number = Number + data [7];
    		Number = Number << 8;
    		Number = Number + data [8];
    		Number = Number << 8;
    		Number = Number + data [9];
    		ui->Lab_Ch_B->setText( "Значение B: " + QString::number(Number) );
    		if ( Number == 0 ) ui->Lab_Ch_B->setText("Значение B: Нет сигнала");
    
    		Number = data[10];
    		Number = Number << 8;
    		Number = Number + data [11];
    		Number = Number << 8;
    		Number = Number + data [12];
    		Number = Number << 8;
    		Number = Number + data [13];
    
    		ui->Lab_Ch_C->setText( "Значение C: " + QString::number(Number) );
    		if ( Number == 0 ) ui->Lab_Ch_C->setText("Значение C: Нет сигнала");
        }
    }
    

    Вот тут то напутать вроде бы нечего... Но данные выводились то правильно то с ошибками, и ошибки как то близки к 255, но не понятно из-за чего возникают...
    Когда прибор снимает показания 850 попугаев и на компьютере 850 попугаев, когда прибор снимает 1000 попугаев на компьютере 750 попугаев, когда прибор снимает 1100 попугаев на компьютере снова 1100 и снова все в порядке.
    Из-за этого и стал копать в сторону QDataStream.



  • @Konstantin-Tokarev said in QDataStream прошу помощи:

    QDataStream нельзя использовать для парсинга произвольных бинарных данных

    Не, ну для простых типов (int, double и пр.) или raw данных то можно.



  • @kuzulis said in QDataStream прошу помощи:

    Не, ну для простых типов (int, double и пр.) или raw данных то можно.

    Вот тут то и вопрос как мне правильно отпарсить первые два символа, так как копировать один QByteArray в другой это по моему не правильно.

    Возможно вопросы глупые, признаюсь только начинаю все это дело изучать )))



  • @kuzulis said in QDataStream прошу помощи:

    @Konstantin-Tokarev said in QDataStream прошу помощи:

    QDataStream нельзя использовать для парсинга произвольных бинарных данных

    Не, ну для простых типов (int, double и пр.) или raw данных то можно.

    Но зачем так извращаться, когда можно использовать QByteArray::fromRawData и конвертировать в нужный тип? Только не забыть endian учесть



  • @Konstantin-Tokarev said in QDataStream прошу помощи:

    Но зачем так извращаться, когда можно использовать QByteArray::fromRawData и конвертировать в нужный тип? Только не забыть endian учесть

    Изврат - это использовать QByteArray::fromRawData(), ИМХО. C QDataStream это проще.

    Вот тут то и вопрос как мне правильно отпарсить первые два символа, так как копировать один QByteArray в другой это по моему не правильно.

    Foo::onReadyRead()
    {
        for (;;) {
            const auto bytesAvailable = m_serial->bytesAvailable();
            if (bytesAvailable < 14)
                return;
            // Synchronizing... Lookup for 'CV' sequence.
            char c = 0;
            if (!m_serial->getChar(&c))
                continue;
            if (c != 'C')
                continue;
            if (!m_serial->getChar(&c))
                continue;
            if (c != 'V')
                continue;
    
            // 'CV' sequence where received, now we can read data.
            QDadaStream in(m_serial);
            quint32 value1 = 0;
            quint32 value2 = 0;
            quint32 value3 = 0;
            in >> value1 >> value2 >> value3;
    
            // Values where received... Handle values...
        }
    }
    


  • @kuzulis QDataStream вообще не для того предназначен, это формат сериализации



  • Для удобного чтения бинарных данных можно использовать, например, std::stringstream



  • Решил проблему с "лишними символами" удалением их из QByteArray, после чего передаю его в QDataStream и все отлично парсится без лишних массивов

    void Pribor::Read_Data_in_Port()
    {
        QByteArray data = Port->readAll();
        
        if ( data.at(0) == 'C' && data.at(1) == 'V' )
        {
    
        int32_t  A,B,C ;
        
        data.remove(0,2);
    		
        QDataStream ds (data);	
    		
        ds >> A >> B >> C;
    		
        ui->Lab_Ch_A->setText( "Значение A: " + QString::number(A) );
        if ( A == 0 ) ui->Lab_Ch_A->setText("Значение A: Нет сигнала");
    
        ui->Lab_Ch_B->setText( "Значение B: " + QString::number(B) );
        if ( B == 0 ) ui->Lab_Ch_B->setText("Значение B: Нет сигнала");
    
        ui->Lab_Ch_C->setText( "Значение C: " + QString::number(C) );
        if ( C == 0 ) ui->Lab_Ch_C->setText("Значение C: Нет сигнала");
        }
    }
    

    Прошу комментариев/критики, чем грозит перевод данного QByteArray из const ?



  • Ничем не грозит, const вообще был лишним в оригинале



  • А вот использование QDataStream грозит проблемами в будущем, особенно если не зафиксировать версию формата и endianness



  • А вот использование QDataStream грозит проблемами в будущем, особенно если не зафиксировать версию формата и endianness

    Вот тут я поспорю, т.к. версия формата тут ни к месту (еще раз повторюсь - простые типы сериализуются без всяких "выкрутасов", как есть), и я не понимаю, причем тут endianless вообще (даже лучше что оно есть)?

    Решил проблему с "лишними символами" удалением их из QByteArray,

    Ты неправильно читаешь из порта.. как правильно я тебе выложил код выше.



  • еще раз повторюсь - простые типы сериализуются без всяких "выкрутасов", как есть

    Нет никаких гарантий, что в следующих версиях формата это не изменится, или вообще внутри не будет какой-нибудь CBOR

    я не понимаю, причем тут endianless вообще

    Тогда о чем с вами дальше говорить



  • @kuzulis said in QDataStream прошу помощи:

    Ты неправильно читаешь из порта.. как правильно я тебе выложил код выше.

    Прошу пояснить почему не правильно читаю? Это конструкция взята из примеров QSerialPort. Вот отсюда https://doc.qt.io/qt-5/qtserialport-terminal-example.html



  • @VasiliyVN said in QDataStream прошу помощи:

    Прошу пояснить почему не правильно читаю?

    Port->readAll();

    Даю подсказку: что readAll() по твоему мнению должен делать? и что значит all, это сколько? и зачем вообще вычитывать в QByteArray?

    ЗЫ: Я не хочу каждый раз (снова и снова) разжевывать очевидные вещи (я устал)... Дальше уже разбирайся сам.



  • @kuzulis said in QDataStream прошу помощи:

    Прошу пояснить почему не правильно читаю?

    Port->readAll();

    Даю подсказку: что readAll() по твоему мнению должен делать? и что значит all, это сколько? и зачем вообще вычитывать в QByteArray?
    ЗЫ: Я не хочу каждый раз (снова и снова) разжевывать очевидные вещи (я устал)... Дальше уже разбирайся сам.

    Ну тут как бы понятно, radAll это считать все что есть в буфере порта на момент когда пришел сигнал readyRead. И логично предположить, что этот QByteArray надо успеть отпарсить ну и среагировать до следующего прихода данных.



  • @VasiliyVN said in QDataStream прошу помощи:

    надо успеть отпарсить

    Не надо ничего успевать, т.к. QSP все данные буферизирует у себя.

    все что есть в буфере порта на момент когда пришел сигнал readyRead

    Ну а теперь посмотри на свой код, а если придет только один или два байта? Что у тебя случится тогда?



  • @kuzulis said in QDataStream прошу помощи:

    Ну а теперь посмотри на свой код, а если придет только один или два байта? Что у тебя случится тогда?

    Ну это только кусочек кода, я же не буду выкладывать всю свою портянку сюда, она вряд ли кому интересна.
    Прибор шлет разные посылки и самая короткая в три байта - это такой самопальный idle, если порт открыт а idle не приходит в определенный таймаут, то программа закрывает порт и говорит что это не ее прибор как то так.

    Соответственно если придут 2 байта или 1 то вывалится программа с ошибкой, так как посылка будет принята а QBA такого индекса иметь не будет, понял, введу дополнительную проверку. Спасибо!


Log in to reply