From 9309a57839db672926f0938f74a67d8a7511052f Mon Sep 17 00:00:00 2001 From: Mark Watkins Date: Tue, 5 Aug 2014 01:40:56 +1000 Subject: [PATCH] Fix PRS1 buried session issue --- .../SleepLib/loader_plugins/prs1_loader.cpp | 279 ++++++++---------- .../SleepLib/loader_plugins/prs1_loader.h | 83 ++---- 2 files changed, 151 insertions(+), 211 deletions(-) diff --git a/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp b/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp index bece790e..21a41958 100644 --- a/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp @@ -404,10 +404,11 @@ int PRS1Loader::OpenMachine(Machine *m, QString path) int size = paths.size(); - prs1sessions.clear(); + sesstasks.clear(); new_sessions.clear(); // this hash is used by OpenFile + PRS1Import * task = nullptr; // Note, I have observed p0/p1/etc folders containing duplicates session files (in Robin Sanders data.) // for each p0/p1/p2/etc... folder @@ -421,16 +422,18 @@ int PRS1Loader::OpenMachine(Machine *m, QString path) // Scan for individual session files for (int i = 0; i < flist.size(); i++) { QFileInfo fi = flist.at(i); - QString ext_s = fi.fileName().section(".", -1); - QString session_s = fi.fileName().section(".", 0, -2); - ext = ext_s.toLong(&ok); - if (!ok) { // not a numerical extension + QString ext_s = fi.fileName().section(".", -1); + ext = ext_s.toInt(&ok); + if (!ok) { + // not a numerical extension continue; } - sid = session_s.toLong(&ok); - if (!ok) { // not a numerical session ID + QString session_s = fi.fileName().section(".", 0, -2); + sid = session_s.toInt(&ok); + if (!ok) { + // not a numerical session ID continue; } @@ -439,41 +442,57 @@ int PRS1Loader::OpenMachine(Machine *m, QString path) continue; } - PRS1FileGroup * group = nullptr; + if (ext == 5) { + // Waveform files aren't grouped... so we just want to add the filename for later + QHash::iterator it = sesstasks.find(sid); + if (it != sesstasks.end()) { + task = it.value(); + } else { + // Create the group if we see it first.. + task = new PRS1Import(this, sid, m); + sesstasks[sid] = task; + queTask(task); + } - // There is a Problem here - // The previous session could have a data chunk that encompasses this sessions data, causing duplicate sessions during initial import. + if (!task->waveform.isEmpty()) continue; + task->waveform = fi.canonicalFilePath(); - QHash::iterator it = prs1sessions.find(sid); - if (it != prs1sessions.end()) { - group = it.value(); - } else { - group = new PRS1FileGroup(); - prs1sessions[sid] = group; - // save a loop an que this now - queTask(new PRS1Import(this, sid, group, m)); - } + continue; + } + // Parse the data chunks and read the files.. + QList Chunks = ParseFile(fi.canonicalFilePath()); - switch (ext) { - case 0: - if (!group->compliance.isEmpty()) continue; - group->compliance = fi.canonicalFilePath(); - break; - case 1: - if (!group->summary.isEmpty()) continue; - group->summary = fi.canonicalFilePath(); - break; - case 2: - if (!group->event.isEmpty()) continue; - group->event = fi.canonicalFilePath(); - break; - case 5: - if (!group->waveform.isEmpty()) continue; - group->waveform = fi.canonicalFilePath(); - break; - default: - break; + for (int i=0; i < Chunks.size(); ++i) { + PRS1DataChunk * chunk = Chunks.at(i); + sid = chunk->sessionid; + + task = nullptr; + QHash::iterator it = sesstasks.find(sid); + if (it != sesstasks.end()) { + task = it.value(); + } else { + task = new PRS1Import(this, sid, m); + sesstasks[sid] = task; + // save a loop an que this now + queTask(task); + } + switch (ext) { + case 0: + if (task->compliance) continue; + task->compliance = chunk; + break; + case 1: + if (task->summary) continue; + task->summary = chunk; + break; + case 2: + if (task->event) continue; + task->event = chunk; + break; + default: + break; + } } } } @@ -485,7 +504,7 @@ int PRS1Loader::OpenMachine(Machine *m, QString path) return tasks; } -bool PRS1SessionData::ParseF5Events() +bool PRS1Import::ParseF5Events() { ChannelID Codes[] = { PRS1_00, PRS1_01, CPAP_Pressure, CPAP_EPAP, CPAP_PressurePulse, CPAP_Obstructive, @@ -826,7 +845,7 @@ bool PRS1SessionData::ParseF5Events() } -bool PRS1SessionData::ParseF0Events() +bool PRS1Import::ParseF0Events() { unsigned char code=0; EventList *Code[0x20] = {0}; @@ -1101,17 +1120,23 @@ bool PRS1SessionData::ParseF0Events() } -bool PRS1SessionData::ParseCompliance() +bool PRS1Import::ParseCompliance() { // Bleh!! There is probably 10 different formats for these useless piece of junk machines if (!compliance) return false; return true; } -bool PRS1SessionData::ParseSummaryF0() +bool PRS1Import::ParseSummaryF0() { const unsigned char * data = (unsigned char *)summary->m_data.constData(); + if (data[0x00] > 0) { + return false; + } + + session->set_first(qint64(summary->timestamp) * 1000L); + CPAPMode cpapmode = MODE_UNKNOWN; switch (data[0x02]) { // PRS1 mode // 0 = CPAP, 2 = APAP @@ -1223,10 +1248,16 @@ bool PRS1SessionData::ParseSummaryF0() return true; } -bool PRS1SessionData::ParseSummaryF0V4() +bool PRS1Import::ParseSummaryF0V4() { const unsigned char * data = (unsigned char *)summary->m_data.constData(); + if (data[0x00] > 0) { + return false; + } + + session->set_first(qint64(summary->timestamp) * 1000L); + CPAPMode cpapmode = MODE_UNKNOWN; switch (data[0x02]) { // PRS1 mode // 0 = CPAP, 2 = APAP @@ -1306,10 +1337,16 @@ bool PRS1SessionData::ParseSummaryF0V4() } -bool PRS1SessionData::ParseSummaryF3() +bool PRS1Import::ParseSummaryF3() { const unsigned char * data = (unsigned char *)summary->m_data.constData(); + if (data[0x00] > 0) { + return false; + } + + session->set_first(qint64(summary->timestamp) * 1000L); + EventDataType epap = data[0x04] | (data[0x05] << 8); EventDataType ipap = data[0x06] | (data[0x07] << 8); @@ -1318,10 +1355,16 @@ bool PRS1SessionData::ParseSummaryF3() return true; } -bool PRS1SessionData::ParseSummaryF5() +bool PRS1Import::ParseSummaryF5() { const unsigned char * data = (unsigned char *)summary->m_data.constData(); + if (data[0x00] > 0) { + return false; + } + + session->set_first(qint64(summary->timestamp) * 1000L); + CPAPMode cpapmode = MODE_UNKNOWN; // switch (data[0x01]) { // PRS1 mode // 0 = CPAP, 2 = APAP @@ -1404,7 +1447,7 @@ bool PRS1SessionData::ParseSummaryF5() -bool PRS1SessionData::ParseSummary() +bool PRS1Import::ParseSummary() { // Family 0 = XPAP @@ -1413,9 +1456,6 @@ bool PRS1SessionData::ParseSummary() if (!summary) return false; - if (summary->m_data.size() < 59) { - //return false; - } session->setPhysMax(CPAP_LeakTotal, 120); session->setPhysMin(CPAP_LeakTotal, 0); @@ -1428,11 +1468,6 @@ bool PRS1SessionData::ParseSummary() session->setPhysMax(CPAP_PS, 25); session->setPhysMin(CPAP_PS, 0); - session->set_first(qint64(summary->timestamp) * 1000L); - - if (this->session->session() == 3880) { - int i=5; - } switch (summary->family) { case 0: @@ -1586,7 +1621,7 @@ bool PRS1SessionData::ParseSummary() return true; } -bool PRS1SessionData::ParseEvents() +bool PRS1Import::ParseEvents() { bool res = false; if (!event) return false; @@ -1652,7 +1687,7 @@ bool PRS1SessionData::ParseEvents() return res; } -bool PRS1SessionData::ParseWaveforms() +bool PRS1Import::ParseWaveforms() { int size = waveforms.size(); @@ -1702,80 +1737,53 @@ bool PRS1SessionData::ParseWaveforms() void PRS1Import::run() { - group->ParseChunks(loader); + session = new Session(mach, sessionid); - QMap::iterator it; - - // Do session lists.. - for (it = group->sessions.begin(); it != group->sessions.end(); ++it) { - PRS1SessionData * sg = it.value(); - - sg->session = new Session(mach, it.key()); - - if (!sg->ParseSummary()) { -// delete sg->session; -// continue; + if (summary && ParseSummary()) { + if (event && !ParseEvents()) { } - if (!sg->ParseEvents()) { - // delete sg->session; - // continue; + waveforms = loader->ParseFile(waveform); + ParseWaveforms(); + + if (session->first() > 0) { + if (session->last() < session->first()) { + // if last isn't set, duration couldn't be gained from summary, parsing events or waveforms.. + // This session is dodgy, so kill it + session->really_set_last(session->first()); + } + session->SetChanged(true); + + loader->addSession(session); + + // Update indexes, process waveform and perform flagging + session->UpdateSummaries(); + + // Save is not threadsafe + loader->saveMutex.lock(); + session->Store(mach->getDataPath()); + loader->saveMutex.unlock(); + + session->TrashEvents(); } - sg->ParseWaveforms(); - if (sg->session->last() < sg->session->first()) { - // if last isn't set, duration couldn't be gained from summary, parsing events or waveforms.. - // This session is dodgy, so kill it - sg->session->really_set_last(sg->session->first()); - } - sg->session->SetChanged(true); - - loader->addSession(sg->session); - - // Update indexes, process waveform and perform flagging - sg->session->UpdateSummaries(); - - // Save is not threadsafe - loader->saveMutex.lock(); - sg->session->Store(mach->getDataPath()); - loader->saveMutex.unlock(); - - sg->session->TrashEvents(); - - delete sg; } - delete group; } -void PRS1FileGroup::ParseChunks(PRS1Loader * ldr) +QList PRS1Loader::ParseFile(QString path) { - loader = ldr; + QList CHUNKS; -// qDebug() << "Parsing chunks for session" << summary << event; - if (ParseFile(compliance)) { - // Compliance only piece of crap machine, nothing else to do.. :( - // return; - } - - ParseFile(summary); - ParseFile(event); - ParseFile(waveform); - - -} - -bool PRS1FileGroup::ParseFile(QString path) -{ if (path.isEmpty()) - return false; + return CHUNKS; QFile f(path); if (!f.exists()) { - return false; + return CHUNKS; } if (!f.open(QIODevice::ReadOnly)) { - return false; + return CHUNKS; } PRS1DataChunk *chunk = nullptr, *lastchunk = nullptr; @@ -1792,6 +1800,7 @@ bool PRS1FileGroup::ParseFile(QString path) int cruft = 0; int firstsession = 0; + do { QByteArray headerBA = f.read(16); if (headerBA.size() != 16) { @@ -1888,27 +1897,13 @@ bool PRS1FileGroup::ParseFile(QString path) lastheadersize = headersize; blocksize -= headersize; - - // Check header checksum quint8 csum = 0; for (int i=0; i < headersize-1; ++i) csum += header[i]; if (csum != header[headersize-1]) { // header checksum error. delete chunk; - return false; - } - - // Check for a valid file group with this sessionid - if ((chunk->sessionid != firstsession) && (loader->prs1sessions.find(chunk->sessionid) != loader->prs1sessions.end())) { - delete chunk; - return true; - - // I don't see a point in skipping this block, because the next sessions files are present - -// if (!f.seek(f.pos()+blocksize)) { -// return; -// } + return CHUNKS; } // Read data block @@ -1925,9 +1920,11 @@ bool PRS1FileGroup::ParseFile(QString path) chunk->m_data.chop(2); #ifdef PRS1_CRC_CHECK + // This fails.. it needs to include the header! quint16 calc16 = CRC16((unsigned char *)chunk->m_data.data(), chunk->m_data.size()); if (calc16 != crc16) { // corrupt data block.. bleh.. + // qDebug() << "CRC16 doesn't match for chunk" << chunk->sessionid << "for" << path; } #endif @@ -1949,34 +1946,12 @@ bool PRS1FileGroup::ParseFile(QString path) } } - QMap::iterator it = sessions.find(chunk->sessionid); - if (it == sessions.end()) { - it = sessions.insert(chunk->sessionid, new PRS1SessionData()); - } - - switch (chunk->ext) { - case 0: - it.value()->compliance = chunk; - break; - case 1: - it.value()->summary = chunk; - break; - case 2: - it.value()->event = chunk; - break; - case 5: - it.value()->waveforms.append(chunk); - break; - default: - qDebug() << "Cruft file in PRS1FileGroup::ParseFile " << path; - delete chunk; - return false; - } + CHUNKS.append(chunk); lastchunk = chunk; cnt++; } while (!f.atEnd()); - return true; + return CHUNKS; } void InitModelMap() diff --git a/sleepyhead/SleepLib/loader_plugins/prs1_loader.h b/sleepyhead/SleepLib/loader_plugins/prs1_loader.h index 75b60043..3d81f4e0 100644 --- a/sleepyhead/SleepLib/loader_plugins/prs1_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/prs1_loader.h @@ -87,28 +87,32 @@ public: QList waveformInfo; }; -class PRS1SessionData +class PRS1Loader; + + +class PRS1Import:public ImportTask { public: - PRS1SessionData() { - compliance = summary = event = nullptr; + PRS1Import(PRS1Loader * l, SessionID s, Machine * m): loader(l), sessionid(s), mach(m) { + summary = nullptr; + compliance = nullptr; + event = nullptr; session = nullptr; } - PRS1SessionData(const PRS1SessionData & copy) { - session = copy.session; - compliance = copy.compliance; - summary = copy.summary; - event = copy.event; - waveforms = copy.waveforms; - } - ~PRS1SessionData() { + virtual ~PRS1Import() { delete compliance; delete summary; delete event; - Q_FOREACH(PRS1DataChunk * c, waveforms) { - delete c; - } + for (int i=0;i < waveforms.size(); ++i) {delete waveforms.at(i); } } + virtual void run(); + + PRS1DataChunk * compliance; + PRS1DataChunk * summary; + PRS1DataChunk * event; + QList waveforms; + + QString waveform; bool ParseCompliance(); bool ParseSummary(); @@ -127,53 +131,10 @@ public: //! \brief Parse a single data chunk from a .002 file containing event data for a family 5 ASV machine (which has a different format) bool ParseF5Events(); - Session * session; - - PRS1DataChunk * compliance; - PRS1DataChunk * summary; - PRS1DataChunk * event; - QList waveforms; -}; - -class PRS1Loader; - -struct PRS1FileGroup -{ - PRS1FileGroup() { loader = NULL; } - PRS1FileGroup(const PRS1FileGroup & copy) { - compliance = copy.compliance; - summary = copy.summary; - event = copy.event; - waveform = copy.waveform; - loader = copy.loader; - } - ~PRS1FileGroup() { - } - - QString compliance; - QString summary; - QString event; - QString waveform; - - bool ParseFile(QString path); - void ParseChunks(PRS1Loader *); - - PRS1Loader * loader; - - QMap sessions; -}; - -class PRS1Import:public ImportTask -{ -public: - PRS1Import(PRS1Loader * l, SessionID s, PRS1FileGroup *g, Machine * m): loader(l), sessionid(s), group(g), mach(m) {} - virtual ~PRS1Import() {} - virtual void run(); - protected: + Session * session; PRS1Loader * loader; SessionID sessionid; - PRS1FileGroup *group; Machine * mach; }; @@ -201,6 +162,8 @@ class PRS1Loader : public CPAPLoader // //! \brief Create a new PRS1 machine record, indexed by Serial number. //Machine *CreateMachine(QString serial); + QList ParseFile(QString path); + //! \brief Register this Module to the list of Loaders, so it knows to search for PRS1 data. static void Register(); @@ -208,11 +171,12 @@ class PRS1Loader : public CPAPLoader return MachineInfo(MT_CPAP, 0, prs1_class_name, QObject::tr("Philips Respironics"), QString(), QString(), QString(), QObject::tr("System One"), QDateTime::currentDateTime(), prs1_data_version); } + virtual QString PresReliefLabel() { return QObject::tr(""); } virtual ChannelID PresReliefMode() { return PRS1_FlexMode; } virtual ChannelID PresReliefLevel() { return PRS1_FlexLevel; } - QHash prs1sessions; + QHash sesstasks; protected: QString last; @@ -242,6 +206,7 @@ class PRS1Loader : public CPAPLoader //! \brief Open a PRS1 data file, and break into data chunks, delivering them to the correct parser. bool OpenFile(Machine *mach, QString filename); + //bool Parse002(Session *session,unsigned char *buffer,int size,qint64 timestamp,long fpos); //bool Parse002ASV(Session *session,unsigned char *buffer,int size,qint64 timestamp,long fpos); unsigned char *m_buffer;