diff --git a/src/backend/datasources/LiveDataSource.cpp b/src/backend/datasources/LiveDataSource.cpp index 33b47abec..14bb2584d 100644 --- a/src/backend/datasources/LiveDataSource.cpp +++ b/src/backend/datasources/LiveDataSource.cpp @@ -1,1608 +1,1606 @@ /*************************************************************************** File : LiveDataSource.cpp Project : LabPlot Description : Represents live data source -------------------------------------------------------------------- Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * 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; either version 2 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 for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/datasources/LiveDataSource.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/filters/FITSFilter.h" #include "backend/datasources/filters/BinaryFilter.h" #include "backend/core/Project.h" #include "kdefrontend/spreadsheet/PlotDataDialog.h" #include "commonfrontend/spreadsheet/SpreadsheetView.h" #include "kdefrontend/datasources/MQTTErrorWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*! \class LiveDataSource \brief Represents data stored in a file. Reading and writing is done with the help of appropriate I/O-filters. \ingroup datasources */ LiveDataSource::LiveDataSource(AbstractScriptingEngine* engine, const QString& name, bool loading) : Spreadsheet(engine, name, loading), m_fileType(Ascii), m_fileWatched(false), m_fileLinked(false), m_paused(false), m_prepared(false), m_keepLastValues(false), m_bytesRead(0), m_filter(nullptr), m_updateTimer(new QTimer(this)), -#ifdef HAVE_MQTT - m_willTimer(new QTimer(this)), -#endif m_fileSystemWatcher(nullptr), m_file(nullptr), m_localSocket(nullptr), m_tcpSocket(nullptr), m_udpSocket(nullptr), m_serialPort(nullptr), #ifdef HAVE_MQTT + m_willTimer(new QTimer(this)), m_client(new QMqttClient(this)), m_mqttTest(false), m_mqttRetain(false), m_mqttUseWill(false), m_mqttUseID(false), m_mqttUseAuthentication(false), m_mqttFirstConnectEstablished(false), #endif m_device(nullptr) { initActions(); connect(m_updateTimer, &QTimer::timeout, this, &LiveDataSource::read); #ifdef HAVE_MQTT m_willStatistics.fill(false, 15); connect(m_client, &QMqttClient::connected, this, &LiveDataSource::onMqttConnect); connect(m_willTimer, &QTimer::timeout, this, &LiveDataSource::setWillForMqtt); connect(m_client, &QMqttClient::errorChanged, this, &LiveDataSource::mqttErrorChanged); //connect(this, &LiveDataSource::mqttAllArrived, this, &LiveDataSource::onAllArrived ); #endif } LiveDataSource::~LiveDataSource() { //stop reading before deleting the objects pauseReading(); if (m_filter) delete m_filter; if (m_fileSystemWatcher) delete m_fileSystemWatcher; if (m_file) delete m_file; if (m_localSocket) delete m_localSocket; if (m_tcpSocket) delete m_tcpSocket; if (m_serialPort) delete m_serialPort; delete m_updateTimer; #ifdef HAVE_MQTT delete m_willTimer; m_client->disconnectFromHost(); delete m_client; #endif } /*! * depending on the update type, periodically or on data changes, starts the timer or activates the file watchers, respectively. */ void LiveDataSource::ready() { if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else watch(); } void LiveDataSource::initActions() { m_reloadAction = new QAction(QIcon::fromTheme("view-refresh"), i18n("Reload"), this); connect(m_reloadAction, &QAction::triggered, this, &LiveDataSource::read); m_toggleLinkAction = new QAction(i18n("Link the file"), this); m_toggleLinkAction->setCheckable(true); connect(m_toggleLinkAction, &QAction::triggered, this, &LiveDataSource::linkToggled); m_plotDataAction = new QAction(QIcon::fromTheme("office-chart-line"), i18n("Plot data"), this); connect(m_plotDataAction, &QAction::triggered, this, &LiveDataSource::plotData); } QWidget* LiveDataSource::view() const { if (!m_partView) m_partView = new SpreadsheetView(const_cast(this), true); return m_partView; } /*! * \brief Returns a list with the names of the available ports */ QStringList LiveDataSource::availablePorts() { QStringList ports; qDebug() << "available ports count:" << QSerialPortInfo::availablePorts().size(); for(const QSerialPortInfo& sp : QSerialPortInfo::availablePorts()) { ports.append(sp.portName()); qDebug() << sp.description(); qDebug() << sp.manufacturer(); qDebug() << sp.portName(); qDebug() << sp.serialNumber(); qDebug() << sp.systemLocation(); } return ports; } /*! * \brief Returns a list with the supported baud rates */ QStringList LiveDataSource::supportedBaudRates() { QStringList baudRates; for(const auto& baud : QSerialPortInfo::standardBaudRates()) baudRates.append(QString::number(baud)); return baudRates; } /*! * \brief Updates this data source at this moment */ void LiveDataSource::updateNow() { - if(m_sourceType != SourceType::Mqtt) { + if(m_sourceType != SourceType::MQTT) { m_updateTimer->stop(); read(); //restart the timer after update if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); } else { #ifdef HAVE_MQTT m_updateTimer->stop(); read(); if (m_updateType == TimeInterval && !m_paused) m_updateTimer->start(m_updateInterval); #endif } } /*! * \brief Continue reading from the live data source after it was paused. */ void LiveDataSource::continueReading() { m_paused = false; if (m_updateType == TimeInterval) m_updateTimer->start(m_updateInterval); else if (m_updateType == NewData) { - if(m_sourceType != LiveDataSource::SourceType::Mqtt) + if(m_sourceType != LiveDataSource::SourceType::MQTT) connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); #ifdef HAVE_MQTT else connect(this, &LiveDataSource::mqttAllArrived, this, &LiveDataSource::onAllArrived); #endif } } /*! * \brief Pause the reading of the live data source. */ void LiveDataSource::pauseReading() { m_paused = true; if (m_updateType == TimeInterval) m_updateTimer->stop(); else if (m_updateType == NewData) { - if(m_sourceType != LiveDataSource::SourceType::Mqtt) + if(m_sourceType != LiveDataSource::SourceType::MQTT) disconnect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); #ifdef HAVE_MQTT else disconnect(this, &LiveDataSource::mqttAllArrived, this, &LiveDataSource::onAllArrived); #endif } } /*! returns the list with all supported data file formats. */ QStringList LiveDataSource::fileTypes() { // see LiveDataSource::FileType return (QStringList()<< i18n("ASCII data") << i18n("Binary data") << i18n("Image") << i18n("Hierarchical Data Format 5 (HDF5)") << i18n("Network Common Data Format (NetCDF)") // << "CDF" << i18n("Flexible Image Transport System Data Format (FITS)") // << i18n("Sound") ); } void LiveDataSource::setFileName(const QString& name) { m_fileName = name; } QString LiveDataSource::fileName() const { return m_fileName; } /*! * \brief Sets the local socket's server name to name * \param name */ void LiveDataSource::setLocalSocketName(const QString& name) { m_localSocketName = name; } QString LiveDataSource::localSocketName() const { return m_localSocketName; } void LiveDataSource::setFileType(FileType type) { m_fileType = type; } LiveDataSource::FileType LiveDataSource::fileType() const { return m_fileType; } void LiveDataSource::setFilter(AbstractFileFilter* f) { m_filter = f; } AbstractFileFilter* LiveDataSource::filter() const { return m_filter; } /*! sets whether the file should be watched or not. In the first case the data source will be automatically updated on file changes. */ void LiveDataSource::setFileWatched(bool b) { m_fileWatched = b; } bool LiveDataSource::isFileWatched() const { return m_fileWatched; } /*! * \brief Sets whether we'll keep the last values or append it to the previous ones * \param keepLastValues */ void LiveDataSource::setKeepLastValues(bool keepLastValues) { m_keepLastValues = keepLastValues; } bool LiveDataSource::keepLastValues() const { return m_keepLastValues; } /*! * \brief Sets the serial port's baud rate * \param baudrate */ void LiveDataSource::setBaudRate(int baudrate) { m_baudRate = baudrate; } int LiveDataSource::baudRate() const { return m_baudRate; } /*! * \brief Sets the source's update interval to \c interval * \param interval */ void LiveDataSource::setUpdateInterval(int interval) { m_updateInterval = interval; if(!m_paused) m_updateTimer->start(m_updateInterval); } int LiveDataSource::updateInterval() const { return m_updateInterval; } /*! * \brief Sets how many values we should store * \param keepnvalues */ void LiveDataSource::setKeepNvalues(int keepnvalues) { m_keepNvalues = keepnvalues; } int LiveDataSource::keepNvalues() const { return m_keepNvalues; } /*! * \brief Sets the network socket's port to port * \param port */ void LiveDataSource::setPort(quint16 port) { m_port = port; } void LiveDataSource::setBytesRead(qint64 bytes) { m_bytesRead = bytes; } int LiveDataSource::bytesRead() const { return m_bytesRead; } int LiveDataSource::port() const { return m_port; } /*! * \brief Sets the serial port's name to name * \param name */ void LiveDataSource::setSerialPort(const QString& name) { m_serialPortName = name; } QString LiveDataSource::serialPortName() const { return m_serialPortName; } bool LiveDataSource::isPaused() const { return m_paused; } /*! * \brief Sets the sample rate to samplerate * \param samplerate */ void LiveDataSource::setSampleRate(int samplerate) { m_sampleRate = samplerate; } int LiveDataSource::sampleRate() const { return m_sampleRate; } /*! * \brief Sets the source's type to sourcetype * \param sourcetype */ void LiveDataSource::setSourceType(SourceType sourcetype) { m_sourceType = sourcetype; } LiveDataSource::SourceType LiveDataSource::sourceType() const { return m_sourceType; } /*! * \brief Sets the source's reading type to readingType * \param readingType */ void LiveDataSource::setReadingType(ReadingType readingType) { m_readingType = readingType; } LiveDataSource::ReadingType LiveDataSource::readingType() const { return m_readingType; } /*! * \brief Sets the source's update type to updatetype and handles this change * \param updatetype */ void LiveDataSource::setUpdateType(UpdateType updatetype) { if (updatetype == NewData) { m_updateTimer->stop(); - if (m_sourceType != SourceType::Mqtt) { + if (m_sourceType != SourceType::MQTT) { if (m_fileSystemWatcher == nullptr) watch(); else connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } #ifdef HAVE_MQTT - else if (m_sourceType == SourceType::Mqtt) { + else if (m_sourceType == SourceType::MQTT) { connect(this, &LiveDataSource::mqttAllArrived, this, &LiveDataSource::onAllArrived) ; } #endif } else { - if (m_sourceType != SourceType::Mqtt) { + if (m_sourceType != SourceType::MQTT) { if (m_fileSystemWatcher) disconnect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } #ifdef HAVE_MQTT - else if (m_sourceType == SourceType::Mqtt) { + else if (m_sourceType == SourceType::MQTT) { if (m_updateType == UpdateType::NewData) disconnect(this, &LiveDataSource::mqttAllArrived, this, &LiveDataSource::onAllArrived) ; } #endif } m_updateType = updatetype; } LiveDataSource::UpdateType LiveDataSource::updateType() const { return m_updateType; } /*! * \brief Sets the network socket's host * \param host */ void LiveDataSource::setHost(const QString& host) { m_host = host; } QString LiveDataSource::host() const { return m_host; } /*! sets whether only a link to the file is saved in the project file (\c b=true) or the whole content of the file (\c b=false). */ void LiveDataSource::setFileLinked(bool b) { m_fileLinked = b; } /*! returns \c true if only a link to the file is saved in the project file. \c false otherwise. */ bool LiveDataSource::isFileLinked() const { return m_fileLinked; } QIcon LiveDataSource::icon() const { QIcon icon; if (m_fileType == LiveDataSource::Ascii) icon = QIcon::fromTheme("text-plain"); else if (m_fileType == LiveDataSource::Binary) icon = QIcon::fromTheme("application-octet-stream"); else if (m_fileType == LiveDataSource::Image) icon = QIcon::fromTheme("image-x-generic"); // TODO: HDF5, NetCDF, FITS, etc. return icon; } QMenu* LiveDataSource::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); QAction* firstAction = 0; // if we're populating the context menu for the project explorer, then //there're already actions available there. Skip the first title-action //and insert the action at the beginning of the menu. if (menu->actions().size()>1) firstAction = menu->actions().at(1); menu->insertAction(firstAction, m_plotDataAction); menu->insertSeparator(firstAction); //TODO: doesnt' always make sense... // if (!m_fileWatched) // menu->insertAction(firstAction, m_reloadAction); // // m_toggleWatchAction->setChecked(m_fileWatched); // menu->insertAction(firstAction, m_toggleWatchAction); // // m_toggleLinkAction->setChecked(m_fileLinked); // menu->insertAction(firstAction, m_toggleLinkAction); return menu; } //############################################################################## //################################# SLOTS #################################### //############################################################################## /* * called periodically or on new data changes (file changed, new data in the socket, etc.) */ void LiveDataSource::read() { if (m_filter == nullptr) return; //initialize the device (file, socket, serial port), when calling this function for the first time if (!m_prepared) { switch (m_sourceType) { case FileOrPipe: m_file = new QFile(m_fileName); m_device = m_file; break; case NetworkTcpSocket: m_tcpSocket = new QTcpSocket(this); m_device = m_tcpSocket; m_tcpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); connect(m_tcpSocket, &QTcpSocket::readyRead, this, &LiveDataSource::readyRead); connect(m_tcpSocket, static_cast(&QTcpSocket::error), this, &LiveDataSource::tcpSocketError); break; case NetworkUdpSocket: m_udpSocket = new QUdpSocket(this); m_device = m_udpSocket; m_udpSocket->connectToHost(m_host, m_port); connect(m_udpSocket, &QUdpSocket::readyRead, this, &LiveDataSource::readyRead); connect(m_udpSocket, static_cast(&QUdpSocket::error), this, &LiveDataSource::tcpSocketError); break; case LocalSocket: m_localSocket = new QLocalSocket(this); m_device = m_localSocket; m_localSocket->connectToServer(m_localSocketName, QLocalSocket::ReadOnly); connect(m_localSocket, &QLocalSocket::readyRead, this, &LiveDataSource::readyRead); connect(m_localSocket, static_cast(&QLocalSocket::error), this, &LiveDataSource::localSocketError); break; case SerialPort: m_serialPort = new QSerialPort; m_device = m_serialPort; m_serialPort->setBaudRate(m_baudRate); m_serialPort->setPortName(m_serialPortName); connect(m_serialPort, static_cast(&QSerialPort::error), this, &LiveDataSource::serialPortError); connect(m_serialPort, &QSerialPort::readyRead, this, &LiveDataSource::readyRead); break; - case Mqtt:{ + case MQTT:{ #ifdef HAVE_MQTT qDebug()<<"Trying to connect 1"; m_client->connectToHost(); #endif break; } } m_prepared = true; } qint64 bytes = 0; switch (m_sourceType) { case FileOrPipe: switch (m_fileType) { case Ascii: qDebug() << "Reading live ascii file.." ; if (m_readingType == LiveDataSource::ReadingType::WholeFile) { dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, 0); } else { bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); m_bytesRead += bytes; } qDebug() << "Read " << bytes << " bytes, in total: " << m_bytesRead; break; case Binary: //bytes = dynamic_cast(m_filter)->readFromLiveDevice(*m_file, this, m_bytesRead); m_bytesRead += bytes; case Image: case HDF5: case NETCDF: case FITS: break; } break; case NetworkTcpSocket: DEBUG("reading from a TCP socket"); qDebug() << "reading from a TCP socket before abort: " << m_tcpSocket->state(); m_tcpSocket->abort(); m_tcpSocket->connectToHost(m_host, m_port, QIODevice::ReadOnly); qDebug() << "reading from a TCP socket after reconnect: " << m_tcpSocket->state(); break; case NetworkUdpSocket: DEBUG("reading from a UDP socket"); qDebug() << "reading from a UDP socket before abort: " << m_udpSocket->state(); m_udpSocket->abort(); m_udpSocket->connectToHost(m_host, m_port); qDebug() << "reading from a UDP socket after reconnect: " << m_udpSocket->state(); break; case LocalSocket: DEBUG("reading from a local socket"); qDebug() << "reading from a local socket before abort: " << m_localSocket->state(); m_localSocket->abort(); m_localSocket->connectToServer(m_localSocketName, QLocalSocket::ReadOnly); qDebug() << "reading from a local socket after reconnect: " << m_localSocket->state(); break; case SerialPort: DEBUG("reading from the serial port"); m_serialPort->setBaudRate(m_baudRate); m_serialPort->setPortName(m_serialPortName); m_device = m_serialPort; //TODO break; - case Mqtt: { + case MQTT: { #ifdef HAVE_MQTT qDebug()<<"Trying to connect 2"; if(m_client->state() == QMqttClient::ClientState::Connected) while(checkAllArrived()) onAllArrived(); #endif break; } } } /*! * Slot for the signal that is emitted once every time new data is available for reading from the device. * It will only be emitted again once new data is available, such as when a new payload of network data has arrived on the network socket, * or when a new block of data has been appended to your device. */ void LiveDataSource::readyRead() { DEBUG("Got new data from the device"); qDebug()<< "Got new data from the device"; if (m_fileType == Ascii) dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_device, this); // else if (m_fileType == Binary) // dynamic_cast(m_filter)->readFromLiveDeviceNotFile(*m_device, this); //since we won't have the timer to call read() where we create new connections //for sequencial devices in read() we just request data/connect to servers if (m_updateType == NewData) read(); } void LiveDataSource::localSocketError(QLocalSocket::LocalSocketError socketError) { Q_UNUSED(socketError); /*disconnect(m_localSocket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError))); disconnect(m_localSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));*/ /*switch (socketError) { case QLocalSocket::ServerNotFoundError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket was not found. Please check the socket name.")); break; case QLocalSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The connection was refused by the peer")); break; case QLocalSocket::PeerClosedError: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The socket has closed the connection.")); break; default: QMessageBox::critical(0, i18n("Local Socket Error"), i18n("The following error occurred: %1.").arg(m_localSocket->errorString())); }*/ } void LiveDataSource::tcpSocketError(QAbstractSocket::SocketError socketError) { Q_UNUSED(socketError); /*switch (socketError) { case QAbstractSocket::ConnectionRefusedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The connection was refused by the peer. Make sure the server is running and check the host name and port settings.")); break; case QAbstractSocket::RemoteHostClosedError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The remote host closed the connection.")); break; case QAbstractSocket::HostNotFoundError: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The host was not found. Please check the host name and port settings.")); break; default: QMessageBox::critical(0, i18n("TCP Socket Error"), i18n("The following error occurred: %1.").arg(m_tcpSocket->errorString())); }*/ } void LiveDataSource::serialPortError(QSerialPort::SerialPortError serialPortError) { switch (serialPortError) { case QSerialPort::DeviceNotFoundError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to open the device.")); break; case QSerialPort::PermissionError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to open the device. Please check your permissions on this device.")); break; case QSerialPort::OpenError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Device already opened.")); break; case QSerialPort::NotOpenError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device is not opened.")); break; case QSerialPort::ReadError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data.")); break; case QSerialPort::ResourceError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("Failed to read data. The device is removed.")); break; case QSerialPort::TimeoutError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The device timed out.")); break; #ifndef _MSC_VER //MSVC complains about the usage of deprecated enums, g++ and clang complain about missing enums case QSerialPort::ParityError: case QSerialPort::FramingError: case QSerialPort::BreakConditionError: #endif case QSerialPort::WriteError: case QSerialPort::UnsupportedOperationError: case QSerialPort::UnknownError: QMessageBox::critical(0, i18n("Serial Port Error"), i18n("The following error occurred: %1.").arg(m_serialPort->errorString())); break; case QSerialPort::NoError: break; } } void LiveDataSource::watchToggled() { m_fileWatched = !m_fileWatched; watch(); project()->setChanged(true); } void LiveDataSource::linkToggled() { m_fileLinked = !m_fileLinked; project()->setChanged(true); } //watch the file upon reading for changes if required void LiveDataSource::watch() { if (m_fileWatched) { if (!m_fileSystemWatcher) { m_fileSystemWatcher = new QFileSystemWatcher; connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &LiveDataSource::read); } if ( !m_fileSystemWatcher->files().contains(m_fileName) ) m_fileSystemWatcher->addPath(m_fileName); } else { if (m_fileSystemWatcher) m_fileSystemWatcher->removePath(m_fileName); } } /*! returns a string containing the general information about the file \c name and some content specific information (number of columns and lines for ASCII, color-depth for images etc.). */ QString LiveDataSource::fileInfoString(const QString &name) { QString infoString; QFileInfo fileInfo; QString fileTypeString; QIODevice *file = new QFile(name); QString fileName; #ifdef Q_OS_WIN if (name.at(1) != QLatin1Char(':')) { fileName = QDir::homePath() + name; } else { fileName = name; } #else if (name.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + name; else fileName = name; #endif if(file==0) file = new QFile(fileName); if (file->open(QIODevice::ReadOnly)) { QStringList infoStrings; //general information about the file infoStrings << "" + fileName + "
"; fileInfo.setFile(fileName); infoStrings << i18n("Readable: %1", fileInfo.isReadable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Writable: %1", fileInfo.isWritable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Executable: %1", fileInfo.isExecutable() ? i18n("yes") : i18n("no")); infoStrings << i18n("Created: %1", fileInfo.created().toString()); infoStrings << i18n("Last modified: %1", fileInfo.lastModified().toString()); infoStrings << i18n("Last read: %1", fileInfo.lastRead().toString()); infoStrings << i18n("Owner: %1", fileInfo.owner()); infoStrings << i18n("Group: %1", fileInfo.group()); infoStrings << i18n("Size: %1", i18np("%1 cByte", "%1 cBytes", fileInfo.size())); #ifdef HAVE_FITS if (fileName.endsWith(QLatin1String(".fits"))) { infoStrings << i18n("Images: %1", QString::number(FITSFilter::imagesCount(fileName) )); infoStrings << i18n("Tables: %1", QString::number(FITSFilter::tablesCount(fileName) )); } #endif // file type and type specific information about the file #ifdef Q_OS_LINUX QProcess *proc = new QProcess(); QStringList args; args<<"-b"<start( "file", args); if(proc->waitForReadyRead(1000) == false) infoStrings << i18n("Could not open file %1 for reading.", fileName); else { fileTypeString = proc->readLine(); if( fileTypeString.contains(i18n("cannot open")) ) fileTypeString=""; else { fileTypeString.remove(fileTypeString.length()-1,1); // remove '\n' } } infoStrings << i18n("File type: %1", fileTypeString); #endif //TODO depending on the file type, generate additional information about the file: //Number of lines for ASCII, color-depth for images etc. Use the specific filters here. // port the old labplot1.6 code. if( fileTypeString.contains("ASCII")) { infoStrings << "
"; //TODO: consider choosen separator infoStrings << i18n("Number of columns: %1", AsciiFilter::columnNumber(fileName)); infoStrings << i18n("Number of lines: %1", AsciiFilter::lineNumber(fileName)); } infoString += infoStrings.join("
"); } else infoString += i18n("Could not open file %1 for reading.", fileName); return infoString; } void LiveDataSource::plotData() { PlotDataDialog* dlg = new PlotDataDialog(this); dlg->exec(); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## /*! Saves as XML. */ void LiveDataSource::save(QXmlStreamWriter* writer) const { writer->writeStartElement("LiveDataSource"); writeBasicAttributes(writer); writeCommentElement(writer); //general writer->writeStartElement("general"); writer->writeAttribute("fileName", m_fileName); writer->writeAttribute("fileType", QString::number(m_fileType)); writer->writeAttribute("fileWatched", QString::number(m_fileWatched)); writer->writeAttribute("fileLinked", QString::number(m_fileLinked)); writer->writeAttribute("updateType", QString::number(m_updateType)); writer->writeAttribute("readingType", QString::number(m_readingType)); writer->writeAttribute("sourceType", QString::number(m_sourceType)); writer->writeAttribute("keepValues", QString::number(m_keepNvalues)); if (m_updateType == TimeInterval) writer->writeAttribute("updateInterval", QString::number(m_updateInterval)); if (m_readingType != TillEnd) writer->writeAttribute("sampleRate", QString::number(m_sampleRate)); switch (m_sourceType) { case SerialPort: writer->writeAttribute("baudRate", QString::number(m_baudRate)); writer->writeAttribute("serialPortName", m_serialPortName); break; case NetworkTcpSocket: case NetworkUdpSocket: writer->writeAttribute("host", m_host); writer->writeAttribute("port", QString::number(m_port)); break; case FileOrPipe: break; case LocalSocket: break; - case Mqtt:{ + case MQTT:{ #ifdef HAVE_MQTT writer->writeAttribute("host", m_client->hostname()); writer->writeAttribute("port", QString::number(m_client->port())); writer->writeAttribute("username", m_client->username()); writer->writeAttribute("pasword", m_client->password()); writer->writeAttribute("clientId", m_client->clientId()); writer->writeAttribute("subscriptionNumber", QString::number(m_subscriptions.count()) ); for(int i = 0; iwriteAttribute("subscription"+QString::number(i), m_subscriptions[i]); writer->writeAttribute("subscription"+QString::number(i)+"Qos", QString::number(m_topicMap[m_subscriptions[i]])); } writer->writeAttribute("useRetain", QString::number(m_mqttRetain)); writer->writeAttribute("useWill", QString::number(m_mqttUseWill)); writer->writeAttribute("willTopic", m_willTopic); writer->writeAttribute("willOwnMessage", m_willOwnMessage); writer->writeAttribute("willQoS", QString::number(m_willQoS)); writer->writeAttribute("willRetain", QString::number(m_willRetain)); writer->writeAttribute("willMessageType", QString::number(static_cast(m_willMessageType))); writer->writeAttribute("willUpdateType", QString::number(static_cast(m_willUpdateType))); writer->writeAttribute("willTimeInterval", QString::number(m_willTimeInterval)); for( int i = 0; i < m_willStatistics.count(); ++i){ writer->writeAttribute("willStatistics"+QString::number(i), QString::number(m_willStatistics[i])); } writer->writeAttribute("useID", QString::number(m_mqttUseID)); writer->writeAttribute("useAuthentication", QString::number(m_mqttUseAuthentication)); #endif break; } default: break; } writer->writeEndElement(); //filter m_filter->save(writer); //columns if (!m_fileLinked) { for (auto* col : children(IncludeHidden)) col->save(writer); } writer->writeEndElement(); // "LiveDataSource" } /*! Loads from XML. */ bool LiveDataSource::load(XmlStreamReader* reader, bool preview) { if (!readBasicAttributes(reader)) return false; QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "LiveDataSource") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (reader->name() == "general") { attribs = reader->attributes(); str = attribs.value("fileName").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileName'")); else m_fileName = str; str = attribs.value("fileType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileType'")); else m_fileType = (FileType)str.toInt(); str = attribs.value("fileWatched").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileWatched'")); else m_fileWatched = str.toInt(); str = attribs.value("fileLinked").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'fileLinked'")); else m_fileLinked = str.toInt(); str = attribs.value("updateType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'updateType'")); else m_updateType = static_cast(str.toInt()); str = attribs.value("sourceType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'sourceType'")); else m_sourceType = static_cast(str.toInt()); str = attribs.value("readingType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'readingType'")); else m_readingType = static_cast(str.toInt()); if (m_updateType == TimeInterval) { str = attribs.value("updateInterval").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'updateInterval'")); else m_updateInterval = str.toInt(); } if (m_readingType != TillEnd) { str = attribs.value("sampleRate").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'sampleRate'")); else m_sampleRate = str.toInt(); } switch (m_sourceType) { case SerialPort: str = attribs.value("baudRate").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'baudRate'")); else m_baudRate = str.toInt(); str = attribs.value("serialPortName").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'serialPortName'")); else m_serialPortName = str; break; case NetworkTcpSocket: case NetworkUdpSocket: str = attribs.value("host").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'host'")); else m_host = str; str = attribs.value("port").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'port'")); else m_host = str; break; - case Mqtt: { + case MQTT: { #ifdef HAVE_MQTT str = attribs.value("host").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'host'")); else m_client->setHostname(str); str =attribs.value("port").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'port'")); else m_client->setPort(str.toUInt()); str = attribs.value("useAuthentication").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'useAuthentication")); else m_mqttUseAuthentication = str.toInt(); if(m_mqttUseAuthentication) { str =attribs.value("username").toString(); if(!str.isEmpty()) m_client->setUsername(str); str =attribs.value("password").toString(); if(!str.isEmpty()) m_client->setPassword(str); } str = attribs.value("useID").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'useID")); else m_mqttUseID = str.toInt(); if(m_mqttUseID) { str =attribs.value("clientId").toString(); if(!str.isEmpty()) m_client->setClientId(str); } int subscribtions; str =attribs.value("subscriptionNumber").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'subscriptionNumber'")); else subscribtions = str.toInt(); for (int i = 0; i < subscribtions; i++) { str =attribs.value("subscription"+QString::number(i)).toString(); if(!str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'subscription"+QString::number(i)+"'")); else { m_subscriptions.push_back(str); } str =attribs.value("subscription"+QString::number(i)+"Qos").toString(); if(!str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'subscription"+QString::number(i)+"Qos'")); else { m_topicMap[m_subscriptions[i]] = str.toUInt(); } } str =attribs.value("useRetain").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'useRetain'")); else m_mqttRetain = str.toInt(); str =attribs.value("useWill").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'useWill'")); else m_mqttUseWill = str.toInt(); str =attribs.value("willTopic").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'willTopic'")); else m_willTopic = str; str =attribs.value("willOwnMessage").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'willOwnMessage'")); else m_willOwnMessage = str; str =attribs.value("willQoS").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'willQoS'")); else m_willQoS = str.toUInt(); str =attribs.value("willRetain").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'willRetain'")); else m_willRetain = str.toInt(); str =attribs.value("willMessageType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'willMessageType'")); else m_willMessageType = static_cast(str.toInt()); str =attribs.value("willUpdateType").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'willUpdateType'")); else m_willUpdateType = static_cast(str.toInt()); str =attribs.value("willTimeInterval").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'willTimeInterval'")); else m_willTimeInterval = str.toInt(); for( int i = 0; i < m_willStatistics.count(); ++i){ str =attribs.value("willStatistics"+QString::number(i)).toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.arg("'willTimeInterval'")); else m_willStatistics[i] = str.toInt(); } #endif break; } case FileOrPipe: break; case LocalSocket: break; default: break; } } else if (reader->name() == "asciiFilter") { m_filter = new AsciiFilter(); if (!m_filter->load(reader)) return false; } else if(reader->name() == "column") { Column* column = new Column("", AbstractColumn::Text); if (!column->load(reader, preview)) { delete column; setColumnCount(0); return false; } addChild(column); } else {// unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } //read the content of the file if it was only linked if (m_fileLinked) this->read(); return !reader->hasError(); } #ifdef HAVE_MQTT void LiveDataSource::setMqttClient(const QString& host, const quint16& port) { m_client->setHostname(host); m_client->setPort(port); } void LiveDataSource::setMqttClientAuthentication(const QString& username, const QString& password) { m_client->setUsername(username); m_client->setPassword(password); } void LiveDataSource::setMqttClientId(const QString &Id){ m_client->setClientId(Id); } void LiveDataSource::addMqttSubscriptions(const QMqttTopicFilter& filter, const quint8& qos) { m_topicMap[filter] = qos; } void LiveDataSource::onMqttConnect() { if(m_client->error() == QMqttClient::NoError) { if(!m_mqttFirstConnectEstablished) { qDebug()<<"connection made in live data source"; QMapIterator i(m_topicMap); while(i.hasNext()) { i.next(); QMqttSubscription *temp = m_client->subscribe(i.key(), i.value()); if(temp) { qDebug()<topic()<<" "<qos(); m_messageArrived[temp->topic().filter()] = false; m_subscriptions.push_back(temp->topic().filter()); connect(temp, &QMqttSubscription::messageReceived, this, &LiveDataSource::mqttSubscribtionMessageReceived); } } m_mqttFirstConnectEstablished = true; emit mqttSubscribed(); } else { qDebug() << "Resubscribing after will set"; QMapIterator i(m_topicMap); while(i.hasNext()) { i.next(); QMqttSubscription *temp = m_client->subscribe(i.key(), i.value()); if(temp) { qDebug()<topic()<<" "<qos(); connect(temp, &QMqttSubscription::messageReceived, this, &LiveDataSource::mqttSubscribtionMessageReceived); } else qDebug()<<"Couldn't subscribe after will change"; } } } } void LiveDataSource::mqttSubscribtionMessageReceived(const QMqttMessage& msg) { if(!msg.retain() || (msg.retain() && m_mqttRetain) ) { qDebug()<<"message received from "< i(m_messageArrived); while(i.hasNext()) { i.next(); if(i.value() == false ) { check = false; break; } } if (check == true) emit mqttAllArrived(); } } void LiveDataSource::onAllArrived() { qDebug()<<"all arrived"; if (m_fileType == Ascii) { qDebug()<<"Ascii ok"; QMapIterator> k(m_messagePuffer); qDebug()<<"first iterator created"; bool ok = true; while(k.hasNext()){ k.next(); qDebug()<<"investigating"<> i(m_messagePuffer); while(i.hasNext()) { i.next(); if(!i.value().isEmpty()) { QMqttMessage temp_msg = m_messagePuffer[i.key()].takeFirst(); qDebug()<<"Start reading from "<(m_filter)->readFromMqtt(QString::fromStdString(temp_msg.payload().data()), temp_msg.topic().name(), this); + dynamic_cast(m_filter)->readFromMqtt(temp_msg.payload(), temp_msg.topic().name(), this); qDebug()<<"readfrommqtt occured"; } } } qDebug()<<"start checking"; QMapIterator j(m_messageArrived); while(j.hasNext()) { j.next(); if(m_messagePuffer[j.key()].isEmpty()) { m_messageArrived[j.key()] = false; } } qDebug()<<"end checking"; } } int LiveDataSource::topicNumber() { return m_subscriptions.count(); } int LiveDataSource::topicIndex(const QString& topic) { return m_subscriptions.indexOf(topic, 0); } bool LiveDataSource::checkAllArrived() { bool check = true; QMapIterator i(m_messageArrived); while(i.hasNext()) { i.next(); if(i.value() == false ) { check = false; break; } } return check; } void LiveDataSource::setMqttWillUse(bool use) { m_mqttUseWill = use; if(use == false) m_willTimer->stop(); } bool LiveDataSource::mqttWillUse() const{ return m_mqttUseWill; } void LiveDataSource::setWillTopic(const QString& topic) { m_willTopic = topic; } QString LiveDataSource::willTopic() const{ return m_willTopic; } void LiveDataSource::setWillRetain(bool retain) { m_willRetain = retain; } bool LiveDataSource::willRetain() const { return m_willRetain; } void LiveDataSource::setWillQoS(quint8 QoS) { m_willQoS = QoS; } quint8 LiveDataSource::willQoS() const { return m_willQoS; } void LiveDataSource::setWillMessageType(WillMessageType messageType) { m_willMessageType = messageType; } LiveDataSource::WillMessageType LiveDataSource::willMessageType() const { return m_willMessageType; } void LiveDataSource::setWillOwnMessage(const QString& ownMessage) { m_willOwnMessage = ownMessage; } QString LiveDataSource::willOwnMessage() const { return m_willOwnMessage; } QVector LiveDataSource::topicVector() const { return m_subscriptions; } void LiveDataSource::setWillForMqtt() { if(m_mqttUseWill && (m_client->state() == QMqttClient::ClientState::Connected) ) { qDebug() << "Disconnecting from host"; m_client->disconnectFromHost(); m_client->setWillQoS(m_willQoS); qDebug()<<"Will QoS" << m_willQoS; m_client->setWillRetain(m_willRetain); qDebug()<<"Will retain" << m_willRetain; m_client->setWillTopic(m_willTopic); qDebug()<<"Will Topic" << m_willTopic; switch (m_willMessageType) { case WillMessageType::OwnMessage: m_client->setWillMessage(m_willOwnMessage.toUtf8()); qDebug()<<"Will own message" << m_willOwnMessage; break; case WillMessageType::Statistics: { AsciiFilter * asciiFilter = dynamic_cast(m_filter); - if(asciiFilter->mqttColumnMode(m_willTopic, this) == AbstractColumn::ColumnMode::Integer || - asciiFilter->mqttColumnMode(m_willTopic, this) == AbstractColumn::ColumnMode::Numeric) { + if((asciiFilter->mqttColumnMode(m_willTopic, this) == AbstractColumn::ColumnMode::Integer) || + (asciiFilter->mqttColumnMode(m_willTopic, this) == AbstractColumn::ColumnMode::Numeric)) { m_client->setWillMessage(asciiFilter->mqttColumnStatistics(m_willTopic, this).toUtf8()); qDebug() << "Will statistics message: "<< QString(m_client->willMessage()); } else { m_client->setWillMessage(QString("").toUtf8()); qDebug() << "Will statistics message: "<< QString(m_client->willMessage()); } break; } case WillMessageType::LastMessage: m_client->setWillMessage(m_willLastMessage.toUtf8()); qDebug()<<"Will last message:\n" << m_willLastMessage; break; default: break; } m_client->connectToHost(); qDebug()<< "Reconnect to host"; } } LiveDataSource::WillUpdateType LiveDataSource::willUpdateType() const{ return m_willUpdateType; } void LiveDataSource::setWillUpdateType(WillUpdateType updateType) { m_willUpdateType = updateType; } int LiveDataSource::willTimeInterval() const{ return m_willTimeInterval; } void LiveDataSource::setWillTimeInterval(int interval) { m_willTimeInterval = interval; } void LiveDataSource::clearLastMessage() { m_willLastMessage.clear(); } void LiveDataSource::addWillStatistics(WillStatistics statistic){ m_willStatistics[static_cast(statistic)] = true; } void LiveDataSource::removeWillStatistics(WillStatistics statistic) { m_willStatistics[static_cast(statistic)] = false; } QVector LiveDataSource::willStatistics() const{ return m_willStatistics; } void LiveDataSource::startWillTimer() const{ if(m_willUpdateType == WillUpdateType::TimePeriod) m_willTimer->start(m_willTimeInterval); } void LiveDataSource::stopWillTimer() const{ m_willTimer->stop(); } void LiveDataSource::setMqttRetain(bool retain) { m_mqttRetain = retain; } bool LiveDataSource::mqttRetain() const { return m_mqttRetain; } void LiveDataSource::mqttErrorChanged(QMqttClient::ClientError clientError) { MQTTErrorWidget* errorWidget = new MQTTErrorWidget(clientError, this); errorWidget->show(); } QString LiveDataSource::clientHostName() const{ return m_client->hostname(); } quint16 LiveDataSource::clientPort() const { return m_client->port(); } QString LiveDataSource::clientPassword() const{ return m_client->password(); } QString LiveDataSource::clientUserName() const{ return m_client->username(); } QString LiveDataSource::clientID () const{ return m_client->clientId(); } void LiveDataSource::setMQTTUseID(bool use) { m_mqttUseID = use; } bool LiveDataSource::mqttUseID() const { return m_mqttUseID; } void LiveDataSource::setMQTTUseAuthentication(bool use) { m_mqttUseAuthentication = use; } bool LiveDataSource::mqttUseAuthentication() const { return m_mqttUseAuthentication; } QVector LiveDataSource::mqttSubscribtions() const { return m_subscriptions; } #endif diff --git a/src/backend/datasources/LiveDataSource.h b/src/backend/datasources/LiveDataSource.h index 9c68bcc66..294a9366a 100644 --- a/src/backend/datasources/LiveDataSource.h +++ b/src/backend/datasources/LiveDataSource.h @@ -1,359 +1,359 @@ /*************************************************************************** File : LiveDataSource.h Project : LabPlot Description : File data source -------------------------------------------------------------------- Copyright : (C) 2012-2013 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * 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; either version 2 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 for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef LIVEDATASOURCE_H #define LIVEDATASOURCE_H #include "backend/spreadsheet/Spreadsheet.h" #include "backend/matrix/Matrix.h" #include #include #include #include #ifdef HAVE_MQTT #include #include #endif #include class QString; class AbstractFileFilter; class QFileSystemWatcher; class QAction; class QTcpSocket; class QUdpSocket; class QFile; class LiveDataSource : public Spreadsheet { Q_OBJECT Q_ENUMS(FileType) public: enum FileType {Ascii, Binary, Image, HDF5, NETCDF, FITS}; enum SourceType { FileOrPipe = 0, NetworkTcpSocket, NetworkUdpSocket, LocalSocket, SerialPort, - Mqtt + MQTT }; enum UpdateType { TimeInterval = 0, NewData }; enum ReadingType { ContinuousFixed = 0, FromEnd, TillEnd, WholeFile }; #ifdef HAVE_MQTT enum WillMessageType { OwnMessage = 0, Statistics, LastMessage }; enum WillUpdateType { TimePeriod = 0, OnClick }; enum WillStatistics { Minimum = 0, Maximum, ArithmeticMean, GeometricMean, HarmonicMean, ContraharmonicMean, Median, Variance, StandardDeviation, MeanDeviation, MeanDeviationAroundMedian, MedianDeviation, Skewness, Kurtosis, Entropy }; #endif LiveDataSource(AbstractScriptingEngine*, const QString& name, bool loading = false); ~LiveDataSource() override; void ready(); static QStringList supportedBaudRates(); static QStringList availablePorts(); static QStringList fileTypes(); static QString fileInfoString(const QString&); void setFileType(const FileType); FileType fileType() const; UpdateType updateType() const; void setUpdateType(UpdateType); SourceType sourceType() const; void setSourceType(SourceType); ReadingType readingType() const; void setReadingType(ReadingType); int sampleRate() const; void setSampleRate(int); void setBytesRead(qint64 bytes); int bytesRead() const; int port() const; void setPort(quint16); bool isPaused() const; void setSerialPort(const QString& name); QString serialPortName() const; QString host() const; void setHost(const QString&); int baudRate() const; void setBaudRate(int); void setUpdateInterval(int); int updateInterval() const; void setKeepNvalues(int); int keepNvalues() const; void setKeepLastValues(bool); bool keepLastValues() const; void setFileWatched(bool); bool isFileWatched() const; void setFileLinked(bool); bool isFileLinked() const; void setFileName(const QString&); QString fileName() const; void setLocalSocketName(const QString&); QString localSocketName() const; #ifdef HAVE_MQTT void setMqttClient(const QString&, const quint16&); void setMqttClientAuthentication(const QString&, const QString&); void setMqttClientId(const QString&); QMqttClient mqttClient() const; void addMqttSubscriptions(const QMqttTopicFilter&, const quint8&); QVector mqttSubscribtions() const; QString clientHostName() const; quint16 clientPort() const; QString clientPassword() const; QString clientUserName() const; QString clientID () const; #endif void updateNow(); void pauseReading(); void continueReading(); void setFilter(AbstractFileFilter*); AbstractFileFilter* filter() const; QIcon icon() const override; QMenu* createContextMenu() override; QWidget* view() const override; void save(QXmlStreamWriter*) const override; bool load(XmlStreamReader*, bool preview) override; #ifdef HAVE_MQTT int topicNumber(); int topicIndex(const QString&); QVector topicVector() const; bool checkAllArrived(); void setMqttWillUse(bool); bool mqttWillUse() const; void setWillTopic(const QString&); QString willTopic() const; void setWillRetain(bool); bool willRetain() const; void setWillQoS(quint8); quint8 willQoS() const; void setWillMessageType(WillMessageType); WillMessageType willMessageType() const; void setWillOwnMessage(const QString&); QString willOwnMessage() const; WillUpdateType willUpdateType() const; void setWillUpdateType(WillUpdateType); int willTimeInterval() const; void setWillTimeInterval(int); void startWillTimer() const; void stopWillTimer() const; void setWillForMqtt() ; void setMqttRetain(bool); bool mqttRetain() const; void setMQTTUseID(bool); bool mqttUseID() const; void setMQTTUseAuthentication(bool); bool mqttUseAuthentication() const; void clearLastMessage(); void addWillStatistics(WillStatistics); void removeWillStatistics(WillStatistics); QVector willStatistics() const; #endif private: void initActions(); void watch(); QString m_fileName; QString m_serialPortName; QString m_localSocketName; QString m_host; FileType m_fileType; UpdateType m_updateType; SourceType m_sourceType; ReadingType m_readingType; bool m_fileWatched; bool m_fileLinked; bool m_paused; bool m_prepared; bool m_keepLastValues; int m_sampleRate; int m_keepNvalues; int m_updateInterval; quint16 m_port; int m_baudRate; qint64 m_bytesRead; AbstractFileFilter* m_filter; QTimer* m_updateTimer; QFileSystemWatcher* m_fileSystemWatcher; QFile* m_file; QLocalSocket* m_localSocket; QTcpSocket* m_tcpSocket; QUdpSocket* m_udpSocket; QSerialPort* m_serialPort; QIODevice* m_device; QAction* m_reloadAction; QAction* m_toggleLinkAction; QAction* m_showEditorAction; QAction* m_showSpreadsheetAction; QAction* m_plotDataAction; #ifdef HAVE_MQTT QMqttClient* m_client; QMap m_topicMap; QMap m_messageArrived; QMap> m_messagePuffer; QVector m_subscriptions; bool m_mqttTest; bool m_mqttUseWill; QString m_willMessage; QString m_willTopic; bool m_willRetain; quint8 m_willQoS; WillMessageType m_willMessageType; QString m_willOwnMessage; QString m_willLastMessage; QTimer* m_willTimer; int m_willTimeInterval; WillUpdateType m_willUpdateType; QVector m_willStatistics; bool m_mqttFirstConnectEstablished; bool m_mqttRetain; bool m_mqttUseID; bool m_mqttUseAuthentication; #endif public slots: void read(); private slots: void watchToggled(); void linkToggled(); void plotData(); void readyRead(); void localSocketError(QLocalSocket::LocalSocketError); void tcpSocketError(QAbstractSocket::SocketError); void serialPortError(QSerialPort::SerialPortError); #ifdef HAVE_MQTT void onMqttConnect(); void mqttSubscribtionMessageReceived(const QMqttMessage&); void onAllArrived(); void mqttErrorChanged(QMqttClient::ClientError); #endif signals: #ifdef HAVE_MQTT void mqttAllArrived(); void mqttSubscribed(); #endif }; #endif diff --git a/src/kdefrontend/datasources/ImportFileWidget.cpp b/src/kdefrontend/datasources/ImportFileWidget.cpp index a41f4d704..435e2fcaf 100644 --- a/src/kdefrontend/datasources/ImportFileWidget.cpp +++ b/src/kdefrontend/datasources/ImportFileWidget.cpp @@ -1,1913 +1,1914 @@ /*************************************************************************** File : ImportFileWidget.cpp Project : LabPlot Description : import file data widget -------------------------------------------------------------------- Copyright : (C) 2009-2017 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2017 Fabian Kristof (fkristofszabolcs@gmail.com) ***************************************************************************/ /*************************************************************************** * * * 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; either version 2 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 for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "ImportFileWidget.h" #include "FileInfoDialog.h" #include "backend/datasources/filters/AsciiFilter.h" #include "backend/datasources/filters/BinaryFilter.h" #include "backend/datasources/filters/HDF5Filter.h" #include "backend/datasources/filters/NetCDFFilter.h" #include "backend/datasources/filters/ImageFilter.h" #include "backend/datasources/filters/FITSFilter.h" #include "AsciiOptionsWidget.h" #include "BinaryOptionsWidget.h" #include "HDF5OptionsWidget.h" #include "ImageOptionsWidget.h" #include "NetCDFOptionsWidget.h" #include "FITSOptionsWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_MQTT #include #include #include #include #include #include #include #endif /*! \class ImportFileWidget \brief Widget for importing data from a file. \ingroup kdefrontend */ ImportFileWidget::ImportFileWidget(QWidget* parent, const QString& fileName) : QWidget(parent), m_fileName(fileName), m_fileEmpty(false), m_liveDataSource(true), #ifdef HAVE_MQTT m_mqttReadyForPreview (false), m_mqttSubscribeButton (true), m_editing(false), #endif m_suppressRefresh(false) { ui.setupUi(this); #ifdef HAVE_MQTT m_timer = new QTimer(this); m_timer->setInterval(10000); #endif QCompleter* completer = new QCompleter(this); completer->setModel(new QDirModel); ui.leFileName->setCompleter(completer); ui.cbFileType->addItems(LiveDataSource::fileTypes()); QStringList filterItems; filterItems << i18n("Automatic") << i18n("Custom"); ui.cbFilter->addItems(filterItems); // file type specific option widgets QWidget* asciiw = new QWidget(); m_asciiOptionsWidget = std::unique_ptr(new AsciiOptionsWidget(asciiw)); ui.swOptions->insertWidget(LiveDataSource::Ascii, asciiw); QWidget* binaryw = new QWidget(); m_binaryOptionsWidget = std::unique_ptr(new BinaryOptionsWidget(binaryw)); ui.swOptions->insertWidget(LiveDataSource::Binary, binaryw); QWidget* imagew = new QWidget(); m_imageOptionsWidget = std::unique_ptr(new ImageOptionsWidget(imagew)); ui.swOptions->insertWidget(LiveDataSource::Image, imagew); QWidget* hdf5w = new QWidget(); m_hdf5OptionsWidget = std::unique_ptr(new HDF5OptionsWidget(hdf5w, this)); ui.swOptions->insertWidget(LiveDataSource::HDF5, hdf5w); QWidget* netcdfw = new QWidget(); m_netcdfOptionsWidget = std::unique_ptr(new NetCDFOptionsWidget(netcdfw, this)); ui.swOptions->insertWidget(LiveDataSource::NETCDF, netcdfw); QWidget* fitsw = new QWidget(); m_fitsOptionsWidget = std::unique_ptr(new FITSOptionsWidget(fitsw, this)); ui.swOptions->insertWidget(LiveDataSource::FITS, fitsw); // the table widget for preview m_twPreview = new QTableWidget(ui.tePreview); m_twPreview->verticalHeader()->hide(); m_twPreview->setEditTriggers(QTableWidget::NoEditTriggers); QHBoxLayout* layout = new QHBoxLayout; layout->addWidget(m_twPreview); ui.tePreview->setLayout(layout); m_twPreview->hide(); // default filter ui.swOptions->setCurrentIndex(LiveDataSource::Ascii); #if !defined(HAVE_HDF5) || !defined(HAVE_NETCDF) || !defined(HAVE_FITS) const QStandardItemModel* model = qobject_cast(ui.cbFileType->model()); #endif #ifndef HAVE_HDF5 // disable HDF5 item QStandardItem* item = model->item(LiveDataSource::HDF5); item->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); #endif #ifndef HAVE_NETCDF // disable NETCDF item QStandardItem* item2 = model->item(LiveDataSource::NETCDF); item2->setFlags(item2->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); #endif #ifndef HAVE_FITS // disable FITS item QStandardItem* item3 = model->item(LiveDataSource::FITS); item3->setFlags(item3->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); #endif +#ifndef HAVE_MQTT + // disable MQTT item + QStandardItem* item4 = model->item(LiveDataSource::MQTT); + item3->setFlags(item4->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled)); +#endif ui.cbReadType->addItem(i18n("Whole file"), LiveDataSource::WholeFile); ui.lePort->setValidator( new QIntValidator(ui.lePort) ); ui.gbOptions->hide(); ui.gbUpdateOptions->hide(); ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); ui.bFileInfo->setIcon( QIcon::fromTheme("help-about") ); ui.bManageFilters->setIcon( QIcon::fromTheme("configure") ); ui.bSaveFilter->setIcon( QIcon::fromTheme("document-save") ); ui.bRefreshPreview->setIcon( QIcon::fromTheme("view-refresh") ); #ifdef HAVE_MQTT m_client = new QMqttClient(this); #endif connect( ui.leFileName, SIGNAL(textChanged(QString)), SLOT(fileNameChanged(QString)) ); connect( ui.bOpen, SIGNAL(clicked()), this, SLOT (selectFile()) ); connect( ui.bFileInfo, SIGNAL(clicked()), this, SLOT (fileInfoDialog()) ); connect( ui.bSaveFilter, SIGNAL(clicked()), this, SLOT (saveFilter()) ); connect( ui.bManageFilters, SIGNAL(clicked()), this, SLOT (manageFilters()) ); connect( ui.cbFileType, SIGNAL(currentIndexChanged(int)), SLOT(fileTypeChanged(int)) ); connect( ui.cbUpdateType, SIGNAL(currentIndexChanged(int)), this, SLOT(updateTypeChanged(int))); connect( ui.cbReadType, SIGNAL(currentIndexChanged(int)), this, SLOT(readingTypeChanged(int))); connect( ui.cbFilter, SIGNAL(activated(int)), SLOT(filterChanged(int)) ); connect( ui.bRefreshPreview, SIGNAL(clicked()), SLOT(refreshPreview()) ); #ifdef HAVE_MQTT connect(ui.chbID, SIGNAL(stateChanged(int)), this, SLOT(idChecked(int))); connect(ui.chbAuthentication, SIGNAL(stateChanged(int)), this, SLOT(authenticationChecked(int))); connect(ui.bConnect, SIGNAL(clicked()), this, SLOT(mqttConnection()) ); connect(m_client, SIGNAL(connected()), this, SLOT(onMqttConnect()) ); connect(ui.bSubscribe, SIGNAL(clicked()), this, SLOT(mqttSubscribe()) ); connect(m_client, SIGNAL(messageReceived(QByteArray, QMqttTopicName)), this, SLOT(mqttMessageReceived(QByteArray, QMqttTopicName)) ); connect(this, &ImportFileWidget::newTopic, this, &ImportFileWidget::setCompleter); connect(ui.cbTopic, &QComboBox::currentTextChanged, this, &ImportFileWidget::topicBeingTyped); connect(m_timer, &QTimer::timeout, this, &ImportFileWidget::topicTimeout); connect(ui.cbTopic, &QComboBox::currentTextChanged, this, &ImportFileWidget::mqttButtonSubscribe); connect(ui.lwSubscriptions, &QListWidget::currentTextChanged, this, &ImportFileWidget::mqttButtonUnsubscribe); connect(m_client, &QMqttClient::disconnected, this, &ImportFileWidget::onMqttDisconnect); connect(ui.chbWill, &QCheckBox::stateChanged, this, &ImportFileWidget::useWillMessage); connect(ui.cbWillMessageType, static_cast(&QComboBox::currentIndexChanged), this, &ImportFileWidget::willMessageTypeChanged); connect(ui.cbWillUpdate, static_cast(&QComboBox::currentIndexChanged), this, &ImportFileWidget::willUpdateChanged); connect(this, &ImportFileWidget::subscriptionMade, this, &ImportFileWidget::updateWillTopics); connect(m_client, &QMqttClient::errorChanged, this, &ImportFileWidget::mqttErrorChanged); #endif -#ifndef HAVE_MQTT - ui.cbSourceType->removeItem(LiveDataSource::SourceType::Mqtt); -#endif connect(ui.leHost, SIGNAL(textChanged(QString)), this, SIGNAL(hostChanged())); connect(ui.lePort, SIGNAL(textChanged(QString)), this, SIGNAL(portChanged())); connect( ui.cbSourceType, SIGNAL(currentIndexChanged(int)), this, SLOT(sourceTypeChanged(int))); //TODO: implement save/load of user-defined settings later and activate these buttons again ui.bSaveFilter->hide(); ui.bManageFilters->hide(); //defer the loading of settings a bit in order to show the dialog prior to blocking the GUI in refreshPreview() QTimer::singleShot( 100, this, SLOT(loadSettings()) ); hideMQTT(); } void ImportFileWidget::loadSettings() { m_suppressRefresh = true; //load last used settings QString confName; if (m_liveDataSource) confName = QLatin1String("LiveDataImport"); else confName = QLatin1String("FileImport"); KConfigGroup conf(KSharedConfig::openConfig(), confName); //settings for data type specific widgets m_asciiOptionsWidget->loadSettings(); m_binaryOptionsWidget->loadSettings(); m_imageOptionsWidget->loadSettings(); //read the source type first since settings in fileNameChanged() depend on this ui.cbSourceType->setCurrentIndex(conf.readEntry("SourceType").toInt()); //general settings ui.cbFileType->setCurrentIndex(conf.readEntry("Type", 0)); ui.cbFilter->setCurrentIndex(conf.readEntry("Filter", 0)); filterChanged(ui.cbFilter->currentIndex()); // needed if filter is not changed if (m_fileName.isEmpty()) ui.leFileName->setText(conf.readEntry("LastImportedFile", "")); else ui.leFileName->setText(m_fileName); //live data related settings ui.cbBaudRate->setCurrentIndex(conf.readEntry("BaudRate").toInt()); ui.cbReadType->setCurrentIndex(conf.readEntry("ReadType").toInt()); ui.cbSerialPort->setCurrentIndex(conf.readEntry("SerialPort").toInt()); ui.cbUpdateType->setCurrentIndex(conf.readEntry("UpdateType").toInt()); ui.leHost->setText(conf.readEntry("Host","")); ui.leKeepLastValues->setText(conf.readEntry("KeepLastNValues","")); ui.lePort->setText(conf.readEntry("Port","")); ui.sbSampleRate->setValue(conf.readEntry("SampleRate").toInt()); ui.sbUpdateInterval->setValue(conf.readEntry("UpdateInterval").toInt()); #ifdef HAVE_MQTT ui.chbID->setChecked(conf.readEntry("mqttUseId").toInt()); ui.chbAuthentication->setChecked(conf.readEntry("mqttUseAuthentication").toInt()); ui.chbRetain->setChecked(conf.readEntry("mqttUseRetain").toInt()); ui.leUsername->setText(conf.readEntry("mqttUsername","")); ui.lePassword->setText(conf.readEntry("mqttPassword","")); ui.leID->setText(conf.readEntry("mqttId","")); ui.chbWillRetain->setChecked(conf.readEntry("mqttWillRetain").toInt()); ui.cbWillUpdate->setCurrentIndex(conf.readEntry("mqttWillUpdateType").toInt()); ui.cbWillQoS->setCurrentIndex(conf.readEntry("mqttWillQoS").toInt()); ui.leWillOwnMessage->setText(conf.readEntry("mqttWillOwnMessage","")); ui.leWillUpdateInterval->setText(conf.readEntry("mqttWillUpdateInterval","")); QString willStatistics = conf.readEntry("mqttWillStatistics",""); QStringList statisticsList = willStatistics.split('|', QString::SplitBehavior::SkipEmptyParts); for(auto value : statisticsList) { QListWidgetItem* item = ui.lwWillStatistics->item(value.toInt()); item->setCheckState(Qt::Checked); } ui.cbWillMessageType->setCurrentIndex(conf.readEntry("mqttWillMessageType").toInt()); ui.chbWill->setChecked(conf.readEntry("mqttWillUse").toInt()); //chbWill is unchecked by deafult, so if false is loaded it doesn't emit state changed signal, we have to force it if(!ui.chbWill->isChecked()) { ui.chbWill->setChecked(true); ui.chbWill->setChecked(false); } #endif m_suppressRefresh = false; refreshPreview(); } ImportFileWidget::~ImportFileWidget() { // save current settings QString confName; if (m_liveDataSource) confName = QLatin1String("LiveDataImport"); else confName = QLatin1String("FileImport"); KConfigGroup conf(KSharedConfig::openConfig(), confName); // general settings conf.writeEntry("Type", ui.cbFileType->currentIndex()); conf.writeEntry("Filter", ui.cbFilter->currentIndex()); conf.writeEntry("LastImportedFile", ui.leFileName->text()); //live data related settings conf.writeEntry("SourceType", ui.cbSourceType->currentIndex()); conf.writeEntry("UpdateType", ui.cbUpdateType->currentIndex()); conf.writeEntry("ReadType", ui.cbReadType->currentIndex()); conf.writeEntry("SampleRate", ui.sbSampleRate->value()); conf.writeEntry("KeepLastNValues", ui.leKeepLastValues->text()); conf.writeEntry("BaudRate", ui.cbBaudRate->currentIndex()); conf.writeEntry("SerialPort", ui.cbSerialPort->currentIndex()); conf.writeEntry("Host", ui.leHost->text()); conf.writeEntry("Port", ui.lePort->text()); conf.writeEntry("UpdateInterval", ui.sbUpdateInterval->value()); #ifdef HAVE_MQTT conf.writeEntry("mqttUsername", ui.leUsername->text()); conf.writeEntry("mqttPassword", ui.lePassword->text()); conf.writeEntry("mqttId", ui.leID->text()); conf.writeEntry("mqttWillMessageType", ui.cbWillMessageType->currentIndex()); conf.writeEntry("mqttWillUpdateType", ui.cbWillUpdate->currentIndex()); conf.writeEntry("mqttWillQoS", ui.cbWillQoS->currentIndex()); conf.writeEntry("mqttWillOwnMessage", ui.leWillOwnMessage->text()); conf.writeEntry("mqttWillUpdateInterval", ui.leWillUpdateInterval->text()); QString willStatistics; for(int i = 0; i < ui.lwWillStatistics->count(); ++i) { QListWidgetItem* item = ui.lwWillStatistics->item(i); if (item->checkState() == Qt::Checked) willStatistics += QString::number(i)+"|"; } conf.writeEntry("mqttWillStatistics", willStatistics); conf.writeEntry("mqttWillRetain", static_cast(ui.chbWillRetain->isChecked())); conf.writeEntry("mqttWillUse", static_cast(ui.chbWill->isChecked())); conf.writeEntry("mqttUseId", static_cast(ui.chbID->isChecked())); conf.writeEntry("mqttUseAuthentication", static_cast(ui.chbAuthentication->isChecked())); conf.writeEntry("mqttUseRetain", static_cast(ui.chbRetain->isChecked())); #endif // data type specific settings m_asciiOptionsWidget->saveSettings(); m_binaryOptionsWidget->saveSettings(); m_imageOptionsWidget->saveSettings(); } void ImportFileWidget::hideDataSource() { m_liveDataSource = false; ui.gbUpdateOptions->hide(); ui.chbLinkFile->hide(); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); ui.lSourceType->hide(); ui.cbSourceType->hide(); ui.cbUpdateType->hide(); ui.lUpdateType->hide(); ui.sbUpdateInterval->hide(); ui.lUpdateInterval->hide(); #ifdef HAVE_MQTT hideMQTT(); #endif } void ImportFileWidget::showAsciiHeaderOptions(bool b) { m_asciiOptionsWidget->showAsciiHeaderOptions(b); } void ImportFileWidget::showOptions(bool b) { ui.gbOptions->setVisible(b); if (m_liveDataSource) ui.gbUpdateOptions->setVisible(b); resize(layout()->minimumSize()); } QString ImportFileWidget::fileName() const { return ui.leFileName->text(); } QString ImportFileWidget::selectedObject() const { const QString& path = ui.leFileName->text(); //determine the file name only QString name = path.right( path.length()-path.lastIndexOf(QDir::separator())-1 ); //strip away the extension if available if (name.indexOf('.') != -1) name = name.left(name.lastIndexOf('.')); //for multi-dimensinal formats like HDF, netCDF and FITS add the currently selected object const auto format = currentFileType(); if (format == LiveDataSource::HDF5) { const QStringList& hdf5Names = m_hdf5OptionsWidget->selectedHDF5Names(); if (hdf5Names.size()) name += hdf5Names.first(); //the names of the selected HDF5 objects already have '/' } else if (format == LiveDataSource::NETCDF) { const QStringList& names = m_netcdfOptionsWidget->selectedNetCDFNames(); if (names.size()) name += QLatin1Char('/') + names.first(); } else if (format == LiveDataSource::FITS) { const QString& extensionName = m_fitsOptionsWidget->currentExtensionName(); if (!extensionName.isEmpty()) name += QLatin1Char('/') + extensionName; } return name; } /*! * returns \c true if the number of lines to be imported from the currently selected file is zero ("file is empty"), * returns \c false otherwise. */ bool ImportFileWidget::isFileEmpty() const { return m_fileEmpty; } QString ImportFileWidget::host() const { return ui.leHost->text(); } QString ImportFileWidget::port() const { return ui.lePort->text(); } QString ImportFileWidget::serialPort() const { return ui.cbSerialPort->currentText(); } int ImportFileWidget::baudRate() const { return ui.cbBaudRate->currentText().toInt(); } /*! saves the settings to the data source \c source. */ void ImportFileWidget::saveSettings(LiveDataSource* source) const { LiveDataSource::FileType fileType = static_cast(ui.cbFileType->currentIndex()); LiveDataSource::UpdateType updateType = static_cast(ui.cbUpdateType->currentIndex()); LiveDataSource::SourceType sourceType = static_cast(ui.cbSourceType->currentIndex()); LiveDataSource::ReadingType readingType = static_cast(ui.cbReadType->currentIndex()); source->setComment( ui.leFileName->text() ); source->setFileType(fileType); source->setFilter(this->currentFileFilter()); source->setSourceType(sourceType); source->setReadingType(readingType); if (updateType == LiveDataSource::UpdateType::TimeInterval) source->setUpdateInterval(ui.sbUpdateInterval->value()); else source->setFileWatched(true); if (!ui.leKeepLastValues->text().isEmpty()) { source->setKeepLastValues(true); source->setKeepNvalues(ui.leKeepLastValues->text().toInt()); } source->setUpdateType(updateType); if (readingType != LiveDataSource::ReadingType::TillEnd) source->setSampleRate(ui.sbSampleRate->value()); switch (sourceType) { case LiveDataSource::SourceType::FileOrPipe: source->setFileName( ui.leFileName->text() ); source->setFileLinked( ui.chbLinkFile->isChecked() ); break; case LiveDataSource::SourceType::LocalSocket: source->setLocalSocketName(ui.leFileName->text()); break; case LiveDataSource::SourceType::NetworkTcpSocket: case LiveDataSource::SourceType::NetworkUdpSocket: source->setHost(ui.leHost->text()); source->setPort((quint16)ui.lePort->text().toInt()); break; case LiveDataSource::SourceType::SerialPort: source->setBaudRate(ui.cbBaudRate->currentText().toInt()); source->setSerialPort(ui.cbSerialPort->currentText()); break; + case LiveDataSource::SourceType::MQTT:{ #ifdef HAVE_MQTT - case LiveDataSource::SourceType::Mqtt:{ qDebug()<<"Saving mqtt"; source->setMqttClient(m_client->hostname(), m_client->port()); source->setMQTTUseAuthentication(ui.chbAuthentication->isChecked()); if(ui.chbAuthentication->isChecked()) source->setMqttClientAuthentication(m_client->username(), m_client->password()); source->setMQTTUseID(ui.chbID->isChecked()); if(ui.chbID->isChecked()) source->setMqttClientId(m_client->clientId()); for(int i=0; iaddMqttSubscriptions(m_mqttSubscriptions[i]->topic(), m_mqttSubscriptions[i]->qos()); } source->setMqttRetain(ui.chbRetain->isChecked()); source->setWillMessageType(static_cast(ui.cbWillMessageType->currentIndex()) ); source->setWillOwnMessage(ui.leWillOwnMessage->text()); source->setWillQoS(ui.cbWillQoS->currentIndex() ); source->setWillRetain(ui.chbWillRetain->isChecked()); source->setWillTimeInterval(ui.leWillUpdateInterval->text().toInt()); source->setWillTopic(ui.cbWillTopic->currentText()); source->setWillUpdateType(static_cast(ui.cbWillUpdate->currentIndex()) ); source->setMqttWillUse(ui.chbWill->isChecked()); for(int i = 0; i < ui.lwWillStatistics->count(); ++i) { QListWidgetItem* item = ui.lwWillStatistics->item(i); if (item->checkState() == Qt::Checked) source->addWillStatistics(static_cast (i)); } +#endif break; } -#endif default: break; } } /*! returns the currently used file type. */ LiveDataSource::FileType ImportFileWidget::currentFileType() const { return static_cast(ui.cbFileType->currentIndex()); } LiveDataSource::SourceType ImportFileWidget::currentSourceType() const { return static_cast(ui.cbSourceType->currentIndex()); } /*! returns the currently used filter. */ AbstractFileFilter* ImportFileWidget::currentFileFilter() const { DEBUG("currentFileFilter()"); LiveDataSource::FileType fileType = static_cast(ui.cbFileType->currentIndex()); switch (fileType) { case LiveDataSource::Ascii: { //TODO std::unique_ptr filter(new AsciiFilter()); AsciiFilter* filter = new AsciiFilter(); if (ui.cbFilter->currentIndex() == 0) //"automatic" filter->setAutoModeEnabled(true); else if (ui.cbFilter->currentIndex() == 1) { //"custom" filter->setAutoModeEnabled(false); m_asciiOptionsWidget->applyFilterSettings(filter); } else filter->loadFilterSettings( ui.cbFilter->currentText() ); //save the data portion to import filter->setStartRow( ui.sbStartRow->value()); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value()); filter->setEndColumn( ui.sbEndColumn->value()); return filter; } case LiveDataSource::Binary: { BinaryFilter* filter = new BinaryFilter(); if ( ui.cbFilter->currentIndex() == 0 ) //"automatic" filter->setAutoModeEnabled(true); else if ( ui.cbFilter->currentIndex() == 1 ) { //"custom" filter->setAutoModeEnabled(false); m_binaryOptionsWidget->applyFilterSettings(filter); } else { //TODO: load filter settings // filter->setFilterName( ui.cbFilter->currentText() ); } filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); return filter; } case LiveDataSource::Image: { ImageFilter* filter = new ImageFilter(); filter->setImportFormat(m_imageOptionsWidget->currentFormat()); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value() ); filter->setEndColumn( ui.sbEndColumn->value() ); return filter; } case LiveDataSource::HDF5: { HDF5Filter* filter = new HDF5Filter(); QStringList names = selectedHDF5Names(); if (!names.isEmpty()) filter->setCurrentDataSetName(names[0]); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value() ); filter->setEndColumn( ui.sbEndColumn->value() ); return filter; } case LiveDataSource::NETCDF: { NetCDFFilter* filter = new NetCDFFilter(); if (!selectedNetCDFNames().isEmpty()) filter->setCurrentVarName(selectedNetCDFNames()[0]); filter->setStartRow( ui.sbStartRow->value() ); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value() ); filter->setEndColumn( ui.sbEndColumn->value() ); return filter; } case LiveDataSource::FITS: { FITSFilter* filter = new FITSFilter(); filter->setStartRow( ui.sbStartRow->value()); filter->setEndRow( ui.sbEndRow->value() ); filter->setStartColumn( ui.sbStartColumn->value()); filter->setEndColumn( ui.sbEndColumn->value()); return filter; } } return 0; } /*! opens a file dialog and lets the user select the file data source. */ void ImportFileWidget::selectFile() { KConfigGroup conf(KSharedConfig::openConfig(), "ImportFileWidget"); QString dir = conf.readEntry("LastDir", ""); QString path = QFileDialog::getOpenFileName(this, i18n("Select the File Data Source"), dir); if (path.isEmpty()) return; //cancel was clicked in the file-dialog int pos = path.lastIndexOf(QDir::separator()); if (pos != -1) { QString newDir = path.left(pos); if (newDir != dir) conf.writeEntry("LastDir", newDir); } ui.leFileName->setText(path); //TODO: decide whether the selection of several files should be possible // QStringList filelist = QFileDialog::getOpenFileNames(this,i18n("Select one or more files to open")); // if (! filelist.isEmpty() ) // ui.leFileName->setText(filelist.join(";")); } /************** SLOTS **************************************************************/ /*! called on file name changes. Determines the file format (ASCII, binary etc.), if the file exists, and activates the corresponding options. */ void ImportFileWidget::fileNameChanged(const QString& name) { QString fileName = name; #ifndef HAVE_WINDOWS // make relative path if ( !fileName.isEmpty() && fileName.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif bool fileExists = QFile::exists(fileName); if (fileExists) ui.leFileName->setStyleSheet(""); else ui.leFileName->setStyleSheet("QLineEdit{background:red;}"); ui.gbOptions->setEnabled(fileExists); ui.bManageFilters->setEnabled(fileExists); ui.cbFilter->setEnabled(fileExists); ui.cbFileType->setEnabled(fileExists); ui.bFileInfo->setEnabled(fileExists); ui.gbUpdateOptions->setEnabled(fileExists); if (!fileExists) { //file doesn't exist -> delete the content preview that is still potentially //available from the previously selected file ui.tePreview->clear(); m_twPreview->clear(); m_hdf5OptionsWidget->clear(); m_netcdfOptionsWidget->clear(); m_fitsOptionsWidget->clear(); emit fileNameChanged(); return; } if (currentSourceType() == LiveDataSource::FileOrPipe) { QString fileInfo; #ifndef HAVE_WINDOWS //check, if we can guess the file type by content QProcess* proc = new QProcess(this); QStringList args; args << "-b" << ui.leFileName->text(); proc->start("file", args); if (proc->waitForReadyRead(1000) == false) { QDEBUG("ERROR: reading file type of file" << fileName); return; } fileInfo = proc->readLine(); #endif QByteArray imageFormat = QImageReader::imageFormat(fileName); if (fileInfo.contains(QLatin1String("compressed data")) || fileInfo.contains(QLatin1String("ASCII")) || fileName.endsWith(QLatin1String("dat"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("txt"), Qt::CaseInsensitive)) { //probably ascii data ui.cbFileType->setCurrentIndex(LiveDataSource::Ascii); } else if (fileInfo.contains(QLatin1String("Hierarchical Data Format")) || fileName.endsWith(QLatin1String("h5"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("hdf"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("hdf5"), Qt::CaseInsensitive) ) { ui.cbFileType->setCurrentIndex(LiveDataSource::HDF5); // update HDF5 tree widget using current selected file m_hdf5OptionsWidget->updateContent((HDF5Filter*)this->currentFileFilter(), fileName); } else if (fileInfo.contains(QLatin1String("NetCDF Data Format")) || fileName.endsWith(QLatin1String("nc"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("netcdf"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("cdf"), Qt::CaseInsensitive)) { ui.cbFileType->setCurrentIndex(LiveDataSource::NETCDF); // update NetCDF tree widget using current selected file m_netcdfOptionsWidget->updateContent((NetCDFFilter*)this->currentFileFilter(), fileName); } else if (fileInfo.contains(QLatin1String("FITS image data")) || fileName.endsWith(QLatin1String("fits"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("fit"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String("fts"), Qt::CaseInsensitive)) { #ifdef HAVE_FITS ui.cbFileType->setCurrentIndex(LiveDataSource::FITS); #endif // update FITS tree widget using current selected file m_fitsOptionsWidget->updateContent((FITSFilter*)this->currentFileFilter(), fileName); } else if (fileInfo.contains("image") || fileInfo.contains("bitmap") || !imageFormat.isEmpty()) ui.cbFileType->setCurrentIndex(LiveDataSource::Image); else ui.cbFileType->setCurrentIndex(LiveDataSource::Binary); } refreshPreview(); emit fileNameChanged(); } /*! saves the current filter settings */ void ImportFileWidget::saveFilter() { bool ok; QString text = QInputDialog::getText(this, i18n("Save Filter Settings as"), i18n("Filter name:"), QLineEdit::Normal, i18n("new filter"), &ok); if (ok && !text.isEmpty()) { //TODO //AsciiFilter::saveFilter() } } /*! opens a dialog for managing all available predefined filters. */ void ImportFileWidget::manageFilters() { //TODO } /*! Depending on the selected file type, activates the corresponding options in the data portion tab and populates the combobox with the available pre-defined fllter settings for the selected type. */ void ImportFileWidget::fileTypeChanged(int fileType) { ui.swOptions->setCurrentIndex(fileType); //default ui.lFilter->show(); ui.cbFilter->show(); //if we switch from netCDF-format (only two tabs available), add the data preview-tab again if (ui.tabWidget->count() == 2) { ui.tabWidget->setTabText(0, i18n("Data format")); ui.tabWidget->insertTab(1, ui.tabDataPreview, i18n("Preview")); } ui.lPreviewLines->show(); ui.sbPreviewLines->show(); ui.lStartColumn->show(); ui.sbStartColumn->show(); ui.lEndColumn->show(); ui.sbEndColumn->show(); switch (fileType) { case LiveDataSource::Ascii: break; case LiveDataSource::Binary: ui.lStartColumn->hide(); ui.sbStartColumn->hide(); ui.lEndColumn->hide(); ui.sbEndColumn->hide(); break; case LiveDataSource::HDF5: case LiveDataSource::NETCDF: ui.lFilter->hide(); ui.cbFilter->hide(); // hide global preview tab. we have our own ui.tabWidget->setTabText(0, i18n("Data format && preview")); ui.tabWidget->removeTab(1); ui.tabWidget->setCurrentIndex(0); break; case LiveDataSource::Image: ui.lPreviewLines->hide(); ui.sbPreviewLines->hide(); ui.lFilter->hide(); ui.cbFilter->hide(); break; case LiveDataSource::FITS: ui.lFilter->hide(); ui.cbFilter->hide(); ui.tabWidget->setTabText(0, i18n("Data format && preview")); ui.tabWidget->removeTab(1); ui.tabWidget->setCurrentIndex(0); break; default: DEBUG("unknown file type"); } m_hdf5OptionsWidget->clear(); m_netcdfOptionsWidget->clear(); int lastUsedFilterIndex = ui.cbFilter->currentIndex(); ui.cbFilter->clear(); ui.cbFilter->addItem( i18n("Automatic") ); ui.cbFilter->addItem( i18n("Custom") ); //TODO: populate the combobox with the available pre-defined filter settings for the selected type ui.cbFilter->setCurrentIndex(lastUsedFilterIndex); filterChanged(lastUsedFilterIndex); refreshPreview(); } const QStringList ImportFileWidget::selectedHDF5Names() const { return m_hdf5OptionsWidget->selectedHDF5Names(); } const QStringList ImportFileWidget::selectedNetCDFNames() const { return m_netcdfOptionsWidget->selectedNetCDFNames(); } const QStringList ImportFileWidget::selectedFITSExtensions() const { return m_fitsOptionsWidget->selectedFITSExtensions(); } /*! shows the dialog with the information about the file(s) to be imported. */ void ImportFileWidget::fileInfoDialog() { QStringList files = ui.leFileName->text().split(';'); FileInfoDialog* dlg = new FileInfoDialog(this); dlg->setFiles(files); dlg->exec(); } /*! enables the options if the filter "custom" was chosen. Disables the options otherwise. */ void ImportFileWidget::filterChanged(int index) { // ignore filter for these formats if (ui.cbFileType->currentIndex() == LiveDataSource::HDF5 || ui.cbFileType->currentIndex() == LiveDataSource::NETCDF || ui.cbFileType->currentIndex() == LiveDataSource::Image || ui.cbFileType->currentIndex() == LiveDataSource::FITS) { ui.swOptions->setEnabled(true); return; } if (index == 0) { // "automatic" ui.swOptions->setEnabled(false); ui.bSaveFilter->setEnabled(false); } else if (index == 1) { //custom ui.swOptions->setEnabled(true); ui.bSaveFilter->setEnabled(true); } else { // predefined filter settings were selected. //load and show them in the GUI. //TODO } } void ImportFileWidget::refreshPreview() { if (m_suppressRefresh) return; WAIT_CURSOR; QString fileName = ui.leFileName->text(); #ifndef HAVE_WINDOWS if (!fileName.isEmpty() && fileName.at(0) != QDir::separator()) fileName = QDir::homePath() + QDir::separator() + fileName; #endif DEBUG("refreshPreview() file name = " << fileName.toStdString()); QVector importedStrings; LiveDataSource::FileType fileType = (LiveDataSource::FileType)ui.cbFileType->currentIndex(); // generic table widget if (fileType == LiveDataSource::Ascii || fileType == LiveDataSource::Binary) m_twPreview->show(); else m_twPreview->hide(); int lines = ui.sbPreviewLines->value(); bool ok = true; QTableWidget* tmpTableWidget{nullptr}; QStringList vectorNameList; QVector columnModes; switch (fileType) { case LiveDataSource::Ascii: { DEBUG("ASCII"); ui.tePreview->clear(); AsciiFilter* filter = static_cast(this->currentFileFilter()); switch (currentSourceType()) { case LiveDataSource::SourceType::FileOrPipe: { DEBUG(" FileOrPipe"); importedStrings = filter->preview(fileName, lines); break; } case LiveDataSource::SourceType::LocalSocket: { DEBUG(" LocalSocket"); QLocalSocket lsocket{this}; DEBUG("CONNECT PREVIEW"); lsocket.connectToServer(fileName, QLocalSocket::ReadOnly); if (lsocket.waitForConnected()) { DEBUG("connected to local socket " << fileName.toStdString()); if (lsocket.waitForReadyRead()) importedStrings = filter->preview(lsocket); DEBUG("DISCONNECT PREVIEW"); lsocket.disconnectFromServer(); // read-only socket is disconnected immediately (no waitForDisconnected()) } else { DEBUG("failed connect to local socket " << fileName.toStdString() << " - " << lsocket.errorString().toStdString()); } break; } case LiveDataSource::SourceType::NetworkTcpSocket: { DEBUG(" TCPSocket"); QTcpSocket tcpSocket{this}; tcpSocket.connectToHost(host(), port().toInt(), QTcpSocket::ReadOnly); if (tcpSocket.waitForConnected()) { DEBUG("connected to TCP socket"); if ( tcpSocket.waitForReadyRead() ) importedStrings = filter->preview(tcpSocket); tcpSocket.disconnectFromHost(); } else DEBUG("failed to connect to TCP socket " << " - " << tcpSocket.errorString().toStdString()); break; } case LiveDataSource::SourceType::NetworkUdpSocket: { DEBUG(" UDPSocket"); QUdpSocket udpSocket(this); DEBUG("CONNECT PREVIEW"); udpSocket.connectToHost(host(), port().toInt(), QUdpSocket::ReadOnly); if (udpSocket.waitForConnected()) { DEBUG("connected to UDP socket " << host().toStdString() << ':' << port().toInt()); if ( udpSocket.waitForReadyRead(5000) ) importedStrings = filter->preview(udpSocket); else DEBUG(" ERROR: not ready for read"); DEBUG("DISCONNECT PREVIEW"); udpSocket.disconnectFromHost(); } else DEBUG("failed to connect to UDP socket " << " - " << udpSocket.errorString().toStdString()); break; } case LiveDataSource::SourceType::SerialPort: { DEBUG(" SerialPort"); QSerialPort sPort{this}; sPort.setBaudRate(baudRate()); sPort.setPortName(serialPort()); if (sPort.open(QIODevice::ReadOnly)) { bool canread = sPort.waitForReadyRead(500); if (canread) importedStrings = filter->preview(sPort); sPort.close(); } break; } + case LiveDataSource::SourceType::MQTT: { #ifdef HAVE_MQTT - case LiveDataSource::SourceType::Mqtt: { qDebug()<<"preview mqtt, is it ready:"<vectorNames().clear(); QMapIterator i(m_lastMessage); while(i.hasNext()) { i.next(); qDebug()<<"calling ascii mqtt preview"<< importedStrings << " "<mqttPreview(importedStrings, QString(i.value().payload().data()), i.key().name() ); if(importedStrings.isEmpty()) break; } QMapIterator j(m_messageArrived); while(j.hasNext()) { j.next(); m_messageArrived[j.key()] = false; } m_mqttReadyForPreview = false; } +#endif break; } -#endif } tmpTableWidget = m_twPreview; vectorNameList = filter->vectorNames(); columnModes = filter->columnModes(); break; } case LiveDataSource::Binary: { DEBUG("Binary"); ui.tePreview->clear(); BinaryFilter *filter = (BinaryFilter *)this->currentFileFilter(); importedStrings = filter->preview(fileName, lines); tmpTableWidget = m_twPreview; break; } case LiveDataSource::Image: { DEBUG("Image"); ui.tePreview->clear(); QImage image(fileName); QTextCursor cursor = ui.tePreview->textCursor(); cursor.insertImage(image); RESET_CURSOR; return; } case LiveDataSource::HDF5: { DEBUG(" HDF5"); HDF5Filter *filter = (HDF5Filter *)this->currentFileFilter(); lines = m_hdf5OptionsWidget->lines(); importedStrings = filter->readCurrentDataSet(fileName, NULL, ok, AbstractFileFilter::Replace, lines); tmpTableWidget = m_hdf5OptionsWidget->previewWidget(); break; } case LiveDataSource::NETCDF: { DEBUG(" NETCDF"); NetCDFFilter *filter = (NetCDFFilter *)this->currentFileFilter(); lines = m_netcdfOptionsWidget->lines(); importedStrings = filter->readCurrentVar(fileName, NULL, AbstractFileFilter::Replace, lines); tmpTableWidget = m_netcdfOptionsWidget->previewWidget(); break; } case LiveDataSource::FITS: { DEBUG(" FITS"); FITSFilter* filter = (FITSFilter*)this->currentFileFilter(); lines = m_fitsOptionsWidget->lines(); // update file name (may be any file type) m_fitsOptionsWidget->updateContent(filter, fileName); QString extensionName = m_fitsOptionsWidget->extensionName(&ok); if (!extensionName.isEmpty()) { DEBUG(" extension name = " << extensionName.toStdString()); fileName = extensionName; } DEBUG(" file name = " << fileName.toStdString()); bool readFitsTableToMatrix; importedStrings = filter->readChdu(fileName, &readFitsTableToMatrix, lines); emit checkedFitsTableToMatrix(readFitsTableToMatrix); tmpTableWidget = m_fitsOptionsWidget->previewWidget(); break; } } // fill the table widget tmpTableWidget->setRowCount(0); tmpTableWidget->setColumnCount(0); if( !importedStrings.isEmpty() ) { //QDEBUG("importedStrings =" << importedStrings); if (!ok) { // show imported strings as error message tmpTableWidget->setRowCount(1); tmpTableWidget->setColumnCount(1); QTableWidgetItem* item = new QTableWidgetItem(); item->setText(importedStrings[0][0]); tmpTableWidget->setItem(0, 0, item); } else { //TODO: maxrows not used const int rows = qMax(importedStrings.size(), 1); const int maxColumns = 300; tmpTableWidget->setRowCount(rows); for (int i = 0; i < rows; ++i) { QDEBUG(importedStrings[i]); int cols = importedStrings[i].size() > maxColumns ? maxColumns : importedStrings[i].size(); // new if (cols > tmpTableWidget->columnCount()) tmpTableWidget->setColumnCount(cols); for (int j = 0; j < cols; ++j) { QTableWidgetItem* item = new QTableWidgetItem(importedStrings[i][j]); tmpTableWidget->setItem(i, j, item); } } // set header if columnMode available for (int i = 0; i < qMin(tmpTableWidget->columnCount(), columnModes.size()); ++i) { QString columnName = QString::number(i+1); if (i < vectorNameList.size()) columnName = vectorNameList[i]; auto* item = new QTableWidgetItem(columnName + QLatin1String(" {") + ENUM_TO_STRING(AbstractColumn, ColumnMode, columnModes[i]) + QLatin1String("}")); item->setTextAlignment(Qt::AlignLeft); item->setIcon(AbstractColumn::iconForMode(columnModes[i])); tmpTableWidget->setHorizontalHeaderItem(i, item); } } tmpTableWidget->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); m_fileEmpty = false; } else { m_fileEmpty = true; } RESET_CURSOR; } void ImportFileWidget::updateTypeChanged(int idx) { LiveDataSource::UpdateType type = static_cast(idx); if (type == LiveDataSource::UpdateType::TimeInterval) { ui.lUpdateInterval->show(); ui.sbUpdateInterval->show(); ui.lUpdateIntervalUnit->show(); } else if (type == LiveDataSource::UpdateType::NewData) { ui.lUpdateInterval->hide(); ui.sbUpdateInterval->hide(); ui.lUpdateIntervalUnit->hide(); } } void ImportFileWidget::readingTypeChanged(int idx) { LiveDataSource::ReadingType type = static_cast(idx); if (type == LiveDataSource::ReadingType::TillEnd || type == LiveDataSource::ReadingType::WholeFile) { ui.lSampleRate->hide(); ui.sbSampleRate->hide(); } else { ui.lSampleRate->show(); ui.sbSampleRate->show(); } if (type == LiveDataSource::ReadingType::WholeFile) { ui.lKeepLastValues->hide(); ui.leKeepLastValues->hide(); } else { ui.lKeepLastValues->show(); ui.leKeepLastValues->show(); } } void ImportFileWidget::sourceTypeChanged(int idx) { LiveDataSource::SourceType type = static_cast(idx); switch (type) { case LiveDataSource::SourceType::FileOrPipe:{ ui.lFileName->show(); ui.leFileName->show(); ui.bFileInfo->show(); ui.bOpen->show(); ui.chbLinkFile->show(); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); hideMQTT(); fileNameChanged(ui.leFileName->text()); fileNameChanged(m_fileName); int itemIdx = -1; for (int i = 0; i < ui.cbReadType->count(); ++i) { if (ui.cbReadType->itemData(i).toInt() == LiveDataSource::WholeFile) { itemIdx = i; break; } } if (itemIdx == -1) ui.cbReadType->addItem(i18n("Whole file"), LiveDataSource::WholeFile); break; } case LiveDataSource::SourceType::LocalSocket: ui.lFileName->show(); ui.leFileName->show(); ui.bOpen->show(); ui.bFileInfo->hide(); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); ui.chbLinkFile->hide(); hideMQTT(); ui.gbOptions->setEnabled(true); ui.bManageFilters->setEnabled(true); ui.cbFilter->setEnabled(true); ui.cbFileType->setEnabled(true); for (int i = 0; i < ui.cbReadType->count(); ++i) { if (ui.cbReadType->itemData(i).toInt() == LiveDataSource::WholeFile) ui.cbReadType->removeItem(i); } break; case LiveDataSource::SourceType::NetworkTcpSocket: case LiveDataSource::SourceType::NetworkUdpSocket: ui.lHost->show(); ui.leHost->show(); ui.lePort->show(); ui.lPort->show(); ui.lBaudRate->hide(); ui.cbBaudRate->hide(); ui.lSerialPort->hide(); ui.cbSerialPort->hide(); ui.leID->hide(); ui.lMqttID->hide(); ui.lePassword->hide(); ui.lPassword->hide(); ui.leUsername->hide(); ui.lUsername->hide(); ui.cbQos->hide(); ui.lQos->hide(); ui.cbTopic->hide(); ui.lTopic->hide(); ui.lwSubscriptions->hide(); ui.lSubscriptions->hide(); ui.chbAuthentication->hide(); ui.chbID->hide(); ui.bSubscribe->hide(); ui.bConnect->hide(); ui.gbMqttWill->hide(); ui.chbWill->hide(); ui.chbWillRetain->hide(); ui.cbWillQoS->hide(); ui.cbWillMessageType->hide(); ui.cbWillTopic->hide(); ui.cbWillUpdate->hide(); ui.leWillOwnMessage->hide(); ui.leWillUpdateInterval->setValidator(new QIntValidator(2, 1000000) ); ui.leWillUpdateInterval->hide(); ui.lWillMessageType->hide(); ui.lWillOwnMessage->hide(); ui.lWillQos->hide(); ui.lWillTopic->hide(); ui.lWillUpdate->hide(); ui.lWillUpdateInterval->hide(); ui.lwWillStatistics->hide(); ui.lWillStatistics->hide(); ui.lFileName->hide(); ui.leFileName->hide(); ui.bFileInfo->hide(); ui.bOpen->hide(); ui.chbLinkFile->hide(); ui.gbOptions->setEnabled(true); ui.bManageFilters->setEnabled(true); ui.cbFilter->setEnabled(true); ui.cbFileType->setEnabled(true); for (int i = 0; i < ui.cbReadType->count(); ++i) { if (ui.cbReadType->itemData(i).toInt() == LiveDataSource::WholeFile) ui.cbReadType->removeItem(i); } break; case LiveDataSource::SourceType::SerialPort: ui.lBaudRate->show(); ui.cbBaudRate->show(); ui.lSerialPort->show(); ui.cbSerialPort->show(); ui.lHost->hide(); ui.leHost->hide(); ui.lePort->hide(); ui.lPort->hide(); ui.lFileName->hide(); ui.leFileName->hide(); ui.bFileInfo->hide(); ui.bOpen->hide(); ui.chbLinkFile->hide(); ui.cbFileType->setEnabled(true); hideMQTT(); ui.gbOptions->setEnabled(true); ui.bManageFilters->setEnabled(true); ui.cbFilter->setEnabled(true); for (int i = 0; i < ui.cbReadType->count(); ++i) { if (ui.cbReadType->itemData(i).toInt() == LiveDataSource::WholeFile) ui.cbReadType->removeItem(i); } break; + case LiveDataSource::SourceType::MQTT: #ifdef HAVE_MQTT - case LiveDataSource::SourceType::Mqtt: + ui.lBaudRate->hide(); ui.cbBaudRate->hide(); ui.lSerialPort->hide(); ui.cbSerialPort->hide(); ui.lHost->show(); ui.leHost->show(); ui.lePort->show(); ui.lPort->show(); ui.lFileName->hide(); ui.leFileName->hide(); ui.bFileInfo->hide(); ui.bOpen->hide(); ui.chbLinkFile->hide(); ui.cbFileType->setEnabled(true); ui.leID->hide(); ui.lMqttID->hide(); ui.lePassword->hide(); ui.lPassword->hide(); ui.leUsername->hide(); ui.lUsername->hide(); ui.cbQos->show(); ui.lQos->show(); ui.cbTopic->show(); ui.lTopic->show(); ui.lwSubscriptions->show(); ui.lSubscriptions->show(); ui.chbAuthentication->show(); ui.chbID->show(); ui.bSubscribe->show(); ui.bConnect->show(); ui.gbOptions->setEnabled(true); ui.bManageFilters->setEnabled(true); ui.cbFilter->setEnabled(true); for (int i = 0; i < ui.cbReadType->count(); ++i) { if (ui.cbReadType->itemData(i).toInt() == LiveDataSource::WholeFile) ui.cbReadType->removeItem(i); } ui.gbMqttWill->show(); ui.chbWill->show(); ui.chbWillRetain->hide(); ui.cbWillQoS->hide(); ui.cbWillMessageType->hide(); ui.cbWillTopic->hide(); ui.cbWillUpdate->hide(); ui.leWillOwnMessage->hide(); ui.leWillUpdateInterval->setValidator(new QIntValidator(2, 1000000) ); ui.leWillUpdateInterval->hide(); ui.lWillMessageType->hide(); ui.lWillOwnMessage->hide(); ui.lWillQos->hide(); ui.lWillTopic->hide(); ui.lWillUpdate->hide(); ui.lWillUpdateInterval->hide(); ui.lwWillStatistics->hide(); ui.lWillStatistics->hide(); if(ui.chbWill->isChecked()) { ui.chbWillRetain->show(); ui.cbWillQoS->show(); ui.cbWillMessageType->show(); ui.cbWillTopic->show(); ui.cbWillUpdate->show(); ui.lWillMessageType->show(); ui.lWillQos->show(); ui.lWillTopic->show(); ui.lWillUpdate->show(); if (ui.cbWillMessageType->currentIndex() == static_cast(LiveDataSource::WillMessageType::OwnMessage) ) { ui.leWillOwnMessage->show(); ui.lWillOwnMessage->show(); } else if(ui.cbWillMessageType->currentIndex() == static_cast(LiveDataSource::WillMessageType::Statistics) ){ qDebug()<<"source type changed show statistics"; ui.lWillStatistics->show(); ui.lwWillStatistics->show(); } - if(ui.cbWillUpdate->currentIndex() == 0) { ui.leWillUpdateInterval->show(); ui.lWillUpdateInterval->show(); } else if (ui.cbWillUpdate->currentIndex() == 1) { ui.leWillUpdateInterval->hide(); ui.lWillUpdateInterval->hide(); } } - - break; #endif + break; default: break; } //"update options" groupbox can be deactived for "file and pipe" if the file is invalid. //Activate the groupbox when switching from "file and pipe" to a different sourcy type. if (type != LiveDataSource::SourceType::FileOrPipe) ui.gbUpdateOptions->setEnabled(true); emit sourceTypeChanged(); refreshPreview(); } void ImportFileWidget::initializeAndFillPortsAndBaudRates() { for (int i = 2; i < ui.swOptions->count(); ++i) ui.swOptions->removeWidget(ui.swOptions->widget(i)); const int size = ui.cbFileType->count(); for (int i = 2; i < size; ++i) ui.cbFileType->removeItem(2); ui.cbBaudRate->hide(); ui.lBaudRate->hide(); ui.lHost->hide(); ui.leHost->hide(); ui.lPort->hide(); ui.lePort->hide(); ui.cbSerialPort->hide(); ui.lSerialPort->hide(); ui.cbBaudRate->addItems(LiveDataSource::supportedBaudRates()); ui.cbSerialPort->addItems(LiveDataSource::availablePorts()); ui.leKeepLastValues->setValidator(new QIntValidator(2, 100000)); ui.tabWidget->removeTab(2); } #ifdef HAVE_MQTT void ImportFileWidget::idChecked(int state) { if (state == 2) { ui.leID->show(); ui.lMqttID->show(); } else if (state == 0) { ui.leID->hide(); ui.lMqttID->hide(); } } void ImportFileWidget::authenticationChecked(int state) { if(state == 2) { ui.leUsername->show(); ui.lePassword->show(); ui.lPassword->show(); ui.lUsername->show(); } else if (state == 0) { ui.leUsername->hide(); ui.lePassword->hide(); ui.lUsername->hide(); ui.lPassword->hide(); } } void ImportFileWidget::mqttConnection() { if(m_client->state() == QMqttClient::ClientState::Disconnected) { const bool hostSet = !ui.leHost->text().isEmpty(); const bool portSet = !ui.lePort->text().isEmpty(); const bool idUsed = ui.chbID->isChecked(); const bool idSet = !ui.leID->text().isEmpty(); const bool idValid = !(idUsed && !idSet); const bool authenticationUsed = ui.chbAuthentication->isChecked(); const bool usernameSet = !ui.leUsername->text().isEmpty(); const bool passwordSet = !ui.lePassword->text().isEmpty(); const bool authenticationValid = ! (authenticationUsed && ( !usernameSet || !passwordSet) ); const bool valid =hostSet && portSet && idValid && authenticationValid; if (valid) { m_client->setHostname(ui.leHost->text()); m_client->setPort(ui.lePort->text().toUInt()); if(ui.chbID->isChecked()) m_client->setClientId(ui.leID->text()); if(ui.chbAuthentication->isChecked()) { m_client->setUsername(ui.leUsername->text()); m_client->setPassword(ui.lePassword->text()); } qDebug()<hostname() << " " << m_client->port(); qDebug()<<"Trying to connect"; m_client->connectToHost(); } } else if (m_client->state() == QMqttClient::ClientState::Connected) { qDebug()<<"Disconnecting from mqtt broker" ; m_client->disconnectFromHost(); } } void ImportFileWidget::onMqttConnect() { if(m_client->error() == QMqttClient::NoError) { ui.bConnect->setText("Disconnect"); ui.leHost->setEnabled(false); ui.lePort->setEnabled(false); ui.lePassword->setEnabled(false); ui.leUsername->setEnabled(false); ui.leID->setEnabled(false); ui.cbSourceType->setEnabled(false); ui.chbAuthentication->setEnabled(false); ui.chbID->setEnabled(false); QMessageBox::information(this, "Connection successful", "Connection established"); QMqttTopicFilter globalFilter{"#"}; m_mainSubscription = m_client->subscribe(globalFilter, 1); if(!m_mainSubscription) QMessageBox::information(this, "Couldn't subscribe", "Something went wrong"); } } void ImportFileWidget::mqttSubscribe() { if(m_mqttSubscribeButton) { if(ui.lwSubscriptions->findItems(ui.cbTopic->currentText(), Qt::MatchExactly).isEmpty()) { if(ui.cbTopic->findText( ui.cbTopic->currentText() ) != -1){ QMqttTopicFilter filter {ui.cbTopic->currentText()}; QMqttSubscription *temp_subscription = m_client->subscribe(filter, static_cast (ui.cbQos->currentText().toUInt()) ); if(temp_subscription) { m_mqttSubscriptions.push_back(temp_subscription); ui.lwSubscriptions->addItem(temp_subscription->topic().filter()); connect(temp_subscription, &QMqttSubscription::messageReceived, this, &ImportFileWidget::mqttSubscriptionMessageReceived); m_mqttNewTopic = temp_subscription->topic().filter(); m_messageArrived[temp_subscription->topic().filter()] = false; emit subscriptionMade(); } } else QMessageBox::warning(this, "Warning", "There is no such topic listed in the combo box"); } else QMessageBox::warning(this, "Warning", "You already subscribed to this topic"); } else { if(!m_mqttUnsubscribeTopic.isEmpty()) { QMqttTopicFilter filter{m_mqttUnsubscribeTopic}; m_client->unsubscribe(filter); qDebug()<<"unsubscribe occured"; for(int i = 0; i< m_mqttSubscriptions.count(); ++i) if(m_mqttSubscriptions[i]->topic().filter() == m_mqttUnsubscribeTopic) { qDebug()<<"1 subscription found at "< i(m_messageArrived); while(i.hasNext()) { i.next(); if(i.key().name() == m_mqttUnsubscribeTopic) { m_messageArrived.remove(i.key()); qDebug()<<"2 subscription found at "< j(m_lastMessage); while(j.hasNext()) { j.next(); if(j.key().name() == m_mqttUnsubscribeTopic) { m_lastMessage.remove(j.key()); qDebug()<<"3 subscription found at "<count(); row++) { if(ui.lwSubscriptions->item(row)->text() == m_mqttUnsubscribeTopic) { qDebug()<<"4 subscription found at "<item(row)->text() <<"and removed"; delete ui.lwSubscriptions->item(row); //for(int row2 = row; row2 count(); row2++); } } refreshPreview(); } } } void ImportFileWidget::mqttMessageReceived(const QByteArray &message , const QMqttTopicName &topic) { bool known_topic = false; for(int i = 0; i < ui.cbTopic->count() ; ++i) { if(QString::compare(ui.cbTopic->itemText(i), topic.name(), Qt::CaseInsensitive) == 0) { known_topic = true; break; } } if (known_topic == false) { ui.cbTopic->addItem(topic.name()); emit newTopic(topic.name()); } } void ImportFileWidget::setCompleter(QString topic) { if(!m_editing) { m_topicList.append(topic); m_completer = new QCompleter(m_topicList, this); m_completer->setCompletionMode(QCompleter::PopupCompletion); m_completer->setCaseSensitivity(Qt::CaseSensitive); ui.cbTopic->setCompleter(m_completer); } } void ImportFileWidget::topicBeingTyped(const QString topic) { if(!m_editing) { bool found = false; for (int i=0; icount(); ++i) { if(QString::compare(ui.cbTopic->itemText(i), topic, Qt::CaseSensitive) == 0) found = true; } if(!found) { qDebug() << topic; m_editing = true; m_timer->start(); } } } void ImportFileWidget::topicTimeout() { qDebug()<<"lejart ido"; m_editing = false; m_timer->stop(); } bool ImportFileWidget::isMqttValid(){ bool connected = (m_client->state() == QMqttClient::ClientState::Connected); bool subscribed = !m_topicList.isEmpty(); return connected && subscribed; } void ImportFileWidget::mqttSubscriptionMessageReceived(const QMqttMessage &msg) { if(m_messageArrived[msg.topic()] == false) { m_messageArrived[msg.topic()] = true; } m_lastMessage[msg.topic()]= msg; bool check = true; QMapIterator i(m_messageArrived); while(i.hasNext()) { i.next(); if(i.value() == false ) { check = false; break; } } if (check == true) m_mqttReadyForPreview = true; if(m_mqttReadyForPreview && !m_mqttNewTopic.isEmpty() && m_messageArrived[m_mqttNewTopic]) { qDebug() << "New topic for preview: " << m_mqttNewTopic; m_mqttNewTopic.clear(); refreshPreview(); } } void ImportFileWidget::onMqttDisconnect() { ui.bConnect->setText("Connect"); ui.leHost->setEnabled(true); ui.leHost->clear(); ui.lePort->setEnabled(true); ui.lePort->clear(); ui.lePassword->setEnabled(true); ui.lePassword->clear(); ui.leUsername->setEnabled(true); ui.leUsername->clear(); ui.leID->setEnabled(true); ui.leID->clear(); ui.cbTopic->clear(); ui.lwSubscriptions->clear(); ui.cbSourceType->setEnabled(true); ui.chbAuthentication->setEnabled(true); ui.chbID->setEnabled(true); m_mqttNewTopic.clear(); m_mqttReadyForPreview = false; m_mqttSubscriptions.clear(); delete m_completer; m_completer = new QCompleter; m_topicList.clear(); m_editing = false; m_timer->stop(); m_messageArrived.clear(); m_lastMessage.clear(); } void ImportFileWidget::mqttButtonSubscribe(const QString& text) { if(!m_mqttSubscribeButton) { ui.bSubscribe->setText("Subscribe"); m_mqttSubscribeButton = true; } } void ImportFileWidget::mqttButtonUnsubscribe(const QString& item) { qDebug()<< "trying to set unsubscribe, mqttSubscribeButton's value: "<setText("Unsubscribe"); m_mqttSubscribeButton = false; m_mqttUnsubscribeTopic = item; qDebug()<<"Unsubscribe from:"<show(); ui.cbWillQoS->show(); ui.cbWillMessageType->show(); ui.cbWillTopic->show(); ui.cbWillUpdate->show(); ui.lWillMessageType->show(); ui.lWillQos->show(); ui.lWillTopic->show(); ui.lWillUpdate->show(); if (ui.cbWillMessageType->currentIndex() == static_cast(LiveDataSource::WillMessageType::OwnMessage) ) { ui.leWillOwnMessage->show(); ui.lWillOwnMessage->show(); } else if(ui.cbWillMessageType->currentIndex() == static_cast(LiveDataSource::WillMessageType::Statistics) ){ qDebug()<<"will use checked show statistics"; ui.lWillStatistics->show(); ui.lwWillStatistics->show(); } if(ui.cbWillUpdate->currentIndex() == 0) { ui.leWillUpdateInterval->show(); ui.lWillUpdateInterval->show(); } } else if (state == Qt::Unchecked) { qDebug()<<"will use unchecked"; ui.chbWillRetain->hide(); ui.cbWillQoS->hide(); ui.cbWillMessageType->hide(); ui.cbWillTopic->hide(); ui.cbWillUpdate->hide(); ui.leWillOwnMessage->hide(); ui.leWillUpdateInterval->hide(); ui.lWillMessageType->hide(); ui.lWillOwnMessage->hide(); ui.lWillQos->hide(); ui.lWillTopic->hide(); ui.lWillUpdate->hide(); ui.lWillUpdateInterval->hide(); ui.lWillStatistics->hide(); ui.lwWillStatistics->hide(); } } void ImportFileWidget::willMessageTypeChanged(int type) { if(static_cast (type) == LiveDataSource::WillMessageType::OwnMessage) { ui.leWillOwnMessage->show(); ui.lWillOwnMessage->show(); ui.lWillStatistics->hide(); ui.lwWillStatistics->hide(); } else if(static_cast (type) == LiveDataSource::WillMessageType::LastMessage) { ui.leWillOwnMessage->hide(); ui.lWillOwnMessage->hide(); ui.lWillStatistics->hide(); ui.lwWillStatistics->hide(); } else if(static_cast (type) == LiveDataSource::WillMessageType::Statistics) { qDebug()<<"will message type changed show statistics"; ui.lWillStatistics->show(); ui.lwWillStatistics->show(); ui.leWillOwnMessage->hide(); ui.lWillOwnMessage->hide(); } } void ImportFileWidget::updateWillTopics() { for(int i = 0; i < ui.lwSubscriptions->count(); ++i) { QListWidgetItem* item = ui.lwSubscriptions->item(i); if(ui.cbWillTopic->findText(item->text()) < 0) ui.cbWillTopic->addItem(item->text()); } } void ImportFileWidget::willUpdateChanged(int updateType) { if(static_cast(updateType) == LiveDataSource::WillUpdateType::TimePeriod) { ui.leWillUpdateInterval->show(); ui.lWillUpdateInterval->show(); } else if (static_cast(updateType) == LiveDataSource::WillUpdateType::OnClick) { ui.leWillUpdateInterval->hide(); ui.lWillUpdateInterval->hide(); } } #endif void ImportFileWidget::hideMQTT() { ui.leID->hide(); ui.lMqttID->hide(); ui.lePassword->hide(); ui.lPassword->hide(); ui.leUsername->hide(); ui.lUsername->hide(); ui.cbQos->hide(); ui.lQos->hide(); ui.cbTopic->hide(); ui.lTopic->hide(); ui.lwSubscriptions->hide(); ui.lSubscriptions->hide(); ui.chbAuthentication->hide(); ui.chbID->hide(); ui.bSubscribe->hide(); ui.bConnect->hide(); ui.gbMqttWill->hide(); ui.chbWill->hide(); ui.chbWillRetain->hide(); ui.cbWillQoS->hide(); ui.cbWillMessageType->hide(); ui.cbWillTopic->hide(); ui.cbWillUpdate->hide(); ui.leWillOwnMessage->hide(); ui.leWillUpdateInterval->setValidator(new QIntValidator(2, 1000000) ); ui.leWillUpdateInterval->hide(); ui.lWillMessageType->hide(); ui.lWillOwnMessage->hide(); ui.lWillQos->hide(); ui.lWillTopic->hide(); ui.lWillUpdate->hide(); ui.lWillUpdateInterval->hide(); ui.lwWillStatistics->hide(); ui.lWillStatistics->hide(); } #ifdef HAVE_MQTT void ImportFileWidget::mqttErrorChanged(QMqttClient::ClientError clientError) { switch (clientError) { case QMqttClient::BadUsernameOrPassword: QMessageBox::warning(this, "Couldn't connect", "Bad username or password"); break; case QMqttClient::IdRejected: QMessageBox::warning(this, "Couldn't connect", "The client ID wasn't accepted"); break; case QMqttClient::ServerUnavailable: QMessageBox::warning(this, "Server unavailable", "The network connection has been established, but the service is unavailable on the broker side."); break; case QMqttClient::NotAuthorized: QMessageBox::warning(this, "Couldn't connect", "The client is not authorized to connect."); break; case QMqttClient::UnknownError: QMessageBox::warning(this, "Unknown MQTT error", "An unknown error occurred."); break; default: break; } } #endif