Помогите с вопросом
От: milkpot Россия  
Дата: 29.03.24 15:53
Оценка:
Здравствуйте, есть устройство, передающее в хост изображения размером 640Х480 пикселей (каждый пискель — 4 байта)
со скоростью 50 кадров в секунду по протоколу Udp. У пикселя один байт в оттенках серого, остальные три — это rgb.
Первая версия принимала пакеты в главном потоке, в результате чего замерзало окно приложения.
Перенос readPendingDatagrams в рабочую нить привел к тому, что приложение перестало принимать пакеты.
Wireshark видит, что пакеты приходят к хосту от устройства, а приложение пакеты не видит.
Вот фрагменты кода

class Worker2 : public QObject
{
    Q_OBJECT
public:
    QMutex mutex;
    QWaitCondition wtc_worker;
   // QPixmap pixmap_copy;
    bool wait_cnd;
    bool b_counter;
    bool c_variable=true;
    int data_quant=81920;
    QByteArray another_qba;
    QUdpSocket *socket;
    quint16 package_num;
    quint16 str_num;
    int color_shift=0;
    int i_counter=0;
    explicit Worker2()
    {
        //i_counter=0;
        another_qba.resize(2000000/*1228800*/);
        socket=new QUdpSocket(this);
        bool result;
        QVariant value1;
        value1=QVariant(2000000/*100000*/);
        qDebug() << "QVariant-value1 " << value1.toInt();

        result=socket->bind(QHostAddress(QString("192.168.0.1" /*"127.0.0.1"*/)) /*QHostAddress::AnyIPv4*/ /*LocalHost*/,61000/*50011*/);
        QString algostr;
        algostr=QString(tr("bind returned %1")).arg(result);
        qDebug() << algostr;
        qDebug() << result;
        if(result)
        {
            qDebug() << "PASS";
        }
        else
        {
            qDebug() << "FAIL";
            qDebug() << "error" << socket->error();
            qDebug() << socket->errorString().toLocal8Bit();
        }
        socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption,value1);
        value1=socket->socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption);
        qDebug() << "QVariant-value1_ is " << value1.toInt();
       // do_send_result(algostr);
        connect(socket, &QUdpSocket::readyRead,this,&/*UDPresource*/Worker2::readPendingDatagrams);
        qDebug() << "connect-readPendingDatagrams";
        connect(this,&Worker2::writeUdpData,this,&Worker2::setUdpData,Qt::QueuedConnection);
        qDebug() << "connect-setUdpData";
    }

    void  /*Window::*/writeData(const QByteArray& qba)
    {
        QString str;
        QString filename("binaryData.bin");
        QFile file;
        QDataStream dstrm;
        int data_quant=0;
        QByteArray qba_local;
        qba_local.resize(64);

        file.setFileName(filename);

        if(false==file.open(QIODevice::/*WriteOnly*/Append))
        {
            str.append(" *** ОШИБКА: не удалось открыть выбранный файл");
            /*appendtoLogListBox*/do_send_result(str);
            return;
        }
        dstrm.setDevice(&file);

        //data_quant=dstrm.readRawData((char *)&buffer_vect[0],fileSize);
        data_quant=dstrm.writeRawData(qba.constData(),qba.size());

        file.close();

    }

public slots:
    void doWork(const QString &parameter) {
        QString result;
        bool booL_condition=true;
        b_counter=false;
       // another_qba.resize(data_quant);
       // result=QString(tr("--- another_qba _cnd 2 _ is resized, size is %1")).arg(data_quant);
       // do_send_result(result);

        /* ... here is the expensive or blocking operation ... */
        while(booL_condition)
        {
            mutex.lock();
            wait_cnd=wtc_worker.wait(&mutex, 5400000/*one_and_a_half_hour_wait*/);
       // emit resultReady(result);
           // do_send_result(result);
           // mutex.unlock();
            if(false==wait_cnd)
            {
                result=QString(tr("*** wait_cnd 2 Worker _ is false"));
                do_send_result(result);
                mutex.unlock();
                continue;
            }
            else if(true==wait_cnd)
            {
 //                while(c_variable)
 //                {
 //                    if(i_counter==0)
//                    prepare_generate_data();
 //                    ::Sleep(20);
 //                }
                observe_data();
                result=QString(tr("--- wait_cnd 2 _ is true"));
                do_send_result(result);
            ;
            //::Sleep(30);
                mutex.unlock();
            }

        }
    }
public: 
    void observe_data()
    {
        QString str;//ReadOnly
        QString filename("binaryData.bin");
        QString filenameout("dataOut.bin");
        QFile file,file_2;
        QDataStream dstrm;
        QDataStream dstrm_2;
        int data_quant=0;
        QByteArray qba_local;
        qba_local.resize(648);

        ;
        file.setFileName(filename);
        file_2.setFileName(filenameout);

        if(false==file.open(QIODevice::/*WriteOnly*//*Append*/ReadOnly))
        {
            str.append(" *** ОШИБКА 2: не удалось открыть выбранный файл");
            /*appendtoLogListBox*/do_send_result(str);
            return;
        }
        if(false==file_2.open(QIODevice::WriteOnly/*Append*//*ReadOnly*/))
        {
            str.append(" *** ОШИБКА 3: не удалось открыть выбранный файл");
            /*appendtoLogListBox*/do_send_result(str);
            return;
        }
        dstrm.setDevice(&file);
        dstrm_2.setDevice(&file_2);
        while(false==dstrm.atEnd())
        {
            data_quant=dstrm.readRawData(qba_local.data(),qba_local.size());
           // if(data_quant!=8)
            //    qDebug() << "data_quant != 8";
          // // file.seek(640);
            data_quant=dstrm_2.writeRawData(qba_local.constData(),qba_local.size()-640);
        }
        file.close();
        file_2.close();

        return;
    }

public slots:
    void readPendingDatagrams()
    {
        int size_of_data=0;
        qint64 ret_value=0;
        QByteArray buffer;
        QString str;
        while(socket->hasPendingDatagrams())
        {
            size_of_data=socket->pendingDatagramSize();
            if(-1==size_of_data) continue;
            buffer.resize(/*81920*/ /*64*1024*/ socket->pendingDatagramSize());
            QHostAddress sender;
            quint16 senderPort;
            ret_value=socket->readDatagram(buffer.data(),buffer.size(),&sender,&senderPort);
            if(-1==ret_value)
            {
                str=QString(tr("readDatagram failed"));
                qDebug() << str;
                do_send_result(str);
               // appendtoLogListBox(str);
                continue;
            }
            qDebug() << "Message from: " << sender;
            qDebug() << "Message port: " << senderPort;
            quint32 value; bool ok;
            value=sender.toIPv4Address(&ok);
            if(true==ok)
            {
                str=QString(tr("address is %1 port %2")).arg(value,0,16).arg(senderPort);
                //qDebug() << str;
               // do_send_result(str);
    //            appendtoLogListBox(str);
            }
            writeData(buffer);
            emit writeUdpData(buffer);
            //qDebug() << "emit writeUdpData(buffer)";
        }
    }

    void /*Window::*/setUdpData(const QByteArray& qba)
    {
        QString result;
        int block=0;
        //result=QString(tr("--- Window _ setUdpData slot"));
        //appendtoLogListBox(result);
     //    do_send_result(result);
        quint8 kadr_num=qba.at(3);
    quint16 str_num=MAKEWORD(qba.at(5),qba.at(4));
    quint16 package_num=MAKEWORD(qba.at(7),qba.at(6));
        // new commented at 06.03.2024
        //int data_quant=81920;
        int data_quant=/*1228800*/ 307200;
        //qDebug() << "receiveUdpData slot";
       // if(qba.size() %648 == 0)qDebug() << "setUdpData qba slot";
        if(qba[0]==0x01 && qba[1]==0 && qba[2]==0 )
        for(int i=0;i<640;i++)
        {
            block=/*i+*/8+color_shift;
            if(block<648)
            {
                another_qba[i_counter]=qba.at(/*i+8*/block);
                i_counter++;
                color_shift+=4;
            }

        }
        if(i_counter==data_quant)
        {
            color_shift=0;
           // wtc_worker.wakeOne();
     //        c_variable=false;
            prepare_generate_data();
            i_counter=0;
        }
        //    qDebug() << "i_counter" << i_counter;
        return;
    }
public:
    void prepare_generate_data()
    {
        QString str;
       // str.append(" --- slot button6Clicked() ");
       // appendtoLogListBox(str);
        QString filePath;
        QFile file;
        QDataStream dstrm;
        int n=0;
        int data_quant=0;
        std::vector<unsigned short> buffer_vect;
     //   QSize rct_size(320,256);
        QSize rct_size(640,480);

       // QPixmap myPixmap;

        QImage loc_img(rct_size,QImage::Format_RGB32 /*QImage::Format_RGBX8888*/);

        QRgb *line;

       // pixmap_vect.clear();

       // QBuffer hr;

        unsigned long/* long */ start_time=GetTickCount();
//        for(int k=0;k<100;k++)
//        for(int i=0;i<256;i++)
//        {
            for(int y=0;y<loc_img.height();y++)
            {
               // QRgb *line=reinterpret_cast<QRgb*>(loc_img.scanLine(y));
                line=static_cast<QRgb *>(static_cast<void *>(loc_img.scanLine(y)));
                for(int x=0;x<loc_img.width();x++)
                {
                    line[x]=qRgb(another_qba[n],another_qba[n],another_qba[n]);
                    n++;
                }
            }

           // pixmap_copy=QPixmap::fromImage( loc_img);
            if(b_counter==false)
            {
                b_counter=true;
            }
            else
            {

                //::Sleep(1);
            }
            ;
            emit sendData(loc_img/*pixmap_copy*/);
//        }

        unsigned long/* long */ end_time=GetTickCount();
        unsigned long/* long */ loc_delta=end_time-start_time;
//         str=QString(tr("  --- loc_delta = %1")).arg(loc_delta);
//         do_send_result(str);
       // loc_list.clear();
        return;
    }
signals:
    void resultReady(const QString &result);
    void sendData(const /*QPixmap*/QImage &image);
    void writeUdpData(const QByteArray & );
};

class Controller2 : public QObject
{
    Q_OBJECT
    QThread workerThread;
    Worker2 *worker;
   // UDPresource *resource;
public:
    Controller2() {
        /*Worker * */worker = new Worker2();
       // resource = new UDPresource();
        worker->moveToThread(&workerThread);
       // resource->moveToThread(&workerThread);
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
        connect(this, &Controller2::operate, worker, &Worker2::doWork);//main foo
        connect(worker, &Worker2::resultReady, this, &Controller2::handleResults);//print
        connect(worker, &Worker2::sendData, this, &Controller2::handlePicture);

       // connect(this,&Controller2::writeUdpPacket, resource,&UDPresource::writeToUdp);
        connect(this,&Controller2::sign_UdpData, worker, &Worker2::receiveUdpData,Qt::QueuedConnection);
       // connect(resource,&UDPresource::resultReady, this,&Controller2::handleResults);//print
       // // workerThread.start();
    }
    ~Controller2() {
        workerThread.quit();
        workerThread.wait();
    }
    void do_start(const QString &param )
    {
        workerThread.start();
       // worker->doWork(QString(tr(" --- Controller: (1) do_start")));
       // do_contrl_send_msg(QString(tr(" --- Controller: (2) do_start")));
        do_contrl_send_msg(param);
    }
    void do_contrl_send_msg(const QString &msg)
    {
        emit operate(msg);
    }
    void processWaitCond()
    {
        worker->wtc_worker.wakeOne();
    }

public slots:
    void handleResults(const QString &str1)
    {
        emit writeResults(str1);
    }
    void handlePicture(const QImage/*QPixmap*/ &image)
    {
        emit writePicture(image);
    }
    void handleUdpData(const QByteArray& qBinArray)
    {
        emit sign_UdpData(qBinArray);
        qDebug() << "handleUdpData, sign_UdpData(qBinArray) is emited";
    }

   // void handleWriteData(const QByteArray& qBinArray)
   // {
   //     emit writeUdpPacket(qBinArray);
   // }

signals:
    void operate(const QString &);
    void writeResults(const QString &);
    void writePicture(const QImage/*QPixmap*/ &image);
    void sign_UdpData(const QByteArray& data);
   // void writeUdpPacket(const QByteArray& qBinArray);
};

и
Window::Window()
{
    timer->start(10);
    QString str,str2,string3;
    str.append(" --- Worker");
    cntrl=new Controller();
    if(nullptr!=cntrl)
    {
        cntrl->do_start(str);
       // b_blocking_res_value=false;
    }
    cntrl_2=new Controller2();
    str2.append(" --- Worker2");
    if(nullptr!=cntrl_2)
    {
        cntrl_2->do_start(str2);
        string3=QString(tr("Controller2 is started"));
        qDebug() << string3;
       // b_blocking_res_value=false;
    }
    connect(this->cntrl,&Controller::writeResults,this,&Window::appendtoLogListBox);
   // connect(this->cntrl,&Controller::writePicture,this,&MainWindow::displayPixmap,Qt::QueuedConnection);
    //connect(button,&QPushButton::clicked,this,&Window::launch);
    connect(button, SIGNAL(clicked()), this, SLOT(launch()));
    connect(button_2, SIGNAL(clicked()), listWidget, SLOT(clear()));
    connect(this->cntrl,&Controller::writePicture,this,&Window::displayImage,Qt::QueuedConnection);

   // connect(this->cntrl_2,&Controller2::writeResults,this,&Window::appendtoLogListBox);
    connect(button_3,SIGNAL(clicked()),this,SLOT(processUdpData()));
    connect(button_4,SIGNAL(clicked()),this,SLOT(launchRead()));

    connect(this->cntrl_2,&Controller2::writeResults,this,&Window::appendtoLogListBox);//print
    connect(this->cntrl_2,&Controller2::writePicture,this,&Window::displayImage,Qt::QueuedConnection);
}
Re: Помогите с вопросом
От: SaZ  
Дата: 29.03.24 16:52
Оценка: 3 (1)
Здравствуйте, milkpot, Вы писали:

M>Здравствуйте, есть устройство, передающее в хост изображения размером 640Х480 пикселей (каждый пискель — 4 байта)

M>со скоростью 50 кадров в секунду по протоколу Udp. У пикселя один байт в оттенках серого, остальные три — это rgb.
M>Первая версия принимала пакеты в главном потоке, в результате чего замерзало окно приложения.
M>Перенос readPendingDatagrams в рабочую нить привел к тому, что приложение перестало принимать пакеты.
M>Wireshark видит, что пакеты приходят к хосту от устройства, а приложение пакеты не видит.
M>Вот фрагменты кода

M>...


Очень тяжело читать такой код (особенно явные сравнения с true/false). Можете убрать всё ненужное и просто сделать пересылку массива байт по udp? Отлаживать пробовали (убедиться что слоты вызываются и т.п.) отладчиком или хотя бы выводом в консоль? Зачем вам conditional variable?
Отредактировано 29.03.2024 16:53 SaZ . Предыдущая версия .
Re[2]: Помогите с вопросом
От: milkpot Россия  
Дата: 02.04.24 16:59
Оценка:
Здравствуйте, SaZ, Вы писали:

SaZ>Очень тяжело читать такой код (особенно явные сравнения с true/false). Можете убрать всё ненужное и просто сделать пересылку массива байт по udp? Отлаживать пробовали (убедиться что слоты вызываются и т.п.) отладчиком или хотя бы выводом в консоль? Зачем вам conditional variable?



Работает только посылка по UDP — функция writeToUdp. readPendingDatagrams не запускается (нет отладочных печатей). Она запускалась
только когда находилась в gui классе. Отладчиком не получается. У меня mingw отладчик в QtCreator.

Вывод в консоль QtCreator:
QVariant-value1 2000000
"bind returned 1"
true
PASS
QVariant-value1_ is 2000000
connect-readPendingDatagrams
connect-setUdpData
"Controller2 is started"
--- launchWrite: launch Worker: doWork func
--- launchWrite: launch Worker: doWork func
--- launchWrite: launch Worker: doWork func
--- launchWrite: launch Worker: doWork func
--- launchWrite: launch Worker: doWork func


Код
class Worker2 : public QObject
{
    Q_OBJECT
public:
    QMutex mutex;
    QWaitCondition wtc_worker;
   // QPixmap pixmap_copy;
    bool wait_cnd;
    bool b_counter;
    bool c_variable=true;
    int data_quant=81920;
    QByteArray another_qba;
    QUdpSocket *socket;
    quint16 package_num;
    quint16 str_num;
    int color_shift=0;
    int i_counter=0;
    explicit Worker2()
    {
        //i_counter=0;
        another_qba.resize(2100000/*1228800*/);
        socket=new QUdpSocket(this);
        bool result;
        QVariant value1;
        value1=QVariant(2000000/*100000*/);
        qDebug() << "QVariant-value1 " << value1.toInt();

        result=socket->bind(/*QHostAddress(QString(/*"192.168.0.1" "127.0.0.1"))*/ QHostAddress::AnyIPv4 /*LocalHost*/,/*61000*/ 50012);
        QString algostr;
        algostr=QString(tr("bind returned %1")).arg(result);
        qDebug() << algostr;
        qDebug() << result;
        if(result)
        {
            qDebug() << "PASS";
        }
        else
        {
            qDebug() << "FAIL";
            qDebug() << "error" << socket->error();
            qDebug() << socket->errorString().toLocal8Bit();
        }
        socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption,value1);
        value1=socket->socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption);
        qDebug() << "QVariant-value1_ is " << value1.toInt();
        connect(socket, &QUdpSocket::readyRead,this,&Worker2::readPendingDatagrams);
        qDebug() << "connect-readPendingDatagrams";
        connect(this,&Worker2::writeUdpData,this,&Worker2::setUdpData,Qt::QueuedConnection);
        qDebug() << "connect-setUdpData";
    }

    void writeToUdp(const QByteArray& qBinArray)
    {
        QByteArray Data;
        QHostAddress address;
       // quint16 port;
        quint16 port_large;
        qint64 retData=0;
        QString str;

//        address.setAddress(0xC0A800A0);// 0xC0 - 192, 0xA8 - 168, 0, A0 - 160
        //port=62000/*44755*/;

        port_large=/*62000*/ /*50011;*/ 50012;
        retData=socket->writeDatagram(qBinArray/*Data*/,/*address*/QHostAddress::LocalHost/*AnyIPv4*/, port_large);
        if(-1==retData)
        {
            qWarning() << "Unable to write data to " << /*address.toString()*/ QString("127.0.0.1")<< ":" << /*port*/port_large << endl;
            str=QString(tr("*** writeDatagram returned %1")).arg(retData);
            qDebug() << str;
            qDebug() << "write error " << socket->error();
            qDebug() << socket->errorString().toLocal8Bit();

            do_send_result(str);
        }
    }

public slots:
    void doWork(const QString &parameter) {
        QString result;
        bool booL_condition=true;
        b_counter=false;
        QByteArray byte_array;

        /* ... here is the expensive or blocking operation ... */
        while(booL_condition)
        {
            mutex.lock();
            wait_cnd=wtc_worker.wait(&mutex, 5400000/*one_and_a_half_hour_wait*/);
            if(!wait_cnd)
            {
                mutex.unlock();
                continue;
            }
            else if(wait_cnd)
            {
                byte_array.resize(648);
                for(int i=0;i<648;i++)
                {
                    byte_array[i]=i;
                }

                writeToUdp(byte_array);
            ;
                mutex.unlock();
            }

        }
    }

public slots:
    void readPendingDatagrams()
    {
        int size_of_data=0;
        qint64 ret_value=0;
        QByteArray buffer;
        QString str;
        while(socket->hasPendingDatagrams())
        {
            size_of_data=socket->pendingDatagramSize();
            if(-1==size_of_data) continue;
            buffer.resize(/*81920*/ /*64*1024*/ socket->pendingDatagramSize());
            QHostAddress sender;
            quint16 senderPort;
            ret_value=socket->readDatagram(buffer.data(),buffer.size(),&sender,&senderPort);
            if(-1==ret_value)
            {
                str=QString(tr("readDatagram failed"));
                qDebug() << str;
                do_send_result(str);
               // appendtoLogListBox(str);
                continue;
            }
            qDebug() << "Message from: " << sender;
            qDebug() << "Message port: " << senderPort;
            quint32 value; bool ok;
            value=sender.toIPv4Address(&ok);
            if(ok)
            {
                str=QString(tr("address is %1 port %2")).arg(value,0,16).arg(senderPort);
                //qDebug() << str;
               // do_send_result(str);
    //            appendtoLogListBox(str);
            }
            writeData(buffer);
            emit writeUdpData(buffer);
            //qDebug() << "emit writeUdpData(buffer)";
        }
    }

public:
    void writeData(const QByteArray& qba)
    {
        QString str;
        QString filename("binaryData.bin");
        QFile file;
        QDataStream dstrm;
        int data_quant=0;
        QByteArray qba_local;
        qba_local.resize(64);

        file.setFileName(filename);

        if( !file.open(QIODevice::/*WriteOnly*/Append))
        {
            str.append(" *** ОШИБКА: не удалось открыть выбранный файл");
            do_send_result(str);
            return;
        }
        dstrm.setDevice(&file);

        qDebug() << "- writeData(const QByteArray& qba)";
        //data_quant=dstrm.readRawData((char *)&buffer_vect[0],fileSize);
        data_quant=dstrm.writeRawData(qba.constData(),qba.size());

        file.close();

    }

signals:
    void resultReady(const QString &result);
    void sendData(const /*QPixmap*/QImage &image);
    void writeUdpData(const QByteArray & );
};


//-------------------------

class Controller2 : public QObject
{
    Q_OBJECT
    QThread workerThread;
    Worker2 *worker;
   // UDPresource *resource;
public:
    Controller2() {
        worker = new Worker2();
        worker->moveToThread(&workerThread);
        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
        connect(this, &Controller2::operate, worker, &Worker2::doWork);//main foo
        connect(worker, &Worker2::resultReady, this, &Controller2::handleResults);//print
        connect(worker, &Worker2::sendData, this, &Controller2::handlePicture);

       // connect(this,&Controller2::writeUdpPacket, resource,&UDPresource::writeToUdp);
        connect(this,&Controller2::sign_UdpData, worker, &Worker2::receiveUdpData,Qt::QueuedConnection);

    }
    ~Controller2() {
        workerThread.quit();
        workerThread.wait();
    }
    void do_start(const QString &param )
    {
        workerThread.start();
       // worker->doWork(QString(tr(" --- Controller: (1) do_start")));
       // do_contrl_send_msg(QString(tr(" --- Controller: (2) do_start")));
        do_contrl_send_msg(param);
    }
    void do_contrl_send_msg(const QString &msg)
    {
        emit operate(msg);
    }
    void processWaitCond()
    {
        worker->wtc_worker.wakeOne();
    }

public slots:
    void handleResults(const QString &str1)
    {
        emit writeResults(str1);
    }
    void handlePicture(const QImage/*QPixmap*/ &image)
    {
        emit writePicture(image);
    }
    void handleUdpData(const QByteArray& qBinArray)
    {
        emit sign_UdpData(qBinArray);
        qDebug() << "handleUdpData, sign_UdpData(qBinArray) is emited";
    }
signals:
    void operate(const QString &);
    void writeResults(const QString &);
    void writePicture(const QImage &image);
    void sign_UdpData(const QByteArray& data);
   // void writeUdpPacket(const QByteArray& qBinArray);
};

//------------------------------------

void Window::launchWrite()
{
    cntrl_2->processWaitCond();
    qDebug() << "--- launchWrite: launch Worker: doWork func";
    return;
}
Re[3]: Помогите с вопросом
От: Igore Россия  
Дата: 03.04.24 10:01
Оценка: 3 (1) +2
Здравствуйте, milkpot, Вы писали:

SaZ>>Очень тяжело читать такой код (особенно явные сравнения с true/false). Можете убрать всё ненужное и просто сделать пересылку массива байт по udp? Отлаживать пробовали (убедиться что слоты вызываются и т.п.) отладчиком или хотя бы выводом в консоль? Зачем вам conditional variable?


M>Работает только посылка по UDP — функция writeToUdp. readPendingDatagrams не запускается (нет отладочных печатей). Она запускалась

M>только когда находилась в gui классе. Отладчиком не получается. У меня mingw отладчик в QtCreator.

можешь реально сделать минимальный пример на основе http://rsdn.org/forum/cpp.qt/8133654.1
Автор: Igore
Дата: 16.11.21

Сейчас создание объектов в конструкторе вместе с connect которые происходят в основном потоке уже как то не очень выглядит.
        connect(socket, &QUdpSocket::readyRead,this,&Worker2::readPendingDatagrams);

без явного Qt::QueuedConnection
        connect(this,&Worker2::writeUdpData,this,&Worker2::setUdpData,Qt::QueuedConnection);

тут с явным Qt::QueuedConnection
Re[3]: Помогите с вопросом
От: SaZ  
Дата: 03.04.24 10:04
Оценка: 3 (1) +1
Здравствуйте, milkpot, Вы писали:

M>Здравствуйте, SaZ, Вы писали:


SaZ>>Очень тяжело читать такой код (особенно явные сравнения с true/false). Можете убрать всё ненужное и просто сделать пересылку массива байт по udp? Отлаживать пробовали (убедиться что слоты вызываются и т.п.) отладчиком или хотя бы выводом в консоль? Зачем вам conditional variable?



M>Работает только посылка по UDP — функция writeToUdp. readPendingDatagrams не запускается (нет отладочных печатей). Она запускалась

M>только когда находилась в gui классе. Отладчиком не получается. У меня mingw отладчик в QtCreator.

M>...


Подожду пока вы почините отладчик и всё-таки ответите на вопрос, касательно QWaitCondition — зачем он нужен? (есть чуйка, он не нужен). И покажите как вы потоки с воркерами стартуете?

А пока попробуйте покопать в следующую сторону: инстансы всяких стримов, включая QUdpSocket не нужно создавать в конструкторе воркера. А то получается что вы их создаёте в одном потоке, а потом начинаете использоват в другом. Он типа должно работать после moveToThread, но по факту, именно со стримами, есть платформозависимые нюансы. Вообще ничего не делайте в конструкторах воркеров.
Отредактировано 09.04.2024 11:26 SaZ . Предыдущая версия .
Re[4]: Помогите с вопросом
От: milkpot Россия  
Дата: 09.04.24 13:08
Оценка:
Здравствуйте, SaZ, Вы писали:



SaZ>Подожду пока вы почините отладчик и всё-таки ответите на вопрос, касательно QWaitCondition — зачем он нужен? (есть чуйка, он не нужен). И покажите как вы потоки с воркерами стартуете?


SaZ>А пока попробуйте покопать в следующую сторону: инстансы всяких стримов, включая QUdpSocket не нужно создавать в конструкторе воркера. А то получается что вы их создаёте в одном потоке, а потом начинаете использоват в другом. Он типа должно работать после moveToThread, но по факту, именно со стримами, есть платформозависимые нюансы. Вообще ничего не делайте в конструкторах воркеров.


QWaitCondition в функции doWork нужна была, потому что функция работала как аналог run'а.
Сейчас там нет QWaitCondition.

class Worker2 : public QObject
{
    Q_OBJECT
public:
    QMutex mutex;
    QWaitCondition wtc_worker;
   // QPixmap pixmap_copy;
    bool wait_cnd;
    bool b_counter;
    bool c_variable=true;
    int data_quant=81920;
    QByteArray another_qba;
    QUdpSocket *socket;
    quint16 package_num;
    quint16 str_num;
    int color_shift=0;
    int i_counter=0;
    void startStrm()
    {
        QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
    }
public slots:
    void doStart()
    {
        another_qba.resize(2100000/*1228800*/);
        socket=new QUdpSocket(this);
        bool result;
        QVariant value1;
        value1=QVariant(2100000/*100000*/);
        result=socket->bind( QHostAddress::AnyIPv4, 50012);
        socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption,value1);
        value1=socket->socketOption(QAbstractSocket::ReceiveBufferSizeSocketOption);
        connect(socket, &QUdpSocket::readyRead,this,&Worker2::readPendingDatagrams);
    }

public:
    void writeToUdp(const QByteArray& qBinArray)
    {
        quint16 port_large;
        qint64 retData=0;
        QString str;

        port_large= 50012;
        retData=socket->writeDatagram(qBinArray,QHostAddress::LocalHost, port_large);
    }
public slots:
    void doWork(const QString &parameter) {
        QString result;
        bool booL_condition=true;
        b_counter=false;
        QByteArray byte_array;
        QMutexLocker locker(&mutex);

        while(booL_condition)
        {
        }
    }
    void readPendingDatagrams()
    {
        int size_of_data=0;
        qint64 ret_value=0;
        QByteArray buffer;
        QString str;
        while(socket->hasPendingDatagrams())
        {
            size_of_data=socket->pendingDatagramSize();
            if(-1==size_of_data) continue;
            buffer.resize( socket->pendingDatagramSize());
            QHostAddress sender;
            quint16 senderPort;
            ret_value=socket->readDatagram(buffer.data(),buffer.size(),&sender,&senderPort);
            if(-1==ret_value)
            {
                str=QString(tr("readDatagram failed"));
                qDebug() << str;
                continue;
            }
            writeData(buffer);
            setUdpData(buffer);
        }
    }
public:
    void setUdpData(const QByteArray& qba)
    {
        QString result;
        int block=0;
        quint8 kadr_num=qba.at(3);
    quint16 str_num=MAKEWORD(qba.at(5),qba.at(4));
    quint16 package_num=MAKEWORD(qba.at(7),qba.at(6));
        int data_quant=307200;
        for(int i=0;i<640;i++)
        {
                another_qba[i_counter]=qba.at(i+8);
                i_counter++;

        }
        if(i_counter==data_quant)
        {
            prepare_generate_data();
            i_counter=0;
        }
        return;
    }

signals:
    void resultReady(const QString &result);
    void sendData(const QImage &image);
    void writeUdpData(const QByteArray & );
};

class Window : public QWidget
{
    Q_OBJECT
private:
    Worker2 worker;
    QThread m_Thread;
};

Старт рабочей нити

Window::Window()
{
    //---+
    worker.moveToThread( &m_Thread );
    connect(&m_Thread, &QThread::finished, &worker, &QObject::deleteLater);
    connect(&worker, &Worker2::resultReady, this, &Window::appendtoLogListBox);
    connect(&worker, &Worker2::sendData, this, &Window::displayImage);
    worker.startStrm();
    m_Thread.start();
    //----
}

Window::~Window()
{
   // m_Thread.wait();
    m_Thread.quit();
}


В выводе приложения QtCreator'а после закрытия приложения появляются записи:
QThread: Destroyed while thread is still running
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
Re[5]: Помогите с вопросом
От: SaZ  
Дата: 09.04.24 22:30
Оценка:
Здравствуйте, milkpot, Вы писали:

M>...


Даже не знаю что сказать. Честно — пример очень плохо читается. Вы из документации Qt пробовали примеры выполнять?
Re: Помогите с вопросом
От: Pzz Россия https://github.com/alexpevzner
Дата: 10.04.24 07:40
Оценка: 5 (2) +2
Здравствуйте, milkpot, Вы писали:

M>Здравствуйте, есть устройство, передающее в хост изображения размером 640Х480 пикселей (каждый пискель — 4 байта)

M>со скоростью 50 кадров в секунду по протоколу Udp. У пикселя один байт в оттенках серого, остальные три — это rgb.

640*480*4*50*8/1e6 = 491.52

Гм. Полгигабита в секунду для передачи цветной картинки с разрешением VGA — это как-то не очень нормально ИМХО.

Надо увеличить размер приемного буфера до максимума и приоритет принимающего потока до максимума (и при этом он, если зациклится, будет вымораживать всю систему — значит, он не должен делать тяжелую работу, а должен только принимать и складывать; его задача — успевать выгребать приемные буфера UDP-сокета, они в венде не такие уж большие даже если выставлены максимальны возможные значения).

И надо как-то организовать компрессию этого видеопотока. Стримить на скорости полгигабита — это (1) очень серьезная (2) совершенно в данном случае не нужная задача.
Re[5]: Помогите с вопросом
От: Igore Россия  
Дата: 13.04.24 16:19
Оценка: 2 (1)
Здравствуйте, milkpot, Вы писали:

M>QWaitCondition в функции doWork нужна была, потому что функция работала как аналог run'а.

M>Сейчас там нет QWaitCondition.

M>
M>class Worker2 : public QObject
M>{
M>    Q_OBJECT
M>public:
M>    QMutex mutex;
M>    QWaitCondition wtc_worker;
M>   // QPixmap pixmap_copy;
M>    bool wait_cnd;
M>    bool b_counter;
M>    bool c_variable=true;
M>    int data_quant=81920;
M>    quint16 package_num;
M>    quint16 str_num;
M>    int color_shift=0;
//Я надеюсь это от уменьшения примера, так то все эти переменные можно удалить

M>    void doStart()
//Тут код в 2 раза можно сократить

M>    void writeToUdp(const QByteArray& qBinArray)
//Я бы по разным классам сделал читателя и писателя. Или писатель тоже в отдельном потоке должен быть?

M>signals:
M>    void sendData(const QImage &image);

M>Window::Window()
M>{
M>    //---+
M>    connect(&worker, &Worker2::sendData, this, &Window::displayImage);
/*Я надеюсь там сохранение QImage в классе и вызов update
void Window::displayImage(const QImage &image)
{
    m_Image = image;
    update();
}
*/

M>Window::~Window()
M>{
M>   // m_Thread.wait();
M>    m_Thread.quit();
M>}
M>


M>В выводе приложения QtCreator'а после закрытия приложения появляются записи:

M>QThread: Destroyed while thread is still running
вызван m_Thread.quit();, но нет ожидания его завершения(waitForFinished), потом начинает работать ~Window и m_Thread просто уничтожается во время работы
M>QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
когда происходит m_Thread.quit(); начинается завершение потока, finished это уже потока нет, а в нем создан socket, который нужно удалить в том же потоке где он был создан

Ну в примере же по другому
m_Receiver.stop(); // Ставим в очередь потока вызов doStop
m_Thread.quit();   // Завершаем поток
m_Thread.waitForFinished(); // Ждем когда отработает из очереди doStop и нормально завершим внутреннее состояние
Отредактировано 13.04.2024 16:23 Igore . Предыдущая версия .
Re: Помогите с вопросом
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 13.04.24 16:42
Оценка: :)
Здравствуйте, milkpot, Вы писали:

M>Первая версия принимала пакеты в главном потоке, в результате чего замерзало окно приложения.


Видимо, ты в блокирующем режиме читал данные?


M>Перенос readPendingDatagrams в рабочую нить привел к тому, что приложение перестало принимать пакеты.


Тут могут быть какие-то особенности архитектуры Qt. Я как-то из COM-порта пытался читать средствами Qt, нихрена не читалось, оказалось, что нужен цикл обработки сообщений, хотя, казалось бы, причем тут COM-порт в консольной программе. Пришлось что-то руками дёргать. В основном потоке, где у тебя окно, такой цикл есть, а вот в воркере его вероятно нет, и вероятно, что сокету это нужно тоже, как и COM-порту


M>Wireshark видит, что пакеты приходят к хосту от устройства, а приложение пакеты не видит.

M>Вот фрагменты кода

Спасибо, не надо
Маньяк Робокряк колесит по городу
Отредактировано 16.04.2024 21:47 Marty . Предыдущая версия .
Re[2]: Помогите с вопросом
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 13.04.24 16:48
Оценка: 3 (1)
Здравствуйте, Pzz, Вы писали:

M>>Здравствуйте, есть устройство, передающее в хост изображения размером 640Х480 пикселей (каждый пискель — 4 байта)

M>>со скоростью 50 кадров в секунду по протоколу Udp. У пикселя один байт в оттенках серого, остальные три — это rgb.

Pzz>640*480*4*50*8/1e6 = 491.52


Pzz>Гм. Полгигабита в секунду для передачи цветной картинки с разрешением VGA — это как-то не очень нормально ИМХО.


Как я понимаю, это довольно стандартная практика. В НИИ, где я работал, коллега из другого отдела делал СТЗ, и с камерой так же по UDP общался. И формат ответа камеры именно что тоже несжатые пиксели были. У него была проблема, что он делал кучу запросов для получения кадра (там по частям надо было запрашивать, целиком нельзя было), и хотел получить потом чтением полную картинку, а у него большая часть картинки куда-то пропадала. Я предположил, что надо запрашивать по мере прихода очередной части, или по таймауту, если часть не пришла — он переделал, и заработало — я на такое в молодости напарывался, UDP любит отбрасывать пакеты, которые не влезают во всякие его буфера, что на маршрутизаторах, что на конечных хостах
Маньяк Робокряк колесит по городу
Re[3]: Помогите с вопросом
От: Pzz Россия https://github.com/alexpevzner
Дата: 13.04.24 18:19
Оценка: 3 (1)
Здравствуйте, Marty, Вы писали:

Pzz>>Гм. Полгигабита в секунду для передачи цветной картинки с разрешением VGA — это как-то не очень нормально ИМХО.


M>Как я понимаю, это довольно стандартная практика. В НИИ, где я работал, коллега из другого отдела делал СТЗ, и с камерой так же по UDP общался. И формат ответа камеры именно что тоже несжатые пиксели были.


UDP — это нормально для этого случая. Пакеты иногда теряются, и лучше уж, как в UDP, допустить потерю видеокадра, чем как в TCP, застревать иногда на несколько секунд, занимаясь бессмысленными уже (потому, что фатально запоздалыми) ретрансмитами. И, конечно, надо осмысленно подходить к потере пакетов — делать какой-то ограниченный по времени ретрансмит, возможно, FEH (forward error correction).

А вот гнать на такой скорости некомпрессованный видеопоток. Ну, это только в НИИ так делают.

M>У него была проблема, что он делал кучу запросов для получения кадра (там по частям надо было запрашивать, целиком нельзя было), и хотел получить потом чтением полную картинку, а у него большая часть картинки куда-то пропадала. Я предположил, что надо запрашивать по мере прихода очередной части, или по таймауту, если часть не пришла — он переделал, и заработало — я на такое в молодости напарывался, UDP любит отбрасывать пакеты, которые не влезают во всякие его буфера, что на маршрутизаторах, что на конечных хостах


Я, вообще-то, эта. Сетевик-затейник. И медиастримингом тоже нормально занимался. Я даже умудрился попасть в соавторы американского патента на эту тему
Re[4]: Помогите с вопросом
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 13.04.24 23:26
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>А вот гнать на такой скорости некомпрессованный видеопоток. Ну, это только в НИИ так делают.


У нас в НИИ ничего не переизобретали, у нас в только пользовались существующим от ведущих производителей. Но если ты такой тугой, то флаг тебе в руки и барабан на шею
Маньяк Робокряк колесит по городу
Re[5]: Помогите с вопросом
От: Pzz Россия https://github.com/alexpevzner
Дата: 14.04.24 06:59
Оценка:
Здравствуйте, Marty, Вы писали:

Pzz>>А вот гнать на такой скорости некомпрессованный видеопоток. Ну, это только в НИИ так делают.


M>У нас в НИИ ничего не переизобретали, у нас в только пользовались существующим от ведущих производителей. Но если ты такой тугой, то флаг тебе в руки и барабан на шею


Чего?
Re: Помогите с вопросом
От: BSOD  
Дата: 17.04.24 17:24
Оценка:
Здравствуйте, milkpot, Вы писали:

M>Здравствуйте, есть устройство, передающее в хост изображения размером 640Х480 пикселей (каждый пискель — 4 байта)

M>со скоростью 50 кадров в секунду по протоколу Udp.

Похоже на изобретение велосипеда. Готовых решений — вагон и маленькая тележка. Часть из них опенсорсные.
VNC, RDP и пр. Со сжатием-разжатием картинки на лету.
Sine vilitate, sine malitiosa mente
Re[6]: Помогите с вопросом
От: milkpot Россия  
Дата: 18.04.24 16:45
Оценка:
Здравствуйте, Igore, Вы писали:



M>>В выводе приложения QtCreator'а после закрытия приложения появляются записи:

M>>QThread: Destroyed while thread is still running
I>вызван m_Thread.quit();, но нет ожидания его завершения(waitForFinished), потом начинает работать ~Window и m_Thread просто уничтожается во время работы
M>>QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
I>когда происходит m_Thread.quit(); начинается завершение потока, finished это уже потока нет, а в нем создан socket, который нужно удалить в том же потоке где он был создан

I>Ну в примере же по другому

I>
I>m_Receiver.stop(); // Ставим в очередь потока вызов doStop
I>m_Thread.quit();   // Завершаем поток
I>m_Thread.waitForFinished(); // Ждем когда отработает из очереди doStop и нормально завершим внутреннее состояние
I>


//----
class Window : public QWidget
{
    Q_OBJECT
private:
    Worker2 worker;
    QThread m_Thread;
    Worker3 worker3;
    QThread m_Thread3;
};

Window::Window()
{

    //---+
    worker.moveToThread( &m_Thread );
    connect(&m_Thread, &QThread::finished, &worker, &QObject::deleteLater);
    connect(&worker, &Worker2::resultReady, this, &Window::appendtoLogListBox);
    connect(&worker, &Worker2::sendData, this, &Window::displayImage);
    worker.startStrm();
    m_Thread.start(QThread::TimeCriticalPriority);
    //----
    //---+
    worker3.moveToThread( &m_Thread3 );
    connect(&m_Thread3, &QThread::finished, &worker3, &QObject::deleteLater);
    //connect(&worker3, &Worker3::resultReady, this, &Window::appendtoLogListBox);
    worker3.startStrm3();
    m_Thread3.start(QThread::HighPriority);
    //----
}
Window::~Window()
{
    worker.stopStrm();
    m_Thread.quit();
    b_result=m_Thread.wait(200);
    qDebug() << "m_Thread.wait(200) = " << b_result;// Не печатается
    //--- Если Worker3 нет, то печатается

    worker3.stopStrm3();
    m_Thread3.quit();
    b_result=m_Thread3.wait(200);
    qDebug() << "m_Thread3.wait(200) = " << b_result;// Не печатается
}


void Window::displayImage(const QImage& img)
{
    img1=img;
    helper.img2=img1;
    update();
    return;
}

void Window::launchWrite()
{
    QByteArray byte_array;
    byte_array.resize(648);
    for(int j=0;j<480;j++)
    {
        for(int i=0;i<648;i++)
        {
            byte_array[i]=i;
        }
//        worker.writeToUdp(byte_array);
        worker3.writeToUdp3(byte_array);// Выводится сообщение в консоль QtCreator:
//QObject: Cannot create children for a parent that is in a different thread.
//(Parent is QUdpSocket(0x1e03938), parent's thread is QThread(0x61fdc4), current thread is QThread(0x1692d80)
//Или надо делать вызов через signal-slot? 
//Если убрать Worker3 и реализовать через Worker2, то сообщение не появляется
//----
    }
    return;
}

//-------------------------------------
class Worker2 : public QObject
{
    Q_OBJECT
private:
    QScopedPointer<QUdpSocket> m_pSocket;
public:
    void startStrm()
    {
        QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
    }
    void stopStrm()
    {
        QMetaObject::invokeMethod(this, "doStop", Qt::QueuedConnection);
    }
public slots:
    void doStart()
    {
        m_pSocket.reset( new QUdpSocket(this) );
        result=m_pSocket->bind( QHostAddress::AnyIPv4 , 50012);
        connect(m_pSocket.data()/*socket*/, &QUdpSocket::readyRead,this,&Worker2::readPendingDatagrams);
    }
    void doStop()
    {
        disconnect(m_pSocket.data()/*socket*/, &QUdpSocket::readyRead,this,&Worker2::readPendingDatagrams);
        m_pSocket.reset();
    }

public:
    void writeToUdp(const QByteArray& qBinArray)
    {
        port_large=50012;
        retData=m_pSocket->writeDatagram(qBinArray,QHostAddress::LocalHost, port_large);
    }
    void readPendingDatagrams()
    {
        QByteArray buffer;
        while(m_pSocket->hasPendingDatagrams())
        {
            size_of_data=m_pSocket->pendingDatagramSize();
            if(-1==size_of_data) continue;
            buffer.resize(m_pSocket->pendingDatagramSize());
            ret_value=m_pSocket->readDatagram(buffer.data(),buffer.size(),&sender,&senderPort);
            writeData(buffer);
            setUdpData(buffer);
        }
    }
signals:
    void resultReady(const QString &result);
    void sendData(const QImage &image);
    void writeUdpData(const QByteArray & );
};
//--- Вводим класс Worker3, чтобы производить запись датаграмм в сокет
class Worker3 : public QObject
{
    Q_OBJECT
private:
    QScopedPointer<QUdpSocket> m_pSocket3;
public:
    void startStrm3()
    {
        QMetaObject::invokeMethod(this, "doStart3", Qt::QueuedConnection);
    }
    void stopStrm3()
    {
        QMetaObject::invokeMethod(this, "doStop3", Qt::QueuedConnection);
    }
public slots:
    void doStart3()
    {
        m_pSocket3.reset( new QUdpSocket(this) );
    }
    void doStop3()
    {
        m_pSocket3.reset();
    }
public:
    void writeToUdp3(const QByteArray& qBinArray)
    {
        port_large= 50012;
        retData=/*socket*/m_pSocket3->writeDatagram(qBinArray,QHostAddress::LocalHost, port_large);
    }
};
Re[7]: Помогите с вопросом
От: Igore Россия  
Дата: 19.04.24 06:26
Оценка: 3 (1) +1
Здравствуйте, milkpot, Вы писали:

Не понимание времени жизни объектов
1) Если нет new не нужно deleteLater, ну или delete, это двойное удаление, то что на стэке само удалится
2) Если Qt объекту передали родителя, то при уничтожении родителя он удаляет своих детей. Поэтому тут тоже двойное удаление
m_pSocket.reset( new QUdpSocket(this) );

а) Удаляется по this
б) Удаляется при уничтожении QScopedPointer


M>//----

M>
M>private:
M>    Worker2 worker;
M>    QThread m_Thread;
M>    Worker3 worker3;
M>    QThread m_Thread3;
M>};

M>Window::Window()
M>{
M>    connect(&m_Thread, &QThread::finished, &worker, &QObject::deleteLater);
M>    connect(&m_Thread3, &QThread::finished, &worker3, &QObject::deleteLater);
// не нужно так как на стэке worker, m_Thread, worker3, m_Thread3 удалятся сами после деструктора ~Window 
M>}
M>Window::~Window()
M>{
M>    worker.stopStrm();
M>    m_Thread.quit();
M>    b_result=m_Thread.wait(200);
//При waitForFinished зависает?
M>}

M>void Window::displayImage(const QImage& img)
M>{
M>    helper.img2=img;
M>    update();
M>}

M>void Window::launchWrite()
M>{
M>    QByteArray byte_array;
M>        worker3.writeToUdp3(byte_array);// Выводится сообщение в консоль QtCreator:
M>//QObject: Cannot create children for a parent that is in a different thread.
M>//(Parent is QUdpSocket(0x1e03938), parent's thread is QThread(0x61fdc4), current thread is QThread(0x1692d80)
M>//Или надо делать вызов через signal-slot? 
M>//Если убрать Worker3 и реализовать через Worker2, то сообщение не появляется
M>


worker3.writeToUdp3 это вызов метода напрямую из UI потока, при этом и worker3 и m_pSocket3 в другом потоке/
Поэтому
вариант 1: убирай поток,
вариант 2: делай signal из Window и connect на Worker
class Window
signal:
    void writeToUdp(QByteArray)

Window::Window() {
    connect(this, &Window::writeToUdp, &worker2, &Worker2::writeToUdp3);

void Window::launchWrite() {
    //...
    emit writeToUdp(byte_array);
}

class Worker3 : public QObject {
public slots:
    void writeToUdp3(const QByteArray& qBinArray)

вариант 3:
через очередь потока

class Worker3 : public QObject {
public:
    void writeToUdp3(const QByteArray& qBinArray)
    {
// не помню как аргументы передавать, псевдокод
        QMetaObject::invokeMethod(this, "doWriteToUdp3", Q_ARG(qBinArray), Qt::QueuedConnection);
    }
public:
    void doWriteToUdp3(const QByteArray& qBinArray)


M>
M>class Worker2 : public QObject
M>{
M>    QScopedPointer<QUdpSocket> m_pSocket;
M>    void doStart()
M>    {
M>        m_pSocket.reset( new QUdpSocket(this) );
//Не нужно this передавать

M>//--- Вводим класс Worker3, чтобы производить запись датаграмм в сокет
M>class Worker3 : public QObject
M>{
M>public slots:
M>    void doStart3()
M>    {
M>        m_pSocket3.reset( new QUdpSocket(this) );
//Не нужно this передавать
M>    }
M>
Re[8]: Помогите с вопросом
От: milkpot Россия  
Дата: 27.04.24 12:30
Оценка:
Здравствуйте, Igore, Вы писали:

I>вариант 2: делай signal из Window и connect на Worker

I>
I>class Window
I>signal:
I>    void writeToUdp(QByteArray)

I>Window::Window() {
I>    connect(this, &Window::writeToUdp, &worker2, &Worker2::writeToUdp3);

I>void Window::launchWrite() {
I>    //...
I>    emit writeToUdp(byte_array);
I>}

I>class Worker3 : public QObject {
I>public slots:
I>    void writeToUdp3(const QByteArray& qBinArray)


Я выбрал второй вариант.
При подаче питания на устройство на текущий момент передаются кадры (изображения) на хост. Если приложение закрыть в момент передачи
кадров, то в окне вывода приложения QtCreator'а появятся сообщения

m_Thread.wait(200) = false
m_Thread3.wait(200) = true
QThread: Destroyed while thread is still running
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
QSocketNotifier: Multiple socket notifiers for same socket 1032 and type Read
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
QSocketNotifier: Multiple socket notifiers for same socket 1032 and type Read
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
QUdpSocket::hasPendingDatagrams() called on a QUdpSocket when not in QUdpSocket::BoundState

Если сначала отключить устройство, то в окно вывода приложения выводятся только
m_Thread.wait(200) = true
m_Thread3.wait(200) = true

class Window : public QWidget
{
    Q_OBJECT
signals:
    void writeCommandData(const QByteArray &);// второй вариант
private:
    Worker2 worker;
    QThread m_Thread;
    Worker3 worker3;
    QThread m_Thread3;
};
Window::Window()
{
    worker.moveToThread( &m_Thread );
    connect(&worker, &Worker2::resultReady, this, &Window::appendtoLogListBox);
    connect(&worker, &Worker2::sendData, this, &Window::displayImage);
    worker.startStrm();
    m_Thread.start(QThread::NormalPriority);

    worker3.moveToThread( &m_Thread3 );
    connect(this,&Window::writeCommandData,&worker3,&Worker3::writeToUdp3);
    worker3.startStrm3();
    m_Thread3.start(QThread::NormalPriority);
}
Window::~Window()
{
    bool b_result=false;
    worker.stopStrm();
    m_Thread.quit();
    b_result=m_Thread.wait(200);
    qDebug() << "m_Thread.wait(200) = " << b_result;
    worker3.stopStrm3();
    m_Thread3.quit();
    b_result=m_Thread3.wait(200);
    qDebug() << "m_Thread3.wait(200) = " << b_result;
// waitForFinished нет в QThread (Qt 5.6.2),а есть в QFuture
}

void Window::launchWrite()
{
    QByteArray byte_array;
    byte_array.resize(648);
    for(int j=0;j<480;j++)
    {
        for(int i=0;i<648;i++)
        {
            byte_array[i]=i;
        }
        emit writeCommandData(byte_array);
    }
    return;
}


class Worker2 : public QObject
{
    Q_OBJECT
private:
    QScopedPointer<QUdpSocket> m_pSocket;
public:
    void startStrm()
    {
        QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection);
    }
    void stopStrm()
    {
        QMetaObject::invokeMethod(this, "doStop", Qt::QueuedConnection);
    }
public slots:
    void doStart()
    {
        m_pSocket.reset( new QUdpSocket() );
        result=m_pSocket->bind( QHostAddress::AnyIPv4 , 50012);
        connect(m_pSocket.data(), &QUdpSocket::readyRead,this,&Worker2::readPendingDatagrams);
    }
    void doStop()
    {
        disconnect(m_pSocket.data(), &QUdpSocket::readyRead,this,&Worker2::readPendingDatagrams);
        m_pSocket.reset();
    }

public:
    void readPendingDatagrams()
    {
        QByteArray buffer;
        while(m_pSocket->hasPendingDatagrams())
        {
            size_of_data=m_pSocket->pendingDatagramSize();
            if(-1==size_of_data) continue;
            buffer.resize(m_pSocket->pendingDatagramSize());
            ret_value=m_pSocket->readDatagram(buffer.data(),buffer.size(),&sender,&senderPort);
            setUdpData(buffer);
        }
    }
signals:
    void resultReady(const QString &result);
    void sendData(const QImage &image);
};


class Worker3 : public QObject
{
    Q_OBJECT
private:
    QScopedPointer<QUdpSocket> m_pSocket3;
public:
    void startStrm3()
    {
        QMetaObject::invokeMethod(this, "doStart3", Qt::QueuedConnection);
    }
    void stopStrm3()
    {
        QMetaObject::invokeMethod(this, "doStop3", Qt::QueuedConnection);
    }
public slots:
    void doStart3()
    {
        m_pSocket3.reset( new QUdpSocket() );
    }
    void doStop3()
    {
        m_pSocket3.reset();
    }
    void writeToUdp3(const QByteArray& qBinArray)
    {
        port_large= 50012;
        retData=m_pSocket3->writeDatagram(qBinArray,QHostAddress::LocalHost, port_large);
    }
};
Re[9]: Помогите с вопросом
От: Igore Россия  
Дата: 28.04.24 10:33
Оценка: 4 (2)
Здравствуйте, milkpot, Вы писали:

M>m_Thread.wait(200) = false

M>m_Thread3.wait(200) = true
А это требования что при закрытии есть только 200мс на остановку?
M>QThread: Destroyed while thread is still running
M>QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
M>QSocketNotifier: Multiple socket notifiers for same socket 1032 and type Read
M>QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
M>QSocketNotifier: Multiple socket notifiers for same socket 1032 and type Read
M>QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
M>QUdpSocket::hasPendingDatagrams() called on a QUdpSocket when not in QUdpSocket::BoundState
Собственно все тоже самое что до этого, за 200мс не успели остановиться, после начинается уничтожение объектов в том состоянии в котором они находятся(поток работает, сокет в этом работающем потоке), ну и вот такие ошибки

M>Если сначала отключить устройство, то в окно вывода приложения выводятся только

M>m_Thread.wait(200) = true
M>m_Thread3.wait(200) = true
Тут успели остановиться за 200мс всё хорошо.

Ну если это прям требования, что так мало времени есть на остановку, то вводи дополнительно ручную синхронизацию.
Сейчас ситуация, в очереди потока есть слоты которые должны отработать, последовательно, и дойти очередь до слота остановки(или readPendingDatagrams просто не заканчивается так как слишком много данных шлется). Обработку при остановке надо ускорить. Грубо
class Worker2 : public QObject {
///...
public:
    void stopStrm()
    {
        mStop = true;
        QMetaObject::invokeMethod(this, "doStop", Qt::QueuedConnection);
    }

    void readPendingDatagrams()
    {
        QByteArray buffer;
        while(m_pSocket->hasPendingDatagrams() && !mStop)
        ///...
    }
private:
   std::atomic<bool> mStop = {false};
}

Ну или увеличить время ожидания остановки 200 как то маловато, если waitForFinished то до бесконечности.
Отредактировано 01.05.2024 7:30 Igore . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.