diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp index eb3b37e2..1504ad8c 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp +++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp @@ -829,7 +829,7 @@ static QString relativePath(const QString & inpath) static bool chunksIdentical(const PRS1DataChunk* a, const PRS1DataChunk* b) { - return (a->timestamp == b->timestamp && a->storedCrc == b->storedCrc); + return (a->hash() == b->hash()); } static QString chunkComparison(const PRS1DataChunk* a, const PRS1DataChunk* b) @@ -942,11 +942,19 @@ void PRS1Loader::ScanFiles(const QStringList & paths, int sessionid_base, Machin } if (ext == 5) { - if (!task->wavefile.isEmpty()) continue; + if (!task->wavefile.isEmpty()) { + qDebug() << sid << "already has waveform file" << relativePath(task->wavefile) + << "skipping" << relativePath(fi.canonicalFilePath()); + continue; + } task->wavefile = fi.canonicalFilePath(); } else if (ext == 6) { qWarning() << fi.canonicalFilePath() << "oximetry is untested"; // TODO: mark as untested/unexpected - if (!task->oxifile.isEmpty()) continue; + if (!task->oxifile.isEmpty()) { + qDebug() << sid << "already has oximetry file" << relativePath(task->oxifile) + << "skipping" << relativePath(fi.canonicalFilePath()); + continue; + } task->oxifile = fi.canonicalFilePath(); } diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.h b/oscar/SleepLib/loader_plugins/prs1_loader.h index 9145caff..dfb3e25f 100644 --- a/oscar/SleepLib/loader_plugins/prs1_loader.h +++ b/oscar/SleepLib/loader_plugins/prs1_loader.h @@ -119,6 +119,9 @@ public: quint32 storedCrc; // header + data CRC stored in file, last 2-4 bytes of chunk quint32 calcCrc; // header + data CRC as calculated when parsing + //! \brief Calculate a simplistic hash to check whether two chunks are identical. + inline quint64 hash(void) const { return ((((quint64) this->calcCrc) << 32) | this->timestamp); } + //! \brief Parse and return the next chunk from a PRS1 file static PRS1DataChunk* ParseNext(class QFile & f); diff --git a/oscar/tests/prs1tests.cpp b/oscar/tests/prs1tests.cpp index 85b0b7f5..1ccb3412 100644 --- a/oscar/tests/prs1tests.cpp +++ b/oscar/tests/prs1tests.cpp @@ -238,6 +238,7 @@ void parseAndEmitChunkYaml(const QString & path) { qDebug() << path; + QHash<QString,QSet<quint64>> written; QStringList paths; QString propertyfile; int sessionid_base; @@ -293,7 +294,9 @@ void parseAndEmitChunkYaml(const QString & path) QString suffix = QString(".%1-chunks.yml").arg(ext, 3, 10, QChar('0')); QString outpath = prs1OutputPath(path, m->serial(), sessionid, suffix); QFile file(outpath); - if (!file.open(QFile::WriteOnly | QFile::Truncate)) { + // Truncate the first time we open this file, to clear out any previous test data. + // Otherwise append, allowing session chunks to be split among multiple files. + if (!file.open(QFile::WriteOnly | (written.contains(outpath) ? QFile::Append : QFile::Truncate))) { qDebug() << outpath; Q_ASSERT(false); } @@ -302,30 +305,38 @@ void parseAndEmitChunkYaml(const QString & path) // keep only P1234568/Pn/00000000.001 QStringList pathlist = QDir::toNativeSeparators(inpath).split(QDir::separator(), QString::SkipEmptyParts); QString relative = pathlist.mid(pathlist.size()-3).join(QDir::separator()); - out << "file: " << relative << endl; + bool first_chunk_from_file = true; // Parse the chunks in the file. QList<PRS1DataChunk *> chunks = s_loader->ParseFile(inpath); for (int i=0; i < chunks.size(); i++) { PRS1DataChunk * chunk = chunks.at(i); - bool ok = true; - - // Parse the inner data. - switch (chunk->ext) { - case 0: ok = chunk->ParseCompliance(); break; - case 1: ok = chunk->ParseSummary(); break; - case 2: ok = chunk->ParseEvents(); break; - case 5: break; // skip flow/pressure waveforms - case 6: // skip oximetry data (but log it) - qWarning() << relative << "oximetry is untested"; // never encountered - break; - default: - qWarning() << relative << "unexpected file type"; - break; + // Only write unique chunks to the file. + if (written[outpath].contains(chunk->hash()) == false) { + if (first_chunk_from_file) { + out << "file: " << relative << endl; + first_chunk_from_file = false; + } + bool ok = true; + + // Parse the inner data. + switch (chunk->ext) { + case 0: ok = chunk->ParseCompliance(); break; + case 1: ok = chunk->ParseSummary(); break; + case 2: ok = chunk->ParseEvents(); break; + case 5: break; // skip flow/pressure waveforms + case 6: // skip oximetry data (but log it) + qWarning() << relative << "oximetry is untested"; // never encountered + break; + default: + qWarning() << relative << "unexpected file type"; + break; + } + + // Emit the YAML. + ChunkToYaml(out, chunk, ok); + written[outpath] += chunk->hash(); } - - // Emit the YAML. - ChunkToYaml(out, chunk, ok); delete chunk; }