File indexing completed on 2024-05-26 03:51:16

0001 /*
0002     File                 : SpiceFilter.cpp
0003     Project              : LabPlot
0004     Description          : Filters for reading spice files
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2022 Martin Marmsoler <martin.marmsoler@gmail.com>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "SpiceFilter.h"
0010 #include "SpiceFilterPrivate.h"
0011 #include "SpiceReader.h"
0012 #include "backend/datasources/AbstractDataSource.h"
0013 
0014 #include <QXmlStreamWriter>
0015 
0016 #include "backend/lib/macros.h"
0017 
0018 const QString SpiceFilter::xmlElementName = QStringLiteral("SpiceFilter");
0019 
0020 SpiceFilter::SpiceFilter()
0021     : AbstractFileFilter(FileType::Spice)
0022     , d(new SpiceFilterPrivate(this)) {
0023 }
0024 
0025 SpiceFilter::~SpiceFilter() = default;
0026 
0027 bool SpiceFilter::isSpiceFile(const QString& fileName, bool* binary) {
0028     SpiceFileReader reader(fileName);
0029     if (!reader.open())
0030         return false;
0031 
0032     if (!reader.validSpiceFile())
0033         return false;
0034 
0035     if (binary)
0036         *binary = reader.binary();
0037 
0038     return true;
0039 }
0040 
0041 QString SpiceFilter::fileInfoString(const QString& fileName) {
0042     SpiceFileReader reader(fileName);
0043     if (!reader.open())
0044         return {};
0045 
0046     if (!reader.validSpiceFile())
0047         return {};
0048 
0049     return reader.infoString();
0050 }
0051 
0052 QVector<QStringList> SpiceFilter::preview(const QString& fileName, int lines) {
0053     return d->preview(fileName, lines);
0054 }
0055 
0056 /*!
0057   reads the content of the file \c fileName.
0058 */
0059 void SpiceFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) {
0060     d->readDataFromFile(fileName, dataSource, importMode);
0061 }
0062 
0063 /*!
0064   writes the content of the data source \c dataSource to the file \c fileName.
0065 */
0066 void SpiceFilter::write(const QString& fileName, AbstractDataSource* dataSource) {
0067     d->write(fileName, dataSource);
0068 }
0069 
0070 void SpiceFilter::setStartRow(const int r) {
0071     d->startRow = r;
0072 }
0073 int SpiceFilter::startRow() const {
0074     return d->startRow;
0075 }
0076 
0077 void SpiceFilter::setEndRow(const int r) {
0078     d->endRow = r;
0079 }
0080 int SpiceFilter::endRow() const {
0081     return d->endRow;
0082 }
0083 
0084 QStringList SpiceFilter::vectorNames() const {
0085     return d->vectorNames;
0086 }
0087 
0088 QVector<AbstractColumn::ColumnMode> SpiceFilter::columnModes() {
0089     return d->columnModes;
0090 }
0091 
0092 // #####################################################################
0093 // ################### Private implementation ##########################
0094 // #####################################################################
0095 SpiceFilterPrivate::SpiceFilterPrivate(SpiceFilter* owner)
0096     : q(owner) {
0097 }
0098 
0099 void SpiceFilterPrivate::generateVectorNamesColumnModes(const SpiceFileReader& reader) {
0100     // add names of the variables
0101     vectorNames.clear();
0102     columnModes.clear();
0103     for (const auto& variable : reader.variables()) {
0104         if (!reader.isReal()) {
0105             vectorNames << variable.variableName + QStringLiteral(", ") + variable.type + QStringLiteral(" REAL");
0106             vectorNames << variable.variableName + QStringLiteral(", ") + variable.type + QStringLiteral(" IMAGINARY");
0107             columnModes << AbstractColumn::ColumnMode::Double;
0108             columnModes << AbstractColumn::ColumnMode::Double;
0109         } else {
0110             vectorNames << variable.variableName + QStringLiteral(", ") + variable.type;
0111             columnModes << AbstractColumn::ColumnMode::Double;
0112         }
0113     }
0114 }
0115 
0116 /*!
0117  * generates the preview for the file \c fileName reading the provided number of \c lines.
0118  */
0119 QVector<QStringList> SpiceFilterPrivate::preview(const QString& fileName, int lines) {
0120     DEBUG(Q_FUNC_INFO);
0121     QVector<QStringList> dataStrings;
0122 
0123     SpiceFileReader reader(fileName);
0124 #ifdef SPICEFILTERTEST_EN
0125     reader.setBulkReadLines(q->mBulkLineCount);
0126 #endif
0127     if (!reader.open() || !reader.validSpiceFile())
0128         return dataStrings;
0129 
0130     generateVectorNamesColumnModes(reader);
0131 
0132     // prepare the data container
0133     const int numberVariables = reader.variables().count();
0134 
0135     // skip data lines, if required
0136     const int skip = startRow - 1;
0137 
0138     // create new datacontainer to store the preview
0139     std::vector<void*> dataContainer;
0140     dataContainer.resize(numberVariables * (1 + !reader.isReal()));
0141     for (uint i = 0; i < dataContainer.size(); i++)
0142         dataContainer[i] = new QVector<double>(lines);
0143 
0144     const int linesRead = reader.readData(dataContainer, skip, lines);
0145 
0146     QStringList lineString;
0147     int isComplex = !reader.isReal();
0148 
0149     for (int l = 0; l < linesRead; l++) {
0150         lineString.clear();
0151         for (int i = 0; i < numberVariables * (1 + isComplex); i++) {
0152             const auto values = static_cast<const QVector<double>*>(dataContainer[i]);
0153             lineString << QString::number(values->at(l), 'e', 15); // real part
0154         }
0155         dataStrings << lineString;
0156     }
0157 
0158     // delete all element of the datacontainer again
0159     for (uint i = 0; i < dataContainer.size(); i++)
0160         delete static_cast<QVector<double>*>(dataContainer[i]);
0161 
0162     return dataStrings;
0163 }
0164 
0165 /*!
0166     reads the content of the file \c fileName to the data source \c dataSource. Uses the settings defined in the data source.
0167 */
0168 void SpiceFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) {
0169     DEBUG(Q_FUNC_INFO << ", fileName = \'" << STDSTRING(fileName) << "\', dataSource = " << dataSource
0170                       << ", mode = " << ENUM_TO_STRING(AbstractFileFilter, ImportMode, importMode));
0171 
0172     SpiceFileReader reader(fileName);
0173 #ifdef SPICEFILTERTEST_EN
0174     reader.setBulkReadLines(q->mBulkLineCount);
0175 #endif
0176     if (!reader.open() || !reader.validSpiceFile())
0177         return;
0178 
0179     q->connect(&reader, &SpiceFileReader::processed, [=](double processed) {
0180         Q_EMIT q->completed(processed);
0181     });
0182 
0183     generateVectorNamesColumnModes(reader);
0184 
0185     // prepare the data container
0186     const int numberVariables = reader.variables().count();
0187     const int actualEndRow = (endRow == -1 || endRow > reader.numberSimulationPoints()) ? reader.numberSimulationPoints() : endRow;
0188     const int actualRows = actualEndRow - startRow + 1;
0189     const int actualCols = reader.isReal() ? numberVariables : 2 * numberVariables;
0190     // resize dataContainer
0191     const int columnOffset = dataSource->prepareImport(m_dataContainer, importMode, actualRows, actualCols, vectorNames, columnModes);
0192 
0193     // skip data lines, if required
0194     const int skip = startRow - 1;
0195     reader.readData(m_dataContainer, skip, actualRows);
0196 
0197     dataSource->finalizeImport(columnOffset, 1, actualCols, QString(), importMode);
0198 }
0199 
0200 /*!
0201     writes the content of \c dataSource to the file \c fileName.
0202 */
0203 void SpiceFilterPrivate::write(const QString& /*fileName*/, AbstractDataSource* /*dataSource*/) {
0204     // TODO: not implemented yet
0205 }
0206 
0207 // ##############################################################################
0208 // ##################  Serialization/Deserialization  ###########################
0209 // ##############################################################################
0210 /*!
0211   Saves as XML.
0212  */
0213 void SpiceFilter::save(QXmlStreamWriter* writer) const {
0214     writer->writeStartElement(xmlElementName);
0215     writer->writeEndElement();
0216 }
0217 
0218 /*!
0219   Loads from XML.
0220 */
0221 bool SpiceFilter::load(XmlStreamReader*) {
0222     return true;
0223 }