mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-11-03 21:20:31 -05:00 
			
		
		
		
	
		
			
	
	
		
			322 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			322 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
								 | 
							
								///////////////////////////////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								// Copyright (C) 2020 Jon Beniston, M7RCE                                        //
							 | 
						||
| 
								 | 
							
								// Copyright (C) 2020 Edouard Griffiths, F4EXB                                   //
							 | 
						||
| 
								 | 
							
								//                                                                               //
							 | 
						||
| 
								 | 
							
								// This program is free software; you can redistribute it and/or modify          //
							 | 
						||
| 
								 | 
							
								// it under the terms of the GNU General Public License as published by          //
							 | 
						||
| 
								 | 
							
								// the Free Software Foundation as version 3 of the License, or                  //
							 | 
						||
| 
								 | 
							
								// (at your option) any later version.                                           //
							 | 
						||
| 
								 | 
							
								//                                                                               //
							 | 
						||
| 
								 | 
							
								// This program is distributed in the hope that it will be useful,               //
							 | 
						||
| 
								 | 
							
								// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
							 | 
						||
| 
								 | 
							
								// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
							 | 
						||
| 
								 | 
							
								// GNU General Public License V3 for more details.                               //
							 | 
						||
| 
								 | 
							
								//                                                                               //
							 | 
						||
| 
								 | 
							
								// You should have received a copy of the GNU General Public License             //
							 | 
						||
| 
								 | 
							
								// along with this program. If not, see <http://www.gnu.org/licenses/>.          //
							 | 
						||
| 
								 | 
							
								///////////////////////////////////////////////////////////////////////////////////
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <algorithm>
							 | 
						||
| 
								 | 
							
								#include <random>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <QDebug>
							 | 
						||
| 
								 | 
							
								#include <QTcpServer>
							 | 
						||
| 
								 | 
							
								#include <QTcpSocket>
							 | 
						||
| 
								 | 
							
								#include <QNetworkDatagram>
							 | 
						||
| 
								 | 
							
								#include <QEventLoop>
							 | 
						||
| 
								 | 
							
								#include <QTimer>
							 | 
						||
| 
								 | 
							
								#include <QRegExp>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "util/ax25.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "pertester.h"
							 | 
						||
| 
								 | 
							
								#include "pertesterworker.h"
							 | 
						||
| 
								 | 
							
								#include "pertesterreport.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								MESSAGE_CLASS_DEFINITION(PERTesterWorker::MsgConfigurePERTesterWorker, Message)
							 | 
						||
| 
								 | 
							
								MESSAGE_CLASS_DEFINITION(PERTesterReport::MsgReportStats, Message)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								PERTesterWorker::PERTesterWorker() :
							 | 
						||
| 
								 | 
							
								    m_msgQueueToFeature(nullptr),
							 | 
						||
| 
								 | 
							
								    m_msgQueueToGUI(nullptr),
							 | 
						||
| 
								 | 
							
								    m_running(false),
							 | 
						||
| 
								 | 
							
								    m_mutex(QMutex::Recursive),
							 | 
						||
| 
								 | 
							
								    m_rxUDPSocket(nullptr),
							 | 
						||
| 
								 | 
							
								    m_tx(0),
							 | 
						||
| 
								 | 
							
								    m_rxMatched(0),
							 | 
						||
| 
								 | 
							
								    m_rxUnmatched(0)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								PERTesterWorker::~PERTesterWorker()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    closeUDP();
							 | 
						||
| 
								 | 
							
								    disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
							 | 
						||
| 
								 | 
							
								    m_inputMessageQueue.clear();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::reset()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    QMutexLocker mutexLocker(&m_mutex);
							 | 
						||
| 
								 | 
							
								    m_inputMessageQueue.clear();
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool PERTesterWorker::startWork()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    qDebug() << "PERTesterWorker::startWork";
							 | 
						||
| 
								 | 
							
								    QMutexLocker mutexLocker(&m_mutex);
							 | 
						||
| 
								 | 
							
								    openUDP(m_settings);
							 | 
						||
| 
								 | 
							
								    // Automatically restart if previous run had finished, otherwise continue
							 | 
						||
| 
								 | 
							
								    if (m_tx >= m_settings.m_packetCount)
							 | 
						||
| 
								 | 
							
								        resetStats();
							 | 
						||
| 
								 | 
							
								    connect(&m_txTimer, SIGNAL(timeout()), this, SLOT(tx()));
							 | 
						||
| 
								 | 
							
								    m_txTimer.start(m_settings.m_interval * 1000.0);
							 | 
						||
| 
								 | 
							
								    m_running = true;
							 | 
						||
| 
								 | 
							
								    return m_running;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::stopWork()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    QMutexLocker mutexLocker(&m_mutex);
							 | 
						||
| 
								 | 
							
								    m_txTimer.stop();
							 | 
						||
| 
								 | 
							
								    closeUDP();
							 | 
						||
| 
								 | 
							
								    disconnect(&m_txTimer, SIGNAL(timeout()), this, SLOT(tx()));
							 | 
						||
| 
								 | 
							
								    m_running = false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::handleInputMessages()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    Message* message;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while ((message = m_inputMessageQueue.pop()) != nullptr)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (handleMessage(*message)) {
							 | 
						||
| 
								 | 
							
								            delete message;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::resetStats()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    m_tx = 0;
							 | 
						||
| 
								 | 
							
								    m_rxMatched = 0;
							 | 
						||
| 
								 | 
							
								    m_rxUnmatched = 0;
							 | 
						||
| 
								 | 
							
								    if (getMessageQueueToGUI())
							 | 
						||
| 
								 | 
							
								        getMessageQueueToGUI()->push(PERTesterReport::MsgReportStats::create(m_tx, m_rxMatched, m_rxUnmatched));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								bool PERTesterWorker::handleMessage(const Message& cmd)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (MsgConfigurePERTesterWorker::match(cmd))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        QMutexLocker mutexLocker(&m_mutex);
							 | 
						||
| 
								 | 
							
								        MsgConfigurePERTesterWorker& cfg = (MsgConfigurePERTesterWorker&) cmd;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        applySettings(cfg.getSettings(), cfg.getForce());
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else if (PERTester::MsgResetStats::match(cmd))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        resetStats();
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return false;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::applySettings(const PERTesterSettings& settings, bool force)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    qDebug() << "PERTesterWorker::applySettings:"
							 | 
						||
| 
								 | 
							
								            << " force: " << force;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (   (settings.m_rxUDPAddress != m_settings.m_rxUDPAddress)
							 | 
						||
| 
								 | 
							
								        || (settings.m_rxUDPPort != m_settings.m_rxUDPPort)
							 | 
						||
| 
								 | 
							
								        || force)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        openUDP(settings);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ((settings.m_interval != m_settings.m_interval) || force)
							 | 
						||
| 
								 | 
							
								        m_txTimer.setInterval(settings.m_interval * 1000.0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    m_settings = settings;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::openUDP(const PERTesterSettings& settings)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    closeUDP();
							 | 
						||
| 
								 | 
							
								    m_rxUDPSocket = new QUdpSocket();
							 | 
						||
| 
								 | 
							
								    if (!m_rxUDPSocket->bind(QHostAddress(settings.m_rxUDPAddress), settings.m_rxUDPPort))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        qCritical() << "PERTesterWorker::openUDP: Failed to bind to port " << settings.m_rxUDPAddress << ":" << settings.m_rxUDPPort << ". Error: " << m_rxUDPSocket->error();
							 | 
						||
| 
								 | 
							
								        if (m_msgQueueToFeature)
							 | 
						||
| 
								 | 
							
								            m_msgQueueToFeature->push(PERTester::MsgReportWorker::create(QString("Failed to bind to port %1:%2 - %3").arg(settings.m_rxUDPAddress).arg(settings.m_rxUDPPort).arg(m_rxUDPSocket->error())));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    else
							 | 
						||
| 
								 | 
							
								        qDebug() << "PERTesterWorker::openUDP: Listening on port " << settings.m_rxUDPAddress << ":" << settings.m_rxUDPPort << ".";
							 | 
						||
| 
								 | 
							
								    connect(m_rxUDPSocket, &QUdpSocket::readyRead, this, &PERTesterWorker::rx);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::closeUDP()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    if (m_rxUDPSocket != nullptr)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        qDebug() << "PERTesterWorker::closeUDP: Closing port " << m_settings.m_rxUDPAddress << ":" << m_settings.m_rxUDPPort << ".";
							 | 
						||
| 
								 | 
							
								        disconnect(m_rxUDPSocket, &QUdpSocket::readyRead, this, &PERTesterWorker::rx);
							 | 
						||
| 
								 | 
							
								        delete m_rxUDPSocket;
							 | 
						||
| 
								 | 
							
								        m_rxUDPSocket = nullptr;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::rx()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    while (m_rxUDPSocket->hasPendingDatagrams())
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        QNetworkDatagram datagram = m_rxUDPSocket->receiveDatagram();
							 | 
						||
| 
								 | 
							
								        QByteArray packet = datagram.data();
							 | 
						||
| 
								 | 
							
								        // Ignore header and CRC, if requested
							 | 
						||
| 
								 | 
							
								        packet = packet.mid(m_settings.m_ignoreLeadingBytes, packet.size() - m_settings.m_ignoreLeadingBytes - m_settings.m_ignoreTrailingBytes);
							 | 
						||
| 
								 | 
							
								        // Remove from list of transmitted packets
							 | 
						||
| 
								 | 
							
								        int i;
							 | 
						||
| 
								 | 
							
								        for (i = 0; i < m_txPackets.size(); i++)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            if (packet == m_txPackets[i])
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                m_rxMatched++;
							 | 
						||
| 
								 | 
							
								                m_txPackets.removeAt(i);
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (i == m_txPackets.size())
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            qDebug() << "PERTesterWorker::rx: Received packet that was not transmitted: " << packet.toHex();
							 | 
						||
| 
								 | 
							
								            m_rxUnmatched++;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (getMessageQueueToGUI())
							 | 
						||
| 
								 | 
							
								        getMessageQueueToGUI()->push(PERTesterReport::MsgReportStats::create(m_tx, m_rxMatched, m_rxUnmatched));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::tx()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    QRegExp ax25Dst("^%\\{ax25\\.dst=([A-Za-z0-9-]+)\\}");
							 | 
						||
| 
								 | 
							
								    QRegExp ax25Src("^%\\{ax25\\.src=([A-Za-z0-9-]+)\\}");
							 | 
						||
| 
								 | 
							
								    QRegExp num("^%\\{num\\}");
							 | 
						||
| 
								 | 
							
								    QRegExp data("^%\\{data=([0-9]+),([0-9]+)\\}");
							 | 
						||
| 
								 | 
							
								    QRegExp hex("^(0x)?([0-9a-fA-F]?[0-9a-fA-F])");
							 | 
						||
| 
								 | 
							
								    QByteArray bytes;
							 | 
						||
| 
								 | 
							
								    int pos = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while (pos < m_settings.m_packet.size())
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (m_settings.m_packet[pos] == '%')
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            if (ax25Dst.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                // AX.25 destination callsign & SSID
							 | 
						||
| 
								 | 
							
								                QString address = ax25Dst.capturedTexts()[1];
							 | 
						||
| 
								 | 
							
								                bytes.append(AX25Packet::encodeAddress(address));
							 | 
						||
| 
								 | 
							
								                pos += ax25Dst.matchedLength();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if (ax25Src.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                // AX.25 source callsign & SSID
							 | 
						||
| 
								 | 
							
								                QString address = ax25Src.capturedTexts()[1];
							 | 
						||
| 
								 | 
							
								                bytes.append(AX25Packet::encodeAddress(address, 1));
							 | 
						||
| 
								 | 
							
								                pos += ax25Src.matchedLength();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if (num.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                // Big endian packet number
							 | 
						||
| 
								 | 
							
								                bytes.append((m_tx >> 24) & 0xff);
							 | 
						||
| 
								 | 
							
								                bytes.append((m_tx >> 16) & 0xff);
							 | 
						||
| 
								 | 
							
								                bytes.append((m_tx >> 8) & 0xff);
							 | 
						||
| 
								 | 
							
								                bytes.append(m_tx & 0xff);
							 | 
						||
| 
								 | 
							
								                pos += num.matchedLength();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else if (data.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                // Constrained random number of random bytes
							 | 
						||
| 
								 | 
							
								                int minBytes = data.capturedTexts()[1].toInt();
							 | 
						||
| 
								 | 
							
								                int maxBytes = data.capturedTexts()[2].toInt();
							 | 
						||
| 
								 | 
							
								                std::random_device rd;
							 | 
						||
| 
								 | 
							
								                std::mt19937 gen(rd());
							 | 
						||
| 
								 | 
							
								                std::uniform_int_distribution<> distr0(minBytes, maxBytes);
							 | 
						||
| 
								 | 
							
								                std::uniform_int_distribution<> distr1(0, 255);
							 | 
						||
| 
								 | 
							
								                int count = distr0(gen);
							 | 
						||
| 
								 | 
							
								                for (int i = 0; i < count; i++)
							 | 
						||
| 
								 | 
							
								                    bytes.append(distr1(gen));
							 | 
						||
| 
								 | 
							
								                pos += data.matchedLength();
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                qWarning() << "PERTester: Unsupported substitution in packet: pos=" << pos << " in " << m_settings.m_packet;
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (m_settings.m_packet[pos] == '\"')
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            // ASCII string in double quotes
							 | 
						||
| 
								 | 
							
								            int startIdx = pos + 1;
							 | 
						||
| 
								 | 
							
								            int endQuoteIdx = m_settings.m_packet.indexOf('\"', startIdx);
							 | 
						||
| 
								 | 
							
								            if (endQuoteIdx != -1)
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                int len = endQuoteIdx - startIdx;
							 | 
						||
| 
								 | 
							
								                QString string = m_settings.m_packet.mid(startIdx, len);
							 | 
						||
| 
								 | 
							
								                bytes.append(string.toLocal8Bit());
							 | 
						||
| 
								 | 
							
								                pos = endQuoteIdx + 1;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            else
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                qWarning() << "PERTester: Unterminated string: pos=" << pos << " in " << m_settings.m_packet;
							 | 
						||
| 
								 | 
							
								                break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if (hex.indexIn(m_settings.m_packet, pos, QRegExp::CaretAtOffset) != -1)
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            // Hex byte
							 | 
						||
| 
								 | 
							
								            int value = hex.capturedTexts()[2].toInt(nullptr, 16);
							 | 
						||
| 
								 | 
							
								            bytes.append(value);
							 | 
						||
| 
								 | 
							
								            pos += hex.matchedLength();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else if ((m_settings.m_packet[pos] == ' ') || (m_settings.m_packet[pos] == ',') || (m_settings.m_packet[pos] == ':'))
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            pos++;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        else
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            qWarning() << "PERTester: Unexpected character in packet: pos=" << pos << " in " << m_settings.m_packet;
							 | 
						||
| 
								 | 
							
								            break;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if ((pos >= m_settings.m_packet.size()) && (m_tx < m_settings.m_packetCount))
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // Send packet via UDP
							 | 
						||
| 
								 | 
							
								        m_txUDPSocket.writeDatagram(bytes.data(), bytes.size(), QHostAddress(m_settings.m_txUDPAddress), m_settings.m_txUDPPort);
							 | 
						||
| 
								 | 
							
								        m_tx++;
							 | 
						||
| 
								 | 
							
								        m_txPackets.append(bytes);
							 | 
						||
| 
								 | 
							
								        if (getMessageQueueToGUI())
							 | 
						||
| 
								 | 
							
								            getMessageQueueToGUI()->push(PERTesterReport::MsgReportStats::create(m_tx, m_rxMatched, m_rxUnmatched));
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (m_tx >= m_settings.m_packetCount)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        // Test complete
							 | 
						||
| 
								 | 
							
								        m_txTimer.stop();
							 | 
						||
| 
								 | 
							
								        // Wait for a couple of seconds for the last packet to be received
							 | 
						||
| 
								 | 
							
								        QTimer::singleShot(2000, this, &PERTesterWorker::testComplete);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void PERTesterWorker::testComplete()
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    stopWork();
							 | 
						||
| 
								 | 
							
								    if (m_msgQueueToFeature != nullptr)
							 | 
						||
| 
								 | 
							
								        m_msgQueueToFeature->push(PERTester::MsgReportWorker::create("Complete"));
							 | 
						||
| 
								 | 
							
								    qDebug() << "PERTesterWorker::tx: Test complete";
							 | 
						||
| 
								 | 
							
								}
							 |