2018-10-13 20:25:51 -04:00
///////////////////////////////////////////////////////////////////////////////////
2019-07-07 18:59:04 -04:00
// Copyright (C) 2015-2019 Edouard Griffiths, F4EXB //
2018-10-13 20:25:51 -04:00
// //
// 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 //
2019-04-11 00:57:41 -04:00
// (at your option) any later version. //
2018-10-13 20:25:51 -04:00
// //
// 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 <string.h>
# include <errno.h>
# include <QDebug>
2018-12-26 05:06:37 -05:00
# include <QNetworkReply>
# include <QBuffer>
2018-10-13 20:25:51 -04:00
# include "SWGDeviceSettings.h"
2019-07-07 18:59:04 -04:00
# include "SWGFileInputSettings.h"
2018-10-13 20:25:51 -04:00
# include "SWGDeviceState.h"
# include "SWGDeviceReport.h"
# include "util/simpleserializer.h"
# include "dsp/dspcommands.h"
2019-05-08 16:11:53 -04:00
# include "dsp/dspdevicesourceengine.h"
2018-10-13 20:25:51 -04:00
# include "dsp/dspengine.h"
# include "dsp/filerecord.h"
2021-05-21 05:06:10 -04:00
# include "dsp/wavfilerecord.h"
2019-05-08 16:11:53 -04:00
# include "device/deviceapi.h"
2018-10-13 20:25:51 -04:00
2019-07-07 18:59:04 -04:00
# include "fileinput.h"
2020-07-11 00:45:16 -04:00
# include "fileinputworker.h"
2019-07-07 18:59:04 -04:00
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgConfigureFileInput , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgConfigureFileSourceName , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgConfigureFileInputWork , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgConfigureFileSourceSeek , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgConfigureFileInputStreamTiming , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgStartStop , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgPlayPause , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgReportFileSourceAcquisition , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgReportFileInputStreamData , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgReportFileInputStreamTiming , Message )
MESSAGE_CLASS_DEFINITION ( FileInput : : MsgReportHeaderCRC , Message )
FileInput : : FileInput ( DeviceAPI * deviceAPI ) :
2018-10-13 20:25:51 -04:00
m_deviceAPI ( deviceAPI ) ,
m_settings ( ) ,
2020-07-11 00:45:16 -04:00
m_fileInputWorker ( nullptr ) ,
2022-03-17 16:10:30 -04:00
m_deviceDescription ( " FileInput " ) ,
2020-07-11 05:09:58 -04:00
m_sampleRate ( 48000 ) ,
2018-10-13 20:25:51 -04:00
m_sampleSize ( 0 ) ,
2020-07-11 05:09:58 -04:00
m_centerFrequency ( 435000000 ) ,
2020-08-25 18:12:30 -04:00
m_recordLengthMuSec ( 0 ) ,
2019-05-21 17:33:36 -04:00
m_startingTimeStamp ( 0 )
2018-10-13 20:25:51 -04:00
{
2022-03-17 16:10:30 -04:00
m_sampleFifo . setLabel ( m_deviceDescription ) ;
2019-05-20 18:27:08 -04:00
m_deviceAPI - > setNbSourceStreams ( 1 ) ;
2019-07-07 18:59:04 -04:00
qDebug ( " FileInput::FileInput: device source engine: %p " , m_deviceAPI - > getDeviceSourceEngine ( ) ) ;
qDebug ( " FileInput::FileInput: device source engine message queue: %p " , m_deviceAPI - > getDeviceEngineInputMessageQueue ( ) ) ;
qDebug ( " FileInput::FileInput: device source: %p " , m_deviceAPI - > getDeviceSourceEngine ( ) - > getSource ( ) ) ;
2018-12-26 05:06:37 -05:00
m_networkManager = new QNetworkAccessManager ( ) ;
2022-03-23 17:32:23 -04:00
QObject : : connect (
m_networkManager ,
& QNetworkAccessManager : : finished ,
this ,
& FileInput : : networkManagerFinished
) ;
2019-05-21 17:33:36 -04:00
m_masterTimer . setTimerType ( Qt : : PreciseTimer ) ;
m_masterTimer . start ( 50 ) ;
2018-10-13 20:25:51 -04:00
}
2019-07-07 18:59:04 -04:00
FileInput : : ~ FileInput ( )
2018-10-13 20:25:51 -04:00
{
2019-05-21 17:33:36 -04:00
m_masterTimer . stop ( ) ;
2022-03-23 17:32:23 -04:00
QObject : : disconnect (
m_networkManager ,
& QNetworkAccessManager : : finished ,
this ,
& FileInput : : networkManagerFinished
) ;
2018-12-26 05:06:37 -05:00
delete m_networkManager ;
2018-10-13 20:25:51 -04:00
stop ( ) ;
}
2019-07-07 18:59:04 -04:00
void FileInput : : destroy ( )
2018-10-13 20:25:51 -04:00
{
delete this ;
}
2019-07-07 18:59:04 -04:00
void FileInput : : openFileStream ( )
2018-10-13 20:25:51 -04:00
{
//stopInput();
2023-01-02 10:42:34 -05:00
# ifdef ANDROID
if ( m_inputFile . isOpen ( ) ) {
m_inputFile . close ( ) ;
}
m_inputFile . setFileName ( m_settings . m_fileName ) ;
m_inputFile . open ( QIODevice : : ReadOnly | QIODevice : : ExistingOnly ) ;
quint64 fileSize = ( quint64 ) m_inputFile . size ( ) ;
# else
if ( m_ifstream . is_open ( ) ) {
2018-10-13 20:25:51 -04:00
m_ifstream . close ( ) ;
}
2019-05-27 01:26:38 -04:00
# ifdef Q_OS_WIN
2021-03-05 10:55:44 -05:00
m_ifstream . open ( m_settings . m_fileName . toStdWString ( ) . c_str ( ) , std : : ios : : binary | std : : ios : : ate ) ;
2019-05-27 01:26:38 -04:00
# else
2021-03-05 10:55:44 -05:00
m_ifstream . open ( m_settings . m_fileName . toStdString ( ) . c_str ( ) , std : : ios : : binary | std : : ios : : ate ) ;
2019-05-27 01:26:38 -04:00
# endif
2018-10-13 20:25:51 -04:00
quint64 fileSize = m_ifstream . tellg ( ) ;
2023-01-02 10:42:34 -05:00
# endif
2018-10-13 20:25:51 -04:00
2021-05-21 05:06:10 -04:00
if ( m_settings . m_fileName . endsWith ( " .wav " ) )
{
WavFileRecord : : Header header ;
2023-01-02 10:42:34 -05:00
# ifdef ANDROID
m_inputFile . seek ( 0 ) ;
bool headerOK = WavFileRecord : : readHeader ( m_inputFile , header ) ;
# else
2021-05-21 05:06:10 -04:00
m_ifstream . seekg ( 0 , std : : ios_base : : beg ) ;
bool headerOK = WavFileRecord : : readHeader ( m_ifstream , header ) ;
2023-01-02 10:42:34 -05:00
# endif
2021-05-21 05:06:10 -04:00
m_sampleRate = header . m_sampleRate ;
if ( header . m_auxiHeader . m_size > 0 )
{
// Some WAV files written by SDR tools have auxi header
m_centerFrequency = header . m_auxi . m_centerFreq ;
2021-12-30 08:24:39 -05:00
m_startingTimeStamp = header . getStartTime ( ) . toMSecsSinceEpoch ( ) ;
2021-05-21 05:06:10 -04:00
}
else
{
2021-05-21 16:05:23 -04:00
// Attempt to extract start time and frequency from filename
QDateTime startTime ;
if ( WavFileRecord : : getStartTime ( m_settings . m_fileName , startTime ) ) {
2021-12-30 08:24:39 -05:00
m_startingTimeStamp = startTime . toMSecsSinceEpoch ( ) ;
2021-05-21 05:06:10 -04:00
}
2021-05-21 16:05:23 -04:00
WavFileRecord : : getCenterFrequency ( m_settings . m_fileName , m_centerFrequency ) ;
2021-05-21 05:06:10 -04:00
}
m_sampleSize = header . m_bitsPerSample ;
if ( headerOK & & ( m_sampleRate > 0 ) & & ( m_sampleSize > 0 ) )
{
2023-01-02 10:42:34 -05:00
# ifdef ANDROID
qint64 pos = m_inputFile . pos ( ) ;
# else
qint64 pos = m_ifstream . tellg ( ) ;
# endif
m_recordLengthMuSec = ( ( fileSize - pos ) * 1000000UL ) / ( ( m_sampleSize = = 24 ? 8 : 4 ) * m_sampleRate ) ;
2021-05-21 05:06:10 -04:00
}
else
{
2022-09-28 09:26:27 -04:00
qCritical ( " FileInput::openFileStream: broken or unsupported format of .wav file " ) ;
2021-05-21 05:06:10 -04:00
m_recordLengthMuSec = 0 ;
}
if ( getMessageQueueToGUI ( ) )
{
MsgReportHeaderCRC * report = MsgReportHeaderCRC : : create ( headerOK ) ;
getMessageQueueToGUI ( ) - > push ( report ) ;
}
}
else if ( fileSize > sizeof ( FileRecord : : Header ) )
2018-10-13 20:25:51 -04:00
{
FileRecord : : Header header ;
2023-01-02 10:42:34 -05:00
# ifdef ANDROID
m_inputFile . seek ( 0 ) ;
bool crcOK = FileRecord : : readHeader ( m_inputFile , header ) ;
# else
2018-10-13 20:25:51 -04:00
m_ifstream . seekg ( 0 , std : : ios_base : : beg ) ;
bool crcOK = FileRecord : : readHeader ( m_ifstream , header ) ;
2023-01-02 10:42:34 -05:00
# endif
2018-10-13 20:25:51 -04:00
m_sampleRate = header . sampleRate ;
m_centerFrequency = header . centerFrequency ;
m_startingTimeStamp = header . startTimeStamp ;
m_sampleSize = header . sampleSize ;
QString crcHex = QString ( " %1 " ) . arg ( header . crc32 , 0 , 16 ) ;
2021-03-08 16:41:46 -05:00
if ( crcOK & & ( m_sampleRate > 0 ) & & ( m_sampleSize > 0 ) )
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
qDebug ( " FileInput::openFileStream: CRC32 OK for header: %s " , qPrintable ( crcHex ) ) ;
2020-08-25 18:12:30 -04:00
m_recordLengthMuSec = ( ( fileSize - sizeof ( FileRecord : : Header ) ) * 1000000UL ) / ( ( m_sampleSize = = 24 ? 8 : 4 ) * m_sampleRate ) ;
2018-10-13 20:25:51 -04:00
}
2021-03-08 16:41:46 -05:00
else if ( ! crcOK )
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
qCritical ( " FileInput::openFileStream: bad CRC32 for header: %s " , qPrintable ( crcHex ) ) ;
2020-08-25 18:12:30 -04:00
m_recordLengthMuSec = 0 ;
2018-10-13 20:25:51 -04:00
}
2021-03-08 16:41:46 -05:00
else
{
qCritical ( " FileInput::openFileStream: invalid header " ) ;
m_recordLengthMuSec = 0 ;
}
2018-10-13 20:25:51 -04:00
2020-07-11 00:56:32 -04:00
if ( getMessageQueueToGUI ( ) )
{
2018-10-13 20:25:51 -04:00
MsgReportHeaderCRC * report = MsgReportHeaderCRC : : create ( crcOK ) ;
getMessageQueueToGUI ( ) - > push ( report ) ;
}
}
else
{
2020-08-25 18:12:30 -04:00
m_recordLengthMuSec = 0 ;
2018-10-13 20:25:51 -04:00
}
2021-03-05 10:55:44 -05:00
qDebug ( ) < < " FileInput::openFileStream: " < < m_settings . m_fileName . toStdString ( ) . c_str ( )
2018-10-13 20:25:51 -04:00
< < " fileSize: " < < fileSize < < " bytes "
2020-08-25 18:12:30 -04:00
< < " length: " < < m_recordLengthMuSec < < " microseconds "
2018-10-13 20:25:51 -04:00
< < " sample rate: " < < m_sampleRate < < " S/s "
< < " center frequency: " < < m_centerFrequency < < " Hz "
< < " sample size: " < < m_sampleSize < < " bits " ;
2020-07-11 00:56:32 -04:00
if ( getMessageQueueToGUI ( ) )
{
2020-07-11 05:09:58 -04:00
DSPSignalNotification * notif = new DSPSignalNotification ( m_sampleRate , m_centerFrequency ) ;
getMessageQueueToGUI ( ) - > push ( notif ) ;
2019-07-07 18:59:04 -04:00
MsgReportFileInputStreamData * report = MsgReportFileInputStreamData : : create ( m_sampleRate ,
2018-10-13 20:25:51 -04:00
m_sampleSize ,
m_centerFrequency ,
m_startingTimeStamp ,
2020-08-25 18:12:30 -04:00
m_recordLengthMuSec ) ; // file stream data
2018-10-13 20:25:51 -04:00
getMessageQueueToGUI ( ) - > push ( report ) ;
}
2020-08-25 18:12:30 -04:00
if ( m_recordLengthMuSec = = 0 ) {
2023-01-02 10:42:34 -05:00
# ifdef ANDROID
m_inputFile . close ( ) ;
# else
2018-10-13 20:25:51 -04:00
m_ifstream . close ( ) ;
2023-01-02 10:42:34 -05:00
# endif
2018-10-13 20:25:51 -04:00
}
}
2019-07-07 18:59:04 -04:00
void FileInput : : seekFileStream ( int seekMillis )
2018-10-13 20:25:51 -04:00
{
QMutexLocker mutexLocker ( & m_mutex ) ;
2023-01-02 10:42:34 -05:00
if (
# ifdef ANDROID
m_inputFile . isOpen ( )
# else
m_ifstream . is_open ( )
# endif
& & m_fileInputWorker & & ! m_fileInputWorker - > isRunning ( ) )
2018-10-13 20:25:51 -04:00
{
2020-08-25 18:12:30 -04:00
quint64 seekPoint = ( ( m_recordLengthMuSec * seekMillis ) / 1000 ) * m_sampleRate ;
seekPoint / = 1000000UL ;
2020-07-11 00:45:16 -04:00
m_fileInputWorker - > setSamplesCount ( seekPoint ) ;
2020-08-03 18:29:15 -04:00
seekPoint * = ( m_sampleSize = = 24 ? 8 : 4 ) ; // + sizeof(FileRecord::Header)
2023-01-02 10:42:34 -05:00
# ifdef ANDROID
m_inputFile . seek ( seekPoint + sizeof ( FileRecord : : Header ) ) ;
# else
2018-10-13 20:25:51 -04:00
m_ifstream . clear ( ) ;
m_ifstream . seekg ( seekPoint + sizeof ( FileRecord : : Header ) , std : : ios : : beg ) ;
2023-01-02 10:42:34 -05:00
# endif
2018-10-13 20:25:51 -04:00
}
}
2019-07-07 18:59:04 -04:00
void FileInput : : init ( )
2018-10-13 20:25:51 -04:00
{
2020-07-11 05:09:58 -04:00
DSPSignalNotification * notif = new DSPSignalNotification ( m_sampleRate , m_centerFrequency ) ;
2018-10-13 20:25:51 -04:00
m_deviceAPI - > getDeviceEngineInputMessageQueue ( ) - > push ( notif ) ;
}
2019-07-07 18:59:04 -04:00
bool FileInput : : start ( )
2018-10-13 20:25:51 -04:00
{
2023-01-02 10:42:34 -05:00
# ifdef ANDROID
if ( ! m_inputFile . isOpen ( ) )
# else
2018-10-13 20:25:51 -04:00
if ( ! m_ifstream . is_open ( ) )
2023-01-02 10:42:34 -05:00
# endif
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
qWarning ( " FileInput::start: file not open. not starting " ) ;
2018-10-13 20:25:51 -04:00
return false ;
}
QMutexLocker mutexLocker ( & m_mutex ) ;
2019-07-07 18:59:04 -04:00
qDebug ( ) < < " FileInput::start " ;
2018-10-13 20:25:51 -04:00
2023-01-02 10:42:34 -05:00
# ifdef ANDROID
m_inputFile . seek ( 0 ) ;
# else
2020-07-11 00:56:32 -04:00
if ( m_ifstream . tellg ( ) ! = ( std : : streampos ) 0 )
{
2018-10-13 20:25:51 -04:00
m_ifstream . clear ( ) ;
m_ifstream . seekg ( sizeof ( FileRecord : : Header ) , std : : ios : : beg ) ;
}
2023-01-02 10:42:34 -05:00
# endif
2018-10-13 20:25:51 -04:00
2020-07-11 00:56:32 -04:00
if ( ! m_sampleFifo . setSize ( m_settings . m_accelerationFactor * m_sampleRate * sizeof ( Sample ) ) )
{
2018-10-13 20:25:51 -04:00
qCritical ( " Could not allocate SampleFifo " ) ;
return false ;
}
2023-01-02 10:42:34 -05:00
# ifdef ANDROID
m_fileInputWorker = new FileInputWorker ( & m_inputFile , & m_sampleFifo , m_masterTimer , & m_inputMessageQueue ) ;
# else
2020-07-11 00:45:16 -04:00
m_fileInputWorker = new FileInputWorker ( & m_ifstream , & m_sampleFifo , m_masterTimer , & m_inputMessageQueue ) ;
2023-01-02 10:42:34 -05:00
# endif
2020-07-11 00:45:16 -04:00
m_fileInputWorker - > moveToThread ( & m_fileInputWorkerThread ) ;
m_fileInputWorker - > setSampleRateAndSize ( m_settings . m_accelerationFactor * m_sampleRate , m_sampleSize ) ; // Fast Forward: 1 corresponds to live. 1/2 is half speed, 2 is double speed
startWorker ( ) ;
2019-07-07 18:59:04 -04:00
m_deviceDescription = " FileInput " ;
2018-10-13 20:25:51 -04:00
mutexLocker . unlock ( ) ;
2019-07-07 18:59:04 -04:00
qDebug ( " FileInput::startInput: started " ) ;
2018-10-13 20:25:51 -04:00
2020-07-11 00:56:32 -04:00
if ( getMessageQueueToGUI ( ) )
{
2018-10-13 20:25:51 -04:00
MsgReportFileSourceAcquisition * report = MsgReportFileSourceAcquisition : : create ( true ) ; // acquisition on
getMessageQueueToGUI ( ) - > push ( report ) ;
}
return true ;
}
2019-07-07 18:59:04 -04:00
void FileInput : : stop ( )
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
qDebug ( ) < < " FileInput::stop " ;
2018-10-13 20:25:51 -04:00
QMutexLocker mutexLocker ( & m_mutex ) ;
2020-07-11 00:45:16 -04:00
if ( m_fileInputWorker )
2018-10-13 20:25:51 -04:00
{
2020-07-11 00:45:16 -04:00
stopWorker ( ) ;
delete m_fileInputWorker ;
m_fileInputWorker = nullptr ;
2018-10-13 20:25:51 -04:00
}
m_deviceDescription . clear ( ) ;
2020-07-11 00:56:32 -04:00
if ( getMessageQueueToGUI ( ) )
{
2018-10-13 20:25:51 -04:00
MsgReportFileSourceAcquisition * report = MsgReportFileSourceAcquisition : : create ( false ) ; // acquisition off
getMessageQueueToGUI ( ) - > push ( report ) ;
}
}
2020-07-11 00:45:16 -04:00
void FileInput : : startWorker ( )
{
m_fileInputWorker - > startWork ( ) ;
m_fileInputWorkerThread . start ( ) ;
}
void FileInput : : stopWorker ( )
{
m_fileInputWorker - > stopWork ( ) ;
m_fileInputWorkerThread . quit ( ) ;
m_fileInputWorkerThread . wait ( ) ;
}
2019-07-07 18:59:04 -04:00
QByteArray FileInput : : serialize ( ) const
2018-10-13 20:25:51 -04:00
{
return m_settings . serialize ( ) ;
}
2019-07-07 18:59:04 -04:00
bool FileInput : : deserialize ( const QByteArray & data )
2018-10-13 20:25:51 -04:00
{
bool success = true ;
if ( ! m_settings . deserialize ( data ) )
{
m_settings . resetToDefaults ( ) ;
success = false ;
}
2022-10-23 14:33:51 -04:00
MsgConfigureFileInput * message = MsgConfigureFileInput : : create ( m_settings , QList < QString > ( ) , true ) ;
2018-10-13 20:25:51 -04:00
m_inputMessageQueue . push ( message ) ;
if ( getMessageQueueToGUI ( ) )
{
2022-10-23 14:33:51 -04:00
MsgConfigureFileInput * messageToGUI = MsgConfigureFileInput : : create ( m_settings , QList < QString > ( ) , true ) ;
2018-10-13 20:25:51 -04:00
getMessageQueueToGUI ( ) - > push ( messageToGUI ) ;
}
return success ;
}
2019-07-07 18:59:04 -04:00
const QString & FileInput : : getDeviceDescription ( ) const
2018-10-13 20:25:51 -04:00
{
return m_deviceDescription ;
}
2019-07-07 18:59:04 -04:00
int FileInput : : getSampleRate ( ) const
2018-10-13 20:25:51 -04:00
{
return m_sampleRate ;
}
2019-07-07 18:59:04 -04:00
quint64 FileInput : : getCenterFrequency ( ) const
2018-10-13 20:25:51 -04:00
{
return m_centerFrequency ;
}
2019-07-07 18:59:04 -04:00
void FileInput : : setCenterFrequency ( qint64 centerFrequency )
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
FileInputSettings settings = m_settings ;
2020-07-11 05:09:58 -04:00
m_centerFrequency = centerFrequency ;
2018-10-13 20:25:51 -04:00
2022-10-23 14:33:51 -04:00
MsgConfigureFileInput * message = MsgConfigureFileInput : : create ( m_settings , QList < QString > { " centerFrequency " } , false ) ;
2018-10-13 20:25:51 -04:00
m_inputMessageQueue . push ( message ) ;
if ( getMessageQueueToGUI ( ) )
{
2022-10-23 14:33:51 -04:00
MsgConfigureFileInput * messageToGUI = MsgConfigureFileInput : : create ( m_settings , QList < QString > { " centerFrequency " } , false ) ;
2018-10-13 20:25:51 -04:00
getMessageQueueToGUI ( ) - > push ( messageToGUI ) ;
}
}
2019-07-07 18:59:04 -04:00
quint64 FileInput : : getStartingTimeStamp ( ) const
2018-10-13 20:25:51 -04:00
{
return m_startingTimeStamp ;
}
2019-07-07 18:59:04 -04:00
bool FileInput : : handleMessage ( const Message & message )
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
if ( MsgConfigureFileInput : : match ( message ) )
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
MsgConfigureFileInput & conf = ( MsgConfigureFileInput & ) message ;
2022-10-23 14:33:51 -04:00
applySettings ( conf . getSettings ( ) , conf . getSettingsKeys ( ) , conf . getForce ( ) ) ;
2018-10-13 20:25:51 -04:00
return true ;
}
else if ( MsgConfigureFileSourceName : : match ( message ) )
{
MsgConfigureFileSourceName & conf = ( MsgConfigureFileSourceName & ) message ;
2021-03-05 10:55:44 -05:00
m_settings . m_fileName = conf . getFileName ( ) ;
2018-10-13 20:25:51 -04:00
openFileStream ( ) ;
return true ;
}
2019-07-07 18:59:04 -04:00
else if ( MsgConfigureFileInputWork : : match ( message ) )
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
MsgConfigureFileInputWork & conf = ( MsgConfigureFileInputWork & ) message ;
2018-10-13 20:25:51 -04:00
bool working = conf . isWorking ( ) ;
2020-07-11 00:56:32 -04:00
if ( m_fileInputWorker )
2018-10-13 20:25:51 -04:00
{
2018-10-14 04:38:31 -04:00
if ( working ) {
2020-07-11 00:45:16 -04:00
startWorker ( ) ;
2018-10-14 04:38:31 -04:00
} else {
2020-07-11 00:45:16 -04:00
stopWorker ( ) ;
2018-10-13 20:25:51 -04:00
}
}
return true ;
}
else if ( MsgConfigureFileSourceSeek : : match ( message ) )
{
MsgConfigureFileSourceSeek & conf = ( MsgConfigureFileSourceSeek & ) message ;
2018-10-13 20:38:24 -04:00
int seekMillis = conf . getMillis ( ) ;
seekFileStream ( seekMillis ) ;
2018-10-13 20:25:51 -04:00
return true ;
}
2019-07-07 18:59:04 -04:00
else if ( MsgConfigureFileInputStreamTiming : : match ( message ) )
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
MsgReportFileInputStreamTiming * report ;
2018-10-13 20:25:51 -04:00
2020-07-11 00:56:32 -04:00
if ( m_fileInputWorker )
2018-10-13 20:25:51 -04:00
{
if ( getMessageQueueToGUI ( ) )
{
2020-07-11 00:45:16 -04:00
report = MsgReportFileInputStreamTiming : : create ( m_fileInputWorker - > getSamplesCount ( ) ) ;
2018-10-13 20:25:51 -04:00
getMessageQueueToGUI ( ) - > push ( report ) ;
}
}
return true ;
}
else if ( MsgStartStop : : match ( message ) )
{
MsgStartStop & cmd = ( MsgStartStop & ) message ;
2019-07-07 18:59:04 -04:00
qDebug ( ) < < " FileInput::handleMessage: MsgStartStop: " < < ( cmd . getStartStop ( ) ? " start " : " stop " ) ;
2018-10-13 20:25:51 -04:00
if ( cmd . getStartStop ( ) )
{
2020-07-11 00:56:32 -04:00
if ( m_deviceAPI - > initDeviceEngine ( ) ) {
2019-05-08 16:11:53 -04:00
m_deviceAPI - > startDeviceEngine ( ) ;
2018-10-13 20:25:51 -04:00
}
}
else
{
2019-05-08 16:11:53 -04:00
m_deviceAPI - > stopDeviceEngine ( ) ;
2018-10-13 20:25:51 -04:00
}
2018-12-26 05:06:37 -05:00
if ( m_settings . m_useReverseAPI ) {
webapiReverseSendStartStop ( cmd . getStartStop ( ) ) ;
}
2018-10-13 20:25:51 -04:00
return true ;
}
2020-07-11 00:45:16 -04:00
else if ( FileInputWorker : : MsgReportEOF : : match ( message ) )
2018-10-13 20:25:51 -04:00
{
2019-07-07 18:59:04 -04:00
qDebug ( ) < < " FileInput::handleMessage: MsgReportEOF " ;
2020-07-11 00:45:16 -04:00
stopWorker ( ) ;
2018-10-13 20:25:51 -04:00
if ( getMessageQueueToGUI ( ) )
{
2020-07-11 00:45:16 -04:00
MsgReportFileInputStreamTiming * report = MsgReportFileInputStreamTiming : : create ( m_fileInputWorker - > getSamplesCount ( ) ) ;
2018-10-13 20:25:51 -04:00
getMessageQueueToGUI ( ) - > push ( report ) ;
}
if ( m_settings . m_loop )
{
seekFileStream ( 0 ) ;
2020-07-11 00:45:16 -04:00
startWorker ( ) ;
2018-10-13 20:25:51 -04:00
}
else
{
if ( getMessageQueueToGUI ( ) )
{
MsgPlayPause * report = MsgPlayPause : : create ( false ) ;
getMessageQueueToGUI ( ) - > push ( report ) ;
}
}
return true ;
}
else
{
return false ;
}
}
2022-10-23 14:33:51 -04:00
bool FileInput : : applySettings ( const FileInputSettings & settings , const QList < QString > & settingsKeys , bool force )
2018-10-13 20:25:51 -04:00
{
2022-10-23 14:33:51 -04:00
qDebug ( ) < < " FileInput::applySettings: force: " < < force < < settings . getDebugString ( settingsKeys , force ) ;
2018-12-26 05:06:37 -05:00
2022-10-23 14:33:51 -04:00
if ( settingsKeys . contains ( " accelerationFactor " ) | | force )
2018-10-13 20:25:51 -04:00
{
2020-07-11 00:45:16 -04:00
if ( m_fileInputWorker )
2018-10-13 20:25:51 -04:00
{
QMutexLocker mutexLocker ( & m_mutex ) ;
2018-10-13 20:58:14 -04:00
if ( ! m_sampleFifo . setSize ( m_settings . m_accelerationFactor * m_sampleRate * sizeof ( Sample ) ) ) {
2019-07-07 18:59:04 -04:00
qCritical ( " FileInput::applySettings: could not reallocate sample FIFO size to %lu " ,
2018-10-13 20:58:14 -04:00
m_settings . m_accelerationFactor * m_sampleRate * sizeof ( Sample ) ) ;
}
2020-07-11 00:45:16 -04:00
m_fileInputWorker - > setSampleRateAndSize ( settings . m_accelerationFactor * m_sampleRate , m_sampleSize ) ; // Fast Forward: 1 corresponds to live. 1/2 is half speed, 2 is double speed
2018-10-13 20:25:51 -04:00
}
}
2022-10-23 14:33:51 -04:00
if ( settingsKeys . contains ( " useReverseAPI " ) )
2018-12-26 05:06:37 -05:00
{
2022-10-23 14:33:51 -04:00
bool fullUpdate = ( settingsKeys . contains ( " useReverseAPI " ) & & settings . m_useReverseAPI ) | |
settingsKeys . contains ( " reverseAPIAddress " ) | |
settingsKeys . contains ( " reverseAPIPort " ) | |
settingsKeys . contains ( " reverseAPIDeviceIndex " ) ;
webapiReverseSendSettings ( settingsKeys , settings , fullUpdate | | force ) ;
2018-12-26 05:06:37 -05:00
}
2022-10-23 14:33:51 -04:00
if ( force ) {
m_settings = settings ;
} else {
m_settings . applySettings ( settingsKeys , settings ) ;
}
2021-03-05 10:55:44 -05:00
// Open the file if there isn't a GUI which will open it
2022-10-23 14:33:51 -04:00
if ( ( m_guiMessageQueue = = nullptr ) & & settingsKeys . contains ( " fileName " ) & & ! m_settings . m_fileName . isEmpty ( ) ) {
2021-03-05 10:55:44 -05:00
openFileStream ( ) ;
2022-10-23 14:33:51 -04:00
}
2021-03-05 10:55:44 -05:00
2018-10-13 20:25:51 -04:00
return true ;
}
2019-07-07 18:59:04 -04:00
int FileInput : : webapiSettingsGet (
2018-10-13 20:25:51 -04:00
SWGSDRangel : : SWGDeviceSettings & response ,
2018-11-13 02:51:14 -05:00
QString & errorMessage )
2018-10-13 20:25:51 -04:00
{
2018-11-13 02:51:14 -05:00
( void ) errorMessage ;
2019-07-07 18:59:04 -04:00
response . setFileInputSettings ( new SWGSDRangel : : SWGFileInputSettings ( ) ) ;
response . getFileInputSettings ( ) - > init ( ) ;
2018-10-14 04:38:31 -04:00
webapiFormatDeviceSettings ( response , m_settings ) ;
return 200 ;
}
2019-07-07 18:59:04 -04:00
int FileInput : : webapiSettingsPutPatch (
2018-10-14 04:38:31 -04:00
bool force ,
const QStringList & deviceSettingsKeys ,
SWGSDRangel : : SWGDeviceSettings & response , // query + response
2018-11-13 02:51:14 -05:00
QString & errorMessage )
2018-10-14 04:38:31 -04:00
{
2018-11-13 02:51:14 -05:00
( void ) errorMessage ;
2019-07-07 18:59:04 -04:00
FileInputSettings settings = m_settings ;
2019-08-04 14:24:44 -04:00
webapiUpdateDeviceSettings ( settings , deviceSettingsKeys , response ) ;
2018-10-14 04:38:31 -04:00
2022-10-23 14:33:51 -04:00
MsgConfigureFileInput * msg = MsgConfigureFileInput : : create ( settings , deviceSettingsKeys , force ) ;
2019-08-04 14:24:44 -04:00
m_inputMessageQueue . push ( msg ) ;
if ( m_guiMessageQueue ) // forward to GUI if any
{
2022-10-23 14:33:51 -04:00
MsgConfigureFileInput * msgToGUI = MsgConfigureFileInput : : create ( settings , deviceSettingsKeys , force ) ;
2019-08-04 14:24:44 -04:00
m_guiMessageQueue - > push ( msgToGUI ) ;
}
webapiFormatDeviceSettings ( response , settings ) ;
return 200 ;
}
void FileInput : : webapiUpdateDeviceSettings (
FileInputSettings & settings ,
const QStringList & deviceSettingsKeys ,
SWGSDRangel : : SWGDeviceSettings & response )
{
2018-10-14 04:38:31 -04:00
if ( deviceSettingsKeys . contains ( " fileName " ) ) {
2019-07-07 18:59:04 -04:00
settings . m_fileName = * response . getFileInputSettings ( ) - > getFileName ( ) ;
2018-10-14 04:38:31 -04:00
}
if ( deviceSettingsKeys . contains ( " accelerationFactor " ) ) {
2019-07-07 18:59:04 -04:00
settings . m_accelerationFactor = response . getFileInputSettings ( ) - > getAccelerationFactor ( ) ;
2018-10-14 04:38:31 -04:00
}
if ( deviceSettingsKeys . contains ( " loop " ) ) {
2019-07-07 18:59:04 -04:00
settings . m_loop = response . getFileInputSettings ( ) - > getLoop ( ) ! = 0 ;
2018-10-14 04:38:31 -04:00
}
2019-01-11 08:45:00 -05:00
if ( deviceSettingsKeys . contains ( " useReverseAPI " ) ) {
2019-07-07 18:59:04 -04:00
settings . m_useReverseAPI = response . getFileInputSettings ( ) - > getUseReverseApi ( ) ! = 0 ;
2019-01-11 08:45:00 -05:00
}
if ( deviceSettingsKeys . contains ( " reverseAPIAddress " ) ) {
2019-07-07 18:59:04 -04:00
settings . m_reverseAPIAddress = * response . getFileInputSettings ( ) - > getReverseApiAddress ( ) ;
2019-01-11 08:45:00 -05:00
}
if ( deviceSettingsKeys . contains ( " reverseAPIPort " ) ) {
2019-07-07 18:59:04 -04:00
settings . m_reverseAPIPort = response . getFileInputSettings ( ) - > getReverseApiPort ( ) ;
2019-01-11 08:45:00 -05:00
}
if ( deviceSettingsKeys . contains ( " reverseAPIDeviceIndex " ) ) {
2019-07-07 18:59:04 -04:00
settings . m_reverseAPIDeviceIndex = response . getFileInputSettings ( ) - > getReverseApiDeviceIndex ( ) ;
2019-01-11 08:45:00 -05:00
}
2018-10-13 20:25:51 -04:00
}
2019-07-07 18:59:04 -04:00
int FileInput : : webapiRunGet (
2018-10-13 20:25:51 -04:00
SWGSDRangel : : SWGDeviceState & response ,
2018-11-13 02:51:14 -05:00
QString & errorMessage )
2018-10-13 20:25:51 -04:00
{
2018-11-13 02:51:14 -05:00
( void ) errorMessage ;
2018-10-13 20:25:51 -04:00
m_deviceAPI - > getDeviceEngineStateStr ( * response . getState ( ) ) ;
return 200 ;
}
2019-07-07 18:59:04 -04:00
int FileInput : : webapiRun (
2018-10-13 20:25:51 -04:00
bool run ,
SWGSDRangel : : SWGDeviceState & response ,
2018-11-13 02:51:14 -05:00
QString & errorMessage )
2018-10-13 20:25:51 -04:00
{
2018-11-13 02:51:14 -05:00
( void ) errorMessage ;
2018-10-13 20:25:51 -04:00
m_deviceAPI - > getDeviceEngineStateStr ( * response . getState ( ) ) ;
MsgStartStop * message = MsgStartStop : : create ( run ) ;
m_inputMessageQueue . push ( message ) ;
if ( getMessageQueueToGUI ( ) ) // forward to GUI if any
{
MsgStartStop * msgToGUI = MsgStartStop : : create ( run ) ;
getMessageQueueToGUI ( ) - > push ( msgToGUI ) ;
}
return 200 ;
}
2019-07-07 18:59:04 -04:00
int FileInput : : webapiReportGet (
2018-10-13 20:25:51 -04:00
SWGSDRangel : : SWGDeviceReport & response ,
2018-11-13 02:51:14 -05:00
QString & errorMessage )
2018-10-13 20:25:51 -04:00
{
2018-11-13 02:51:14 -05:00
( void ) errorMessage ;
2019-07-07 18:59:04 -04:00
response . setFileInputReport ( new SWGSDRangel : : SWGFileInputReport ( ) ) ;
response . getFileInputReport ( ) - > init ( ) ;
2018-10-13 20:25:51 -04:00
webapiFormatDeviceReport ( response ) ;
return 200 ;
}
2019-07-07 18:59:04 -04:00
void FileInput : : webapiFormatDeviceSettings ( SWGSDRangel : : SWGDeviceSettings & response , const FileInputSettings & settings )
2018-10-14 04:38:31 -04:00
{
2019-07-07 18:59:04 -04:00
response . getFileInputSettings ( ) - > setFileName ( new QString ( settings . m_fileName ) ) ;
response . getFileInputSettings ( ) - > setAccelerationFactor ( settings . m_accelerationFactor ) ;
response . getFileInputSettings ( ) - > setLoop ( settings . m_loop ? 1 : 0 ) ;
2018-10-14 04:38:31 -04:00
2019-07-07 18:59:04 -04:00
response . getFileInputSettings ( ) - > setUseReverseApi ( settings . m_useReverseAPI ? 1 : 0 ) ;
2019-01-11 08:45:00 -05:00
2019-07-07 18:59:04 -04:00
if ( response . getFileInputSettings ( ) - > getReverseApiAddress ( ) ) {
* response . getFileInputSettings ( ) - > getReverseApiAddress ( ) = settings . m_reverseAPIAddress ;
2019-01-11 08:45:00 -05:00
} else {
2019-07-07 18:59:04 -04:00
response . getFileInputSettings ( ) - > setReverseApiAddress ( new QString ( settings . m_reverseAPIAddress ) ) ;
2019-01-11 08:45:00 -05:00
}
2019-07-07 18:59:04 -04:00
response . getFileInputSettings ( ) - > setReverseApiPort ( settings . m_reverseAPIPort ) ;
response . getFileInputSettings ( ) - > setReverseApiDeviceIndex ( settings . m_reverseAPIDeviceIndex ) ;
2018-10-14 04:38:31 -04:00
}
2019-07-07 18:59:04 -04:00
void FileInput : : webapiFormatDeviceReport ( SWGSDRangel : : SWGDeviceReport & response )
2018-10-13 20:25:51 -04:00
{
qint64 t_sec = 0 ;
qint64 t_msec = 0 ;
quint64 samplesCount = 0 ;
2020-07-11 00:45:16 -04:00
if ( m_fileInputWorker ) {
samplesCount = m_fileInputWorker - > getSamplesCount ( ) ;
2018-10-13 20:25:51 -04:00
}
if ( m_sampleRate > 0 )
{
t_sec = samplesCount / m_sampleRate ;
t_msec = ( samplesCount - ( t_sec * m_sampleRate ) ) * 1000 / m_sampleRate ;
}
QTime t ( 0 , 0 , 0 , 0 ) ;
t = t . addSecs ( t_sec ) ;
t = t . addMSecs ( t_msec ) ;
2019-07-07 18:59:04 -04:00
response . getFileInputReport ( ) - > setElapsedTime ( new QString ( t . toString ( " HH:mm:ss.zzz " ) ) ) ;
2018-10-13 20:25:51 -04:00
2021-12-30 08:24:39 -05:00
qint64 startingTimeStampMsec = m_startingTimeStamp ;
2018-10-13 20:25:51 -04:00
QDateTime dt = QDateTime : : fromMSecsSinceEpoch ( startingTimeStampMsec ) ;
dt = dt . addSecs ( t_sec ) ;
dt = dt . addMSecs ( t_msec ) ;
2019-07-07 18:59:04 -04:00
response . getFileInputReport ( ) - > setAbsoluteTime ( new QString ( dt . toString ( " yyyy-MM-dd HH:mm:ss.zzz " ) ) ) ;
2018-10-13 20:25:51 -04:00
QTime recordLength ( 0 , 0 , 0 , 0 ) ;
2020-08-25 18:12:30 -04:00
recordLength = recordLength . addMSecs ( m_recordLengthMuSec / 1000UL ) ;
response . getFileInputReport ( ) - > setDurationTime ( new QString ( recordLength . toString ( " HH:mm:ss.zzz " ) ) ) ;
2018-10-13 20:25:51 -04:00
2021-03-05 10:55:44 -05:00
response . getFileInputReport ( ) - > setFileName ( new QString ( m_settings . m_fileName ) ) ;
2019-07-07 18:59:04 -04:00
response . getFileInputReport ( ) - > setSampleRate ( m_sampleRate ) ;
response . getFileInputReport ( ) - > setSampleSize ( m_sampleSize ) ;
2018-10-13 20:25:51 -04:00
}
2022-10-23 14:33:51 -04:00
void FileInput : : webapiReverseSendSettings ( const QList < QString > & deviceSettingsKeys , const FileInputSettings & settings , bool force )
2018-12-26 05:06:37 -05:00
{
SWGSDRangel : : SWGDeviceSettings * swgDeviceSettings = new SWGSDRangel : : SWGDeviceSettings ( ) ;
2019-05-07 08:43:38 -04:00
swgDeviceSettings - > setDirection ( 0 ) ; // single Rx
2019-03-25 08:41:38 -04:00
swgDeviceSettings - > setOriginatorIndex ( m_deviceAPI - > getDeviceSetIndex ( ) ) ;
2019-07-07 18:59:04 -04:00
swgDeviceSettings - > setDeviceHwType ( new QString ( " FileInput " ) ) ;
swgDeviceSettings - > setFileInputSettings ( new SWGSDRangel : : SWGFileInputSettings ( ) ) ;
SWGSDRangel : : SWGFileInputSettings * swgFileInputSettings = swgDeviceSettings - > getFileInputSettings ( ) ;
2018-12-26 05:06:37 -05:00
// transfer data that has been modified. When force is on transfer all data except reverse API data
if ( deviceSettingsKeys . contains ( " accelerationFactor " ) | | force ) {
2019-07-07 18:59:04 -04:00
swgFileInputSettings - > setAccelerationFactor ( settings . m_accelerationFactor ) ;
2018-12-26 05:06:37 -05:00
}
if ( deviceSettingsKeys . contains ( " loop " ) | | force ) {
2019-07-07 18:59:04 -04:00
swgFileInputSettings - > setLoop ( settings . m_loop ) ;
2018-12-26 05:06:37 -05:00
}
if ( deviceSettingsKeys . contains ( " fileName " ) | | force ) {
2019-07-07 18:59:04 -04:00
swgFileInputSettings - > setFileName ( new QString ( settings . m_fileName ) ) ;
2018-12-26 05:06:37 -05:00
}
QString deviceSettingsURL = QString ( " http://%1:%2/sdrangel/deviceset/%3/device/settings " )
. arg ( settings . m_reverseAPIAddress )
. arg ( settings . m_reverseAPIPort )
. arg ( settings . m_reverseAPIDeviceIndex ) ;
m_networkRequest . setUrl ( QUrl ( deviceSettingsURL ) ) ;
m_networkRequest . setHeader ( QNetworkRequest : : ContentTypeHeader , " application/json " ) ;
2019-11-12 12:46:21 -05:00
QBuffer * buffer = new QBuffer ( ) ;
2018-12-26 05:06:37 -05:00
buffer - > open ( ( QBuffer : : ReadWrite ) ) ;
buffer - > write ( swgDeviceSettings - > asJson ( ) . toUtf8 ( ) ) ;
buffer - > seek ( 0 ) ;
// Always use PATCH to avoid passing reverse API settings
2019-11-12 12:46:21 -05:00
QNetworkReply * reply = m_networkManager - > sendCustomRequest ( m_networkRequest , " PATCH " , buffer ) ;
buffer - > setParent ( reply ) ;
2018-12-26 05:06:37 -05:00
delete swgDeviceSettings ;
}
2019-07-07 18:59:04 -04:00
void FileInput : : webapiReverseSendStartStop ( bool start )
2018-12-26 05:06:37 -05:00
{
2019-03-25 10:21:17 -04:00
SWGSDRangel : : SWGDeviceSettings * swgDeviceSettings = new SWGSDRangel : : SWGDeviceSettings ( ) ;
2019-05-07 08:43:38 -04:00
swgDeviceSettings - > setDirection ( 0 ) ; // single Rx
2019-03-25 10:21:17 -04:00
swgDeviceSettings - > setOriginatorIndex ( m_deviceAPI - > getDeviceSetIndex ( ) ) ;
2019-07-07 18:59:04 -04:00
swgDeviceSettings - > setDeviceHwType ( new QString ( " FileInput " ) ) ;
2019-03-25 10:21:17 -04:00
2018-12-26 05:06:37 -05:00
QString deviceSettingsURL = QString ( " http://%1:%2/sdrangel/deviceset/%3/device/run " )
. arg ( m_settings . m_reverseAPIAddress )
. arg ( m_settings . m_reverseAPIPort )
. arg ( m_settings . m_reverseAPIDeviceIndex ) ;
m_networkRequest . setUrl ( QUrl ( deviceSettingsURL ) ) ;
2019-03-25 10:21:17 -04:00
m_networkRequest . setHeader ( QNetworkRequest : : ContentTypeHeader , " application/json " ) ;
2019-11-12 12:46:21 -05:00
QBuffer * buffer = new QBuffer ( ) ;
2019-03-25 10:21:17 -04:00
buffer - > open ( ( QBuffer : : ReadWrite ) ) ;
buffer - > write ( swgDeviceSettings - > asJson ( ) . toUtf8 ( ) ) ;
buffer - > seek ( 0 ) ;
2019-11-12 12:46:21 -05:00
QNetworkReply * reply ;
2018-12-26 05:06:37 -05:00
if ( start ) {
2019-11-12 12:46:21 -05:00
reply = m_networkManager - > sendCustomRequest ( m_networkRequest , " POST " , buffer ) ;
2018-12-26 05:06:37 -05:00
} else {
2019-11-12 12:46:21 -05:00
reply = m_networkManager - > sendCustomRequest ( m_networkRequest , " DELETE " , buffer ) ;
2018-12-26 05:06:37 -05:00
}
2019-06-14 10:58:09 -04:00
2019-11-12 12:46:21 -05:00
buffer - > setParent ( reply ) ;
2019-06-14 10:58:09 -04:00
delete swgDeviceSettings ;
2018-12-26 05:06:37 -05:00
}
2019-07-07 18:59:04 -04:00
void FileInput : : networkManagerFinished ( QNetworkReply * reply )
2018-12-26 05:06:37 -05:00
{
QNetworkReply : : NetworkError replyError = reply - > error ( ) ;
if ( replyError )
{
2019-07-07 18:59:04 -04:00
qWarning ( ) < < " FileInput::networkManagerFinished: "
2018-12-26 05:06:37 -05:00
< < " error( " < < ( int ) replyError
< < " ): " < < replyError
< < " : " < < reply - > errorString ( ) ;
2019-11-12 12:46:21 -05:00
}
else
{
QString answer = reply - > readAll ( ) ;
answer . chop ( 1 ) ; // remove last \n
qDebug ( " FileInput::networkManagerFinished: reply: \n %s " , answer . toStdString ( ) . c_str ( ) ) ;
2018-12-26 05:06:37 -05:00
}
2018-10-13 20:25:51 -04:00
2019-11-12 12:46:21 -05:00
reply - > deleteLater ( ) ;
2018-12-26 05:06:37 -05:00
}