地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
6.4 KiB

/*===================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL 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 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL 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 QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
/**
* @file
* @brief Implementation of class LogCompressor. This class reads in a file containing messages and translates it into a tab-delimited CSV file.
* @author Lorenz Meier <mavteam@student.ethz.ch>
*
*/
#include <QFile>
#include <QTemporaryFile>
#include <QTextStream>
#include <QStringList>
#include <QFileInfo>
#include <QList>
#include "LogCompressor.h"
#include <QDebug>
/**
* Initializes all the variables necessary for a compression run. This won't actually happen
* until startCompression(...) is called.
*/
LogCompressor::LogCompressor(QString logFileName, QString outFileName, QString delimiter) :
logFileName(logFileName),
outFileName(outFileName),
running(true),
currentDataLine(0),
delimiter(delimiter),
holeFillingEnabled(true)
{
}
void LogCompressor::run()
{
// Verify that the input file is useable
QFile infile(logFileName);
if (!infile.exists() || !infile.open(QIODevice::ReadOnly | QIODevice::Text)) {
emit logProcessingStatusChanged(tr("Log Compressor: Cannot start/compress log file, since input file %1 is not readable").arg(QFileInfo(infile.fileName()).absoluteFilePath()));
return;
}
// outFileName = logFileName;
QString outFileName;
QStringList parts = QFileInfo(infile.fileName()).absoluteFilePath().split(".", QString::SkipEmptyParts);
parts.replace(parts.size()-2, "compressed." + parts.last());
outFileName = parts.join(".");
// Verify that the output file is useable
QFile outTmpFile(outFileName);
if (!outTmpFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
emit logProcessingStatusChanged(tr("Log Compressor: Cannot start/compress log file, since output file %1 is not writable").arg(QFileInfo(outTmpFile.fileName()).absoluteFilePath()));
return;
}
// First we search the input file through keySearchLimit number of lines
// looking for variables. This is neccessary before CSV files require
// the same number of fields for every line.
const unsigned int keySearchLimit = 15000;
unsigned int keyCounter = 0;
QTextStream in(&infile);
QMap<QString, int> messageMap;
while (!in.atEnd() && keyCounter < keySearchLimit) {
QString messageName = in.readLine().split(delimiter).at(2);
messageMap.insert(messageName, 0);
++keyCounter;
}
// Now update each key with its index in the output string. These are
// all offset by one to account for the first field: timestamp_ms.
QMap<QString, int>::iterator i = messageMap.begin();
int j;
for (i = messageMap.begin(), j = 1; i != messageMap.end(); ++i, ++j) {
i.value() = j;
}
// Open the output file and write the header line to it
QStringList headerList(messageMap.keys());
QString headerLine = "timestamp_ms" + delimiter + headerList.join(delimiter) + "\n";
outTmpFile.write(headerLine.toLocal8Bit());
emit logProcessingStatusChanged(tr("Log compressor: Dataset contains dimension: ") + headerLine);
// Reset our position in the input file before we start the main processing loop.
infile.reset();
in.reset();
in.resetStatus();
// Template list stores a list for populating with data as it's parsed from messages.
QStringList templateList;
for (int i = 0; i < headerList.size() + 1; ++i) {
templateList << (holeFillingEnabled?"NaN":"");
}
QStringList filledList(templateList);
QStringList currentLine = in.readLine().split(delimiter);
currentDataLine = 1;
while (!in.atEnd()) {
// We only overwrite data from the last time set if we aren't doing a zero-order hold
if (!holeFillingEnabled) {
filledList = templateList;
}
// Populate this time set with the data from this first message
filledList.replace(0, currentLine.at(0));
filledList.replace(messageMap.value(currentLine.at(2)), currentLine.at(3));
// Continue searching for messages in the same time set and adding that data
// to the current time set if appropriate.
while (!in.atEnd()) {
QStringList newLine = in.readLine().split(delimiter);
++currentDataLine;
if (newLine.at(0) == currentLine.at(0)) {
QString currentDataName = newLine.at(2);
QString currentDataValue = newLine.at(3);
filledList.replace(messageMap.value(currentDataName), currentDataValue);
} else {
currentLine = newLine;
break;
}
}
// Write this current time set out to the file
QString output = filledList.join(delimiter) + "\n";
outTmpFile.write(output.toLocal8Bit());
}
// We're now done with the source file
infile.close();
// Make sure we remove the source file before replacing it.
// QFile::remove(outFileName);
// outTmpFile.copy(outFileName);
// outTmpFile.close();
emit logProcessingStatusChanged(tr("Log Compressor: Writing output to file %1").arg(QFileInfo(outFileName).absoluteFilePath()));
// Clean up and update the status before we return.
currentDataLine = 0;
emit logProcessingStatusChanged(tr("Log compressor: Finished processing file: %1").arg(outFileName));
emit finishedFile(outFileName);
qDebug() << "Done with logfile processing";
running = false;
}
/**
* @param holeFilling If hole filling is enabled, the compressor tries to fill empty data fields with previous
* values from the same variable (or NaN, if no previous value existed)
*/
void LogCompressor::startCompression(bool holeFilling)
{
holeFillingEnabled = holeFilling;
start();
}
bool LogCompressor::isFinished()
{
return !running;
}
int LogCompressor::getCurrentLine()
{
return currentDataLine;
}