diff --git a/SleepyHeadQT.pro b/SleepyHeadQT.pro index 5b33bbad..ee6b7058 100644 --- a/SleepyHeadQT.pro +++ b/SleepyHeadQT.pro @@ -12,4 +12,5 @@ TRANSLATIONS += \ Translations/Svenska.se.ts \ Translations/Espaniol.es.ts \ Translations/Bulgarian.bg.ts \ - Translations/English.en_UK.ts + Translations/English.en_UK.ts \ + Translations/Portugues.pt.ts diff --git a/sleepyhead/SleepLib/common.h b/sleepyhead/SleepLib/common.h index a5129a3b..765a451d 100644 --- a/sleepyhead/SleepLib/common.h +++ b/sleepyhead/SleepLib/common.h @@ -95,6 +95,7 @@ const QString STR_MACH_ResMed = "ResMed"; const QString STR_MACH_PRS1 = "PRS1"; const QString STR_MACH_Journal = "Journal"; const QString STR_MACH_Intellipap = "Intellipap"; +const QString STR_MACH_Weinmann= "Weinmann"; const QString STR_MACH_FPIcon = "FPIcon"; const QString STR_MACH_MSeries = "MSeries"; const QString STR_MACH_CMS50 = "CMS50"; diff --git a/sleepyhead/SleepLib/day.cpp b/sleepyhead/SleepLib/day.cpp index f85ca3a9..b5d7c934 100644 --- a/sleepyhead/SleepLib/day.cpp +++ b/sleepyhead/SleepLib/day.cpp @@ -959,7 +959,9 @@ bool Day::removeSession(Session *sess) QString Day::getCPAPMode() { - CPAPMode mode = (CPAPMode)(int)settings_max(CPAP_Mode); + Q_ASSERT(machine_type() == MT_CPAP); + + CPAPMode mode = (CPAPMode)(int)qRound(settings_wavg(CPAP_Mode)); if (mode == MODE_CPAP) { return QObject::tr("Fixed"); } else if (mode == MODE_APAP) { @@ -968,31 +970,59 @@ QString Day::getCPAPMode() return QObject::tr("Fixed Bi-Level"); } else if (mode == MODE_BILEVEL_AUTO_FIXED_PS) { return QObject::tr("Auto Bi-Level (Fixed PS)"); - } else if (mode == MODE_ASV) { + } else if (mode == MODE_BILEVEL_AUTO_VARIABLE_PS) { + return QObject::tr("Auto Bi-Level (Variable PS)"); + } else if (mode == MODE_ASV) { return QObject::tr("ASV Fixed EPAP"); - } else if (mode == MODE_ASV_VARIABLE_EPAP) { + } else if (mode == MODE_ASV_VARIABLE_EPAP) { return QObject::tr("ASV Variable EPAP"); } return STR_TR_Unknown; } +QString Day::getPressureRelief() +{ + CPAPLoader * loader = qobject_cast(machine->loader()); + + if (!loader) return STR_MessageBox_Error; + + QString pr_str; + + ChannelID pr_level_chan = loader->PresReliefLevel(); + ChannelID pr_mode_chan = loader->PresReliefMode(); + + if ((pr_level_chan != NoChannel) && settingExists(pr_level_chan)) { + int pr_level = qRound(settings_wavg(pr_level_chan)); + int pr_mode = qRound(settings_wavg(pr_mode_chan)); + pr_str = QObject::tr("%1%2").arg(loader->PresReliefLabel()). + arg(schema::channel[pr_mode_chan].option(pr_mode)); + if (pr_mode > 0) pr_str += QString(" %1").arg(schema::channel[pr_level_chan].option(pr_level)); + } else pr_str = STR_TR_None; + return pr_str; +} + + QString Day::getPressureSettings() { + Q_ASSERT(machine_type() == MT_CPAP); + CPAPMode mode = (CPAPMode)(int)settings_max(CPAP_Mode); QString units = schema::channel[CPAP_Pressure].units(); if (mode == MODE_CPAP) { - return QObject::tr("%1 %2").arg(settings_min(CPAP_Pressure)).arg(units); + return QObject::tr("Fixed %1 (%2)").arg(settings_min(CPAP_Pressure)).arg(units); } else if (mode == MODE_APAP) { - return QObject::tr("%1-%2 %3").arg(settings_min(CPAP_PressureMin)).arg(settings_max(CPAP_PressureMax)).arg(units); + return QObject::tr("Min %1 Max %2 (%3)").arg(settings_min(CPAP_PressureMin)).arg(settings_max(CPAP_PressureMax)).arg(units); } else if (mode == MODE_BILEVEL_FIXED ) { - return QObject::tr("%1-%2 %3").arg(settings_min(CPAP_EPAP),0,'f',1).arg(settings_max(CPAP_IPAP),0,'f',1).arg(units); + return QObject::tr("IPAP %1 EPAP %2 (%3)").arg(settings_min(CPAP_EPAP),0,'f',1).arg(settings_max(CPAP_IPAP),0,'f',1).arg(units); } else if (mode == MODE_BILEVEL_AUTO_FIXED_PS) { - return QObject::tr("PS %3 over %1-%2 %4 ").arg(settings_min(CPAP_EPAPLo),0,'f',1).arg(settings_max(CPAP_IPAPHi),0,'f',1).arg(settings_max(CPAP_PS),0,'f',1).arg(units); + return QObject::tr("PS %3 over %1-%2 (%4)").arg(settings_min(CPAP_EPAPLo),0,'f',1).arg(settings_max(CPAP_IPAPHi),0,'f',1).arg(settings_max(CPAP_PS),0,'f',1).arg(units); + } else if (mode == MODE_BILEVEL_AUTO_VARIABLE_PS) { + return QObject::tr("Min EPAP %3 Max IPAP %4 PS %1-%2 (%5)").arg(settings_min(CPAP_PSMin),0,'f',1).arg(settings_max(CPAP_PSMax),0,'f',1).arg(settings_min(CPAP_EPAPLo),0,'f',1).arg(settings_max(CPAP_IPAPHi),0,'f',1).arg(units); } else if (mode == MODE_ASV) { - return QObject::tr("PS %4-%5 over %1-%3 %6").arg(settings_min(CPAP_EPAP),0,'f',1).arg(settings_max(CPAP_IPAP),0,'f',1).arg(settings_min(CPAP_PSMin),0,'f',1).arg(settings_max(CPAP_PSMax),0,'f',1).arg(units); + return QObject::tr("PS %4-%5 over %1-%3 (%6)").arg(settings_min(CPAP_EPAP),0,'f',1).arg(settings_max(CPAP_IPAP),0,'f',1).arg(settings_min(CPAP_PSMin),0,'f',1).arg(settings_max(CPAP_PSMax),0,'f',1).arg(units); } else if (mode == MODE_ASV_VARIABLE_EPAP) { - return QObject::tr("PS %4-%5 over EPAP %1-%2 Max %3 %6").arg(settings_min(CPAP_EPAPLo),0,'f',1).arg(settings_max(CPAP_EPAPHi),0,'f',1).arg(settings_max(CPAP_IPAPHi),0,'f',1).arg(settings_min(CPAP_PSMin),0,'f',1).arg(settings_max(CPAP_PSMax),0,'f',1).arg(units); + return QObject::tr("PS %4-%5 over EPAP %1-%2 Max %3 (%6)").arg(settings_min(CPAP_EPAPLo),0,'f',1).arg(settings_max(CPAP_EPAPHi),0,'f',1).arg(settings_max(CPAP_IPAPHi),0,'f',1).arg(settings_min(CPAP_PSMin),0,'f',1).arg(settings_max(CPAP_PSMax),0,'f',1).arg(units); } return STR_TR_Unknown; diff --git a/sleepyhead/SleepLib/day.h b/sleepyhead/SleepLib/day.h index 9ee34123..dbd05d52 100644 --- a/sleepyhead/SleepLib/day.h +++ b/sleepyhead/SleepLib/day.h @@ -179,8 +179,11 @@ class Day bool removeSession(Session *sess); + // Some ugly CPAP specific stuff QString getCPAPMode(); + QString getPressureRelief(); QString getPressureSettings(); + QList sessions; protected: diff --git a/sleepyhead/SleepLib/loader_plugins/cms50_loader.h b/sleepyhead/SleepLib/loader_plugins/cms50_loader.h index 6ab5d34b..63e78471 100644 --- a/sleepyhead/SleepLib/loader_plugins/cms50_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/cms50_loader.h @@ -39,7 +39,7 @@ Q_OBJECT virtual const QString &loaderName() { return cms50_class_name; } virtual MachineInfo newInfo() { - return MachineInfo(MT_OXIMETER, cms50_class_name, QObject::tr("Contec"), QObject::tr("CMS50"), QString(), QString(), QObject::tr("CMS50"), QDateTime::currentDateTime(), cms50_data_version); + return MachineInfo(MT_OXIMETER, 0, cms50_class_name, QObject::tr("Contec"), QObject::tr("CMS50"), QString(), QString(), QObject::tr("CMS50"), QDateTime::currentDateTime(), cms50_data_version); } diff --git a/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp b/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp index acc1d3cc..b30bd857 100644 --- a/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp @@ -537,6 +537,7 @@ bool FPIconLoader::OpenFLW(Machine *mach, QString filename) const int samples_per_block = 50; const double rate = 1000.0 / double(samples_per_block); + // F&P Overwrites this file, not appends to it. flow = new EventList(EVL_Waveform, 1.0F, 0, 0, 0, rate); pressure = new EventList(EVL_Event, 0.01F, 0, 0, 0, rate * double(samples_per_block)); diff --git a/sleepyhead/SleepLib/loader_plugins/icon_loader.h b/sleepyhead/SleepLib/loader_plugins/icon_loader.h index 23a5513a..0df79246 100644 --- a/sleepyhead/SleepLib/loader_plugins/icon_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/icon_loader.h @@ -48,7 +48,7 @@ const QString fpicon_class_name = STR_MACH_FPIcon; This is only relatively recent addition and still needs more work */ -class FPIconLoader : public MachineLoader +class FPIconLoader : public CPAPLoader { public: FPIconLoader(); @@ -76,13 +76,23 @@ class FPIconLoader : public MachineLoader //Machine *CreateMachine(QString serial); virtual MachineInfo newInfo() { - return MachineInfo(MT_CPAP, fpicon_class_name, QObject::tr("Fisher & Paykel"), QString(), QString(), QString(), QObject::tr("ICON"), QDateTime::currentDateTime(), fpicon_data_version); + return MachineInfo(MT_CPAP, 0, fpicon_class_name, QObject::tr("Fisher & Paykel"), QString(), QString(), QString(), QObject::tr("ICON"), QDateTime::currentDateTime(), fpicon_data_version); } //! \brief Registers this MachineLoader with the master list, so F&P Icon data can load static void Register(); + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Now for some CPAPLoader overrides + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual QString presRelType() { return QObject::tr(""); } // might not need this one + + virtual ChannelID presRelSet() { return NoChannel; } + virtual ChannelID presRelLevel() { return NoChannel; } + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + + protected: QDateTime readFPDateTime(quint8 *data); diff --git a/sleepyhead/SleepLib/loader_plugins/intellipap_loader.cpp b/sleepyhead/SleepLib/loader_plugins/intellipap_loader.cpp index 070d7ad6..e540899c 100644 --- a/sleepyhead/SleepLib/loader_plugins/intellipap_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/intellipap_loader.cpp @@ -17,6 +17,8 @@ extern QProgressBar *qprogress; +ChannelID INTP_SmartFlexMode, INTP_SmartFlexLevel; + Intellipap::Intellipap(MachineID id) : CPAP(id) { @@ -339,6 +341,7 @@ int IntellipapLoader::Open(QString path) sess->AddEventList(CPAP_Snore, EVL_Event); sess->AddEventList(CPAP_Obstructive, EVL_Event); + sess->AddEventList(CPAP_VSnore, EVL_Event); sess->AddEventList(CPAP_Hypopnea, EVL_Event); sess->AddEventList(CPAP_NRI, EVL_Event); sess->AddEventList(CPAP_LeakFlag, EVL_Event); @@ -371,6 +374,8 @@ int IntellipapLoader::Open(QString path) sid = 0; SessionID lastsid = 0; + int last_minp=0, last_maxp=0, last_ps=0, last_pres = 0; + for (int i = 0; i < recs; i++) { // convert timestamp to real epoch ts1 = ((m_buffer[pos] << 24) | (m_buffer[pos + 1] << 16) | (m_buffer[pos + 2] << 8) | m_buffer[pos + 3]) + ep; @@ -393,32 +398,21 @@ int IntellipapLoader::Open(QString path) int pres = m_buffer[pos + 0xd]; if (mode >= MODE_BILEVEL_FIXED) { - - sess->settings[CPAP_EPAP] = float(minp) / 10.0; - sess->settings[CPAP_IPAP] = float(maxp) / 10.0; - - sess->settings[CPAP_PS] = float(ps) / 10.0; - - - sess->eventlist[CPAP_IPAP][0]->AddEvent(time, float(pres) / 10.0); - sess->eventlist[CPAP_EPAP][0]->AddEvent(time, float(pres-ps) / 10.0); rampval = maxp; - } else { - sess->eventlist[CPAP_Pressure][0]->AddEvent(time, float(pres) / 10.0); // current pressure rampval = minp; - - if (mode == MODE_APAP) { - sess->settings[CPAP_PressureMin] = float(minp) / 10.0; - sess->settings[CPAP_PressureMax] = float(maxp) / 10.0; - } else if (mode == MODE_CPAP) { - sess->settings[CPAP_Pressure] = float(maxp) / 10.0; - } } + qint64 rs = rampstart[sid]; if (pres < rampval) { if (!rs) { + // ramp started + + +// int rv = pres-rampval; +// double ramp = + rampstart[sid] = time; } rampend[sid] = time; @@ -430,8 +424,34 @@ int IntellipapLoader::Open(QString path) int duration = (time - rs) / 1000L; sess->eventlist[CPAP_Ramp][0]->AddEvent(time, duration); - rampstart[sid] = 0; - //rampend[sid] = 0; // don't need to + rampstart.remove(sid); + rampend.remove(sid); + } + } + + + // Do this after ramp, because ramp calcs might need to insert interpolated pressure samples + if (mode >= MODE_BILEVEL_FIXED) { + + sess->settings[CPAP_EPAP] = float(minp) / 10.0; + sess->settings[CPAP_IPAP] = float(maxp) / 10.0; + + sess->settings[CPAP_PS] = float(ps) / 10.0; + + + sess->eventlist[CPAP_IPAP][0]->AddEvent(time, float(pres) / 10.0); + sess->eventlist[CPAP_EPAP][0]->AddEvent(time, float(pres-ps) / 10.0); +// rampval = maxp; + + } else { + sess->eventlist[CPAP_Pressure][0]->AddEvent(time, float(pres) / 10.0); // current pressure +// rampval = minp; + + if (mode == MODE_APAP) { + sess->settings[CPAP_PressureMin] = float(minp) / 10.0; + sess->settings[CPAP_PressureMax] = float(maxp) / 10.0; + } else if (mode == MODE_CPAP) { + sess->settings[CPAP_Pressure] = float(maxp) / 10.0; } } @@ -446,6 +466,10 @@ int IntellipapLoader::Open(QString path) sess->eventlist[CPAP_Snore][0]->AddEvent(time, m_buffer[pos + 0x4]); //4/5?? + if (m_buffer[pos+0x4] > 0) { + sess->eventlist[CPAP_VSnore][0]->AddEvent(time, m_buffer[pos + 0x5]); + } + // 0x0f == Leak Event // 0x04 == Snore? if (m_buffer[pos + 0xf] > 0) { // Leak Event @@ -477,6 +501,7 @@ int IntellipapLoader::Open(QString path) EventDataType mv = tv * rr; // MinuteVent=TidalVolume * Respiratory Rate sess->eventlist[CPAP_MinuteVent][0]->AddEvent(time, mv / 1000.0); break; + } else { } lastsid = sid; } @@ -521,14 +546,12 @@ int IntellipapLoader::Open(QString path) // sess->really_set_last(last); - sess->settings[CPAP_PresReliefType] = (PRTypes)PR_SMARTFLEX; - - sess->settings[CPAP_PresReliefSet] = smartflex; + sess->settings[INTP_SmartFlexLevel] = smartflex; if (smartflexmode == 0) { - sess->settings[CPAP_PresReliefMode] = PM_FullTime; + sess->settings[INTP_SmartFlexMode] = PM_FullTime; } else { - sess->settings[CPAP_PresReliefMode] = PM_RampOnly; + sess->settings[INTP_SmartFlexMode] = PM_RampOnly; } sess->settings[CPAP_RampPressure] = ramp_pressure; @@ -567,4 +590,23 @@ void IntellipapLoader::Register() RegisterLoader(new IntellipapLoader()); //InitModelMap(); intellipap_initialized = true; + + using namespace schema; + Channel * chan = nullptr; + channel.add(GRP_CPAP, chan = new Channel(INTP_SmartFlexMode = 0x1165, SETTING, SESSION, + "INTPSmartFlexMode", QObject::tr("SmartFlex Mode"), + QObject::tr("Intellipap pressure relief mode."), + QObject::tr("SmartFlex Mode"), + "", DEFAULT, Qt::green)); + + + chan->addOption(0, STR_TR_Off); + chan->addOption(1, QObject::tr("Ramp Only")); + chan->addOption(2, QObject::tr("Full Time")); + + channel.add(GRP_CPAP, chan = new Channel(INTP_SmartFlexLevel = 0x1169, SETTING, SESSION, + "INTPSmartFlexLevel", QObject::tr("SmartFlex Level"), + QObject::tr("Intellipap pressure relief level."), + QObject::tr("SmartFlex Level"), + "", DEFAULT, Qt::green)); } diff --git a/sleepyhead/SleepLib/loader_plugins/intellipap_loader.h b/sleepyhead/SleepLib/loader_plugins/intellipap_loader.h index 03c599ce..45adc508 100644 --- a/sleepyhead/SleepLib/loader_plugins/intellipap_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/intellipap_loader.h @@ -39,6 +39,8 @@ class Intellipap: public CPAP const int intellipap_load_buffer_size = 1024 * 1024; +extern ChannelID INTP_SmartFlexMode; +extern ChannelID INTP_SmartFlexLevel; const QString intellipap_class_name = STR_MACH_Intellipap; @@ -46,7 +48,7 @@ const QString intellipap_class_name = STR_MACH_Intellipap; \brief Loader for DeVilbiss Intellipap Auto data This is only relatively recent addition and still needs more work */ -class IntellipapLoader : public MachineLoader +class IntellipapLoader : public CPAPLoader { public: IntellipapLoader(); @@ -71,8 +73,18 @@ class IntellipapLoader : public MachineLoader static void Register(); virtual MachineInfo newInfo() { - return MachineInfo(MT_CPAP, intellipap_class_name, QObject::tr("DeVilbiss"), QString(), QString(), QString(), QObject::tr("Intellipap"), QDateTime::currentDateTime(), intellipap_data_version); + return MachineInfo(MT_CPAP, 0, intellipap_class_name, QObject::tr("DeVilbiss"), QString(), QString(), QString(), QObject::tr("Intellipap"), QDateTime::currentDateTime(), intellipap_data_version); } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Now for some CPAPLoader overrides + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual QString presRelLabel() { return QObject::tr("SmartFlex Settings"); } // might not need this one + + virtual ChannelID presReliefMode() { return INTP_SmartFlexMode; } + virtual ChannelID presRelLevel() { return INTP_SmartFlexLevel; } + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + protected: QString last; diff --git a/sleepyhead/SleepLib/loader_plugins/md300w1_loader.h b/sleepyhead/SleepLib/loader_plugins/md300w1_loader.h index 0ade2908..b5befb1f 100644 --- a/sleepyhead/SleepLib/loader_plugins/md300w1_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/md300w1_loader.h @@ -41,7 +41,7 @@ Q_OBJECT // Machine *CreateMachine(); virtual MachineInfo newInfo() { - return MachineInfo(MT_OXIMETER, md300w1_class_name, QObject::tr("ChoiceMMed"), QString(), QString(), QString(), QObject::tr("MD300"), QDateTime::currentDateTime(), md300w1_data_version); + return MachineInfo(MT_OXIMETER, 0, md300w1_class_name, QObject::tr("ChoiceMMed"), QString(), QString(), QString(), QObject::tr("MD300"), QDateTime::currentDateTime(), md300w1_data_version); } diff --git a/sleepyhead/SleepLib/loader_plugins/mseries_loader.h b/sleepyhead/SleepLib/loader_plugins/mseries_loader.h index 9808287e..905bd778 100644 --- a/sleepyhead/SleepLib/loader_plugins/mseries_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/mseries_loader.h @@ -63,7 +63,7 @@ class MSeriesLoader : public MachineLoader // Machine *CreateMachine(QString serial); virtual MachineInfo newInfo() { - return MachineInfo(MT_CPAP, mseries_class_name, QObject::tr("Respironics"), QString(), QString(), QString(), QObject::tr("M-Series"), QDateTime::currentDateTime(), mseries_data_version); + return MachineInfo(MT_CPAP, 0, mseries_class_name, QObject::tr("Respironics"), QString(), QString(), QString(), QObject::tr("M-Series"), QDateTime::currentDateTime(), mseries_data_version); } //! \brief Register this Module to the list of Loaders, so it knows to search for PRS1 data. diff --git a/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp b/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp index 18aed646..16cc4a1a 100644 --- a/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp @@ -99,6 +99,10 @@ crc_t CRC16(const unsigned char *data, size_t data_len) } #endif +enum FlexMode { FLEX_None, FLEX_CFlex, FLEX_CFlexPlus, FLEX_AFlex, FLEX_RiseTime, FLEX_BiFlex, FLEX_Unknown }; + + + PRS1::PRS1(MachineID id): CPAP(id) { } @@ -213,6 +217,8 @@ int PRS1Loader::Open(QString path) if ((filename[0] == 'P') && (isdigit(filename[1])) && (isdigit(filename[2]))) { SerialNumbers.push_back(filename); + } else if (isdigit(filename[0]) && isdigit(filename[1])) { + SerialNumbers.push_back(filename); } else if (filename.toLower() == "last.txt") { // last.txt points to the current serial number QString file = fi.canonicalFilePath(); QFile f(file); @@ -359,7 +365,7 @@ int PRS1Loader::OpenMachine(Machine *m, QString path) if ((filename[0].toLower() == 'p') && (isdigit(filename[1]))) { // p0, p1, p2.. etc.. folders contain the session data paths.push_back(fi.canonicalFilePath()); - } else if (filename.toLower() == "properties.txt") { + } else if ((filename.toLower() == "properties.txt") || (filename.toLower() == "prop.txt")) { ParseProperties(m, fi.canonicalFilePath()); } else if (filename.toLower() == "e") { // Error files.. @@ -839,19 +845,22 @@ bool PRS1SessionData::ParseF0Events() EventList *LEAK = session->AddEventList(CPAP_LeakTotal, EVL_Event); EventList *SNORE = session->AddEventList(CPAP_Snore, EVL_Event); - EventList *CA = nullptr; //session->AddEventList(CPAP_ClearAirway, EVL_Event); - EventList *VS = nullptr; - EventList *VS2 = nullptr; - EventList *FL = nullptr; - EventList *RE = nullptr; + EventList *PP = session->AddEventList(CPAP_PressurePulse, EVL_Event); + EventList *RE = session->AddEventList(CPAP_RERA, EVL_Event); + EventList *CA = session->AddEventList(CPAP_ClearAirway, EVL_Event); + EventList *FL = session->AddEventList(CPAP_FlowLimit, EVL_Event); + EventList *VS = session->AddEventList(CPAP_VSnore, EVL_Event); + EventList *VS2 = session->AddEventList(CPAP_VSnore2, EVL_Event); + + Code[12] = session->AddEventList(PRS1_0B, EVL_Event); + Code[17] = session->AddEventList(PRS1_0E, EVL_Event); + Code[20] = session->AddEventList(CPAP_LargeLeak, EVL_Event); EventList *PRESSURE = nullptr; EventList *EPAP = nullptr; EventList *IPAP = nullptr; EventList *PS = nullptr; - EventList *PP = nullptr; - //session->AddEventList(CPAP_VSnore, EVL_Event); //EventList * VS=session->AddEventList(CPAP_Obstructive, EVL_Event); unsigned char lastcode3 = 0, lastcode2 = 0, lastcode = 0; @@ -961,9 +970,6 @@ bool PRS1SessionData::ParseF0Events() break; case 0x04: // Pressure Pulse - if (!PP) { - if (!(PP = session->AddEventList(CPAP_PressurePulse, EVL_Event))) { return false; } - } PP->AddEvent(t, buffer[pos++]); break; @@ -972,10 +978,6 @@ bool PRS1SessionData::ParseF0Events() data[0] = buffer[pos++]; tt = t - (qint64(data[0]) * 1000L); - if (!RE) { - if (!(RE = session->AddEventList(CPAP_RERA, EVL_Event))) { return false; } - } - RE->AddEvent(tt, data[0]); break; @@ -989,10 +991,6 @@ bool PRS1SessionData::ParseF0Events() data[0] = buffer[pos++]; tt = t - (qint64(data[0]) * 1000L); - if (!CA) { - if (!(CA = session->AddEventList(CPAP_ClearAirway, EVL_Event))) { return false; } - } - CA->AddEvent(tt, data[0]); break; @@ -1006,10 +1004,6 @@ bool PRS1SessionData::ParseF0Events() data[0] = buffer[pos++]; tt = t - (qint64(data[0]) * 1000L); - if (!FL) { - if (!(FL = session->AddEventList(CPAP_FlowLimit, EVL_Event))) { return false; } - } - FL->AddEvent(tt, data[0]); break; @@ -1017,19 +1011,11 @@ bool PRS1SessionData::ParseF0Events() data[0] = buffer[pos++]; data[1] = buffer[pos++]; - if (!Code[12]) { - if (!(Code[12] = session->AddEventList(PRS1_0B, EVL_Event))) { return false; } - } - // FIXME Code[12]->AddEvent(t, data[0]); break; case 0x0d: // Vibratory Snore - if (!VS) { - if (!(VS = session->AddEventList(CPAP_VSnore, EVL_Event))) { return false; } - } - VS->AddEvent(t, 0); break; @@ -1041,10 +1027,6 @@ bool PRS1SessionData::ParseF0Events() SNORE->AddEvent(t, data[1]); if (data[1] > 0) { - if (!VS2) { - if (!(VS2 = session->AddEventList(CPAP_VSnore2, EVL_Event))) { return false; } - } - VS2->AddEvent(t, data[1]); } @@ -1061,10 +1043,6 @@ bool PRS1SessionData::ParseF0Events() //pos+=2; data[2] = buffer[pos++]; - if (!Code[17]) { - if (!(Code[17] = session->AddEventList(PRS1_0E, EVL_Event))) { return false; } - } - tdata = unsigned(data[1]) << 8 | unsigned(data[0]); Code[17]->AddEvent(t, tdata); //qDebug() << hex << data[0] << data[1] << data[2]; @@ -1078,10 +1056,6 @@ bool PRS1SessionData::ParseF0Events() pos += 2; data[1] = buffer[pos++]; - if (!Code[20]) { - if (!(Code[20] = session->AddEventList(CPAP_LargeLeak, EVL_Event))) { return false; } - } - tt = t - qint64(data[1]) * 1000L; Code[20]->AddEvent(tt, data[0]); break; @@ -1129,20 +1103,193 @@ bool PRS1SessionData::ParseF0Events() bool PRS1SessionData::ParseCompliance() { + // Bleh!! There is probably 10 different formats for these useless piece of junk machines if (!compliance) return false; return true; } -bool PRS1SessionData::ParseSummary() +bool PRS1SessionData::ParseSummaryF0() { - if (!summary) return false; - if (summary->m_data.size() < 59) { - return false; - } const unsigned char * data = (unsigned char *)summary->m_data.constData(); + CPAPMode cpapmode = MODE_UNKNOWN; + + switch (data[0x02]) { // PRS1 mode // 0 = CPAP, 2 = APAP + case 0x00: + cpapmode = MODE_CPAP; + break; + case 0x01: + cpapmode = MODE_BILEVEL_FIXED; + break; + case 0x02: + cpapmode = MODE_APAP; + break; + case 0x03: + cpapmode = MODE_BILEVEL_AUTO_VARIABLE_PS; + } + + EventDataType min_pressure = float(data[0x03]) / 10.0; + EventDataType max_pressure = float(data[0x04]) / 10.0; + EventDataType ps = float(data[0x05]) / 10.0; // pressure support + + if (cpapmode == MODE_CPAP) { + session->settings[CPAP_Pressure] = min_pressure; + } else if (cpapmode == MODE_APAP) { + session->settings[CPAP_PressureMin] = min_pressure; + session->settings[CPAP_PressureMax] = max_pressure; + } else if (cpapmode == MODE_BILEVEL_FIXED) { + session->settings[CPAP_EPAP] = min_pressure; + session->settings[CPAP_IPAP] = max_pressure; + session->settings[CPAP_PS] = ps; + } else if (cpapmode == MODE_BILEVEL_AUTO_VARIABLE_PS) { + session->settings[CPAP_EPAPLo] = min_pressure; + session->settings[CPAP_EPAPHi] = max_pressure - 2.0; + session->settings[CPAP_IPAPLo] = min_pressure + 2.0; + session->settings[CPAP_IPAPHi] = max_pressure; + session->settings[CPAP_PSMin] = 2.0f; + session->settings[CPAP_PSMax] = ps; + } + + session->settings[CPAP_Mode] = (int)cpapmode; + + + int ramp_time = data[0x06]; + EventDataType ramp_pressure = float(data[0x07]) / 10.0; + + session->settings[CPAP_RampTime] = (int)ramp_time; + session->settings[CPAP_RampPressure] = ramp_pressure; + + // Tubing lock has no setting byte + +// int SysOneResistance = (data[0x0a] & 7); +// bool SysOneResistanceOn = (data[0x0a] & 0x40) ? true : false; +// bool SysOneResistanceLock = (data[0x0a] & 0x80) ? true : false; +// int humidifier = (data[0x09] & 7); +// bool autoOn = (data[0x0b] & 0x40) ? true : false; //? + + // Menu Options + session->settings[PRS1_SysLock] = (bool) (data[0x0a] & 0x80); // System One Resistance Lock Setting + session->settings[PRS1_SysOneResistSet] = (int)data[0x0a] & 7; // SYstem One Resistance setting value + session->settings[PRS1_SysOneResistStat] = (bool) (data[0x0a] & 0x40); // System One Resistance Status bit + session->settings[PRS1_HoseDiam] = (data[0x0a] & 0x08) ? QObject::tr("15mm") : QObject::tr("22mm"); + session->settings[PRS1_AutoOn] = (bool) (data[0x0b] & 0x40); + session->settings[PRS1_AutoOff] = (bool) (data[0x0c] & 0x10); + session->settings[PRS1_MaskAlert] = (bool) (data[0x0c] & 0x08); + session->settings[PRS1_ShowAHI] = (bool) (data[0x0c] & 0x04); + session->settings[PRS1_HumidStatus] = (bool)(data[0x09] & 0x80); // Humidifier Connected + session->settings[PRS1_HumitSetting] = (int)(data[0x09] & 7); // Humidifier Value + + // session-> + + quint8 flex = data[0x08]; + + int flexlevel = flex & 0x03; + FlexMode flexmode = FLEX_Unknown; + + // 88 CFlex+ / AFlex (depending on CPAP mode) + // 80 CFlex + // 00 NoFlex + // c0 Split CFlex then None + // c8 Split CFlex+ then None + + flex &= 0xf8; + bool split = false; + + if (flex & 0x40) { // This bit defines the Flex setting for the CPAP component of the Split night + split = true; + } + if (flex & 0x80) { // CFlex bit + if (flex & 0x10) { + flexmode = FLEX_RiseTime; + } else if (flex & 8) { // Plus bit + if (split || (cpapmode == MODE_CPAP)) { + flexmode = FLEX_CFlexPlus; + } else if (cpapmode == MODE_APAP) { + flexmode = FLEX_AFlex; + } + } else { + // CFlex bits refer to Rise Time on BiLevel machines + flexmode = (cpapmode >= MODE_BILEVEL_FIXED) ? FLEX_BiFlex : FLEX_CFlex; + } + } else flexmode = FLEX_None; + + session->settings[PRS1_FlexMode] = (int)flexmode; + session->settings[PRS1_FlexLevel] = (int)flexlevel; + + int duration = data[0x14] | data[0x15] << 8; + + session->set_last(qint64(summary->timestamp+duration) * 1000L); + + return true; +} + +bool PRS1SessionData::ParseSummaryF0V4() +{ + return true; +} + + +bool PRS1SessionData::ParseSummaryF3() +{ + const unsigned char * data = (unsigned char *)summary->m_data.constData(); + + EventDataType epap = data[0x04] | (data[0x05] << 8); + EventDataType ipap = data[0x06] | (data[0x07] << 8); + + EventDataType f1 = data[0x08] | (data[0x09] << 8); + + return true; +} + +bool PRS1SessionData::ParseSummaryF5() +{ + return true; + +} + + + +bool PRS1SessionData::ParseSummary() +{ + + // Family 0 = XPAP + // Family 3 = BIPAP AVAPS + // Family 5 = BIPAP AutoSV + + + if (!summary) return false; + if (summary->m_data.size() < 59) { + //return false; + } + + session->setPhysMax(CPAP_LeakTotal, 120); + session->setPhysMin(CPAP_LeakTotal, 0); + session->setPhysMax(CPAP_Pressure, 25); + session->setPhysMin(CPAP_Pressure, 4); + session->setPhysMax(CPAP_IPAP, 25); + session->setPhysMin(CPAP_IPAP, 4); + session->setPhysMax(CPAP_EPAP, 25); + session->setPhysMin(CPAP_EPAP, 4); + 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: + return ParseSummaryF0(); + case 3: + return ParseSummaryF3(); + case 5: + return ParseSummaryF5(); + } + + const unsigned char * data = (unsigned char *)summary->m_data.constData(); + ////////////////////////////////////////////////////////////////////////////////////////// // ASV Codes (Family 5) Recheck 17/10/2013 // These are all confirmed off Encore reports @@ -1226,22 +1373,23 @@ bool PRS1SessionData::ParseSummary() session->settings[CPAP_Pressure] = (EventDataType)min; } + if (data[offset + 0x08] & 0x80) { // Flex Setting if (data[offset + 0x08] & 0x08) { if (max > 0) { if (summary->family == 5) { - session->settings[CPAP_PresReliefType] = (int)PR_BIFLEX; + session->settings[PRS1_FlexMode] = (int)PR_BIFLEX; } else { - session->settings[CPAP_PresReliefType] = (int)PR_AFLEX; + session->settings[PRS1_FlexMode] = (int)PR_AFLEX; } - } else { session->settings[CPAP_PresReliefType] = (int)PR_CFLEXPLUS; } - } else { session->settings[CPAP_PresReliefType] = (int)PR_CFLEX; } - } else { session->settings[CPAP_PresReliefType] = (int)PR_NONE; } + } else { session->settings[PRS1_FlexMode] = (int)PR_CFLEXPLUS; } + } else { session->settings[PRS1_FlexMode] = (int)PR_CFLEX; } + } else { session->settings[PRS1_FlexMode] = (int)PR_NONE; } - session->settings[CPAP_PresReliefMode] = (int)PM_FullTime; // only has one mode + // Map the channels + session->settings[PRS1_FlexLevel] = (int)(data[offset + 0x08] & 7); - session->settings[CPAP_PresReliefSet] = (int)(data[offset + 0x08] & 7); session->settings[PRS1_SysLock] = (data[offset + 0x0a] & 0x80) == 0x80; session->settings[PRS1_HoseDiam] = ((data[offset + 0x0a] & 0x08) ? "15mm" : "22mm"); session->settings[PRS1_AutoOff] = (data[offset + 0x0c] & 0x10) == 0x10; @@ -1274,16 +1422,7 @@ bool PRS1SessionData::ParseSummary() // Set recommended Graph values.. - session->setPhysMax(CPAP_LeakTotal, 120); - session->setPhysMin(CPAP_LeakTotal, 0); - session->setPhysMax(CPAP_Pressure, 25); - session->setPhysMin(CPAP_Pressure, 4); - session->setPhysMax(CPAP_IPAP, 25); - session->setPhysMin(CPAP_IPAP, 4); - session->setPhysMax(CPAP_EPAP, 25); - session->setPhysMin(CPAP_EPAP, 4); - session->setPhysMax(CPAP_PS, 25); - session->setPhysMin(CPAP_PS, 0); + if (summary->family == 0 && summary->familyVersion == 0) { @@ -1307,32 +1446,34 @@ bool PRS1SessionData::ParseEvents() qDebug() << "Unknown PRS1 familyVersion" << event->familyVersion; return false; } + if (res) { if (session->count(CPAP_IPAP) > 0) { - if (session->settings[CPAP_Mode].toInt() != (int)MODE_ASV) { - session->settings[CPAP_Mode] = MODE_BILEVEL_FIXED; - } +// if (session->settings[CPAP_Mode].toInt() != (int)MODE_ASV) { +// session->settings[CPAP_Mode] = MODE_BILEVEL_FIXED; +// } - if (session->settings[CPAP_PresReliefType].toInt() != PR_NONE) { - session->settings[CPAP_PresReliefType] = PR_BIFLEX; - } +// if (session->settings[CPAP_PresReliefType].toInt() != PR_NONE) { +// session->settings[CPAP_PresReliefType] = PR_BIFLEX; +// } - EventDataType min = session->settings[CPAP_PressureMin].toDouble(); - EventDataType max = session->settings[CPAP_PressureMax].toDouble(); - session->settings[CPAP_EPAP] = min; - session->settings[CPAP_IPAP] = max; +// EventDataType min = session->settings[CPAP_PressureMin].toDouble(); +// EventDataType max = session->settings[CPAP_PressureMax].toDouble(); +// session->settings[CPAP_EPAP] = min; +// session->settings[CPAP_IPAP] = max; - session->settings[CPAP_PS] = max - min; - session->settings.erase(session->settings.find(CPAP_PressureMin)); - session->settings.erase(session->settings.find(CPAP_PressureMax)); +// session->settings[CPAP_PS] = max - min; +// session->settings.erase(session->settings.find(CPAP_PressureMin)); +// session->settings.erase(session->settings.find(CPAP_PressureMax)); - session->m_valuesummary.erase(session->m_valuesummary.find(CPAP_Pressure)); - session->m_wavg.erase(session->m_wavg.find(CPAP_Pressure)); - session->m_min.erase(session->m_min.find(CPAP_Pressure)); - session->m_max.erase(session->m_max.find(CPAP_Pressure)); - session->m_gain.erase(session->m_gain.find(CPAP_Pressure)); +// session->m_valuesummary.erase(session->m_valuesummary.find(CPAP_Pressure)); +// session->m_wavg.erase(session->m_wavg.find(CPAP_Pressure)); +// session->m_min.erase(session->m_min.find(CPAP_Pressure)); +// session->m_max.erase(session->m_max.find(CPAP_Pressure)); +// session->m_gain.erase(session->m_gain.find(CPAP_Pressure)); } else { + session->setSummaryOnly(true); if (!session->settings.contains(CPAP_Pressure) && !session->settings.contains(CPAP_PressureMin)) { session->settings[CPAP_BrokenSummary] = true; @@ -1691,6 +1832,7 @@ void InitModelMap() ModelMap[0x37] = "RemStar BiPAP Auto with Bi-Flex"; ModelMap[0x38] = "RemStar Plus :("; // 150/250P/260P ModelMap[0x41] = "BiPAP autoSV Advanced"; + ModelMap[0x4E] = "BiPAP AVAPS"; } bool initialized = false; @@ -1709,11 +1851,41 @@ void PRS1Loader::Register() initialized = true; channel.add(GRP_CPAP, new Channel(CPAP_PressurePulse = 0x1009, MINOR_FLAG, SESSION, - "PressurePulse", QObject::tr("Pressure Pulse"), + "PressurePulse", + QObject::tr("Pressure Pulse"), QObject::tr("A pulse of pressure 'pinged' to detect a closed airway."), - QObject::tr("PP"), STR_UNIT_EventsPerHour, DEFAULT, QColor("dark red"))); + QObject::tr("PP"), + STR_UNIT_EventsPerHour, DEFAULT, QColor("dark red"))); + + Channel * chan = nullptr; + channel.add(GRP_CPAP, chan = new Channel(PRS1_FlexMode = 0xe105, SETTING, SESSION, + "PRS1FlexMode", QObject::tr("Flex Mode"), + QObject::tr("PRS1 pressure relief mode."), + QObject::tr("Flex Mode"), + "", DEFAULT, Qt::green)); + + + chan->addOption(FLEX_None, STR_TR_None); + chan->addOption(FLEX_CFlex, QObject::tr("C-Flex")); + chan->addOption(FLEX_CFlexPlus, QObject::tr("C-Flex+")); + chan->addOption(FLEX_AFlex, QObject::tr("A-Flex")); + chan->addOption(FLEX_RiseTime, QObject::tr("Rise Time")); + chan->addOption(FLEX_BiFlex, QObject::tr("Bi-Flex")); + + channel.add(GRP_CPAP, chan = new Channel(PRS1_FlexLevel = 0xe106, SETTING, SESSION, + "PRS1FlexSet", + QObject::tr("Flex Level"), + QObject::tr("PRS1 pressure relief setting."), + QObject::tr("Flex Level"), + "", DEFAULT, Qt::blue)); + + chan->addOption(0, STR_TR_Off); + chan->addOption(1, QObject::tr("x1")); + chan->addOption(2, QObject::tr("x2")); + chan->addOption(3, QObject::tr("x3")); + QString unknowndesc=QObject::tr("Unknown PRS1 Code %1"); QString unknownname=QObject::tr("PRS1_%1"); QString unknownshort=QObject::tr("PRS1_%1"); diff --git a/sleepyhead/SleepLib/loader_plugins/prs1_loader.h b/sleepyhead/SleepLib/loader_plugins/prs1_loader.h index 42f7ef77..75b60043 100644 --- a/sleepyhead/SleepLib/loader_plugins/prs1_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/prs1_loader.h @@ -115,6 +115,12 @@ public: bool ParseEvents(); bool ParseWaveforms(); + bool ParseSummaryF0(); + bool ParseSummaryF0V4(); + bool ParseSummaryF3(); + bool ParseSummaryF5(); + + //! \brief Parse a single data chunk from a .002 file containing event data for a standard system one machine bool ParseF0Events(); @@ -174,7 +180,7 @@ protected: /*! \class PRS1Loader \brief Philips Respironics System One Loader Module */ -class PRS1Loader : public MachineLoader +class PRS1Loader : public CPAPLoader { public: PRS1Loader(); @@ -199,9 +205,12 @@ class PRS1Loader : public MachineLoader static void Register(); virtual MachineInfo newInfo() { - return MachineInfo(MT_CPAP, prs1_class_name, QObject::tr("Philips Respironics"), QString(), QString(), QString(), QObject::tr("System One"), QDateTime::currentDateTime(), prs1_data_version); + 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; diff --git a/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp b/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp index 941c51fb..067987d7 100644 --- a/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp @@ -34,6 +34,9 @@ QHash > Resmed_Model_Map; const QString STR_UnknownModel = "Resmed S9 ???"; +ChannelID RMS9_EPR, RMS9_EPRLevel; + + // Return the model name matching the supplied model number. const QString & lookupModel(quint16 model) { @@ -274,26 +277,38 @@ void ResmedLoader::ParseSTR(Machine *mach, QStringList strfiles) if ((sig = str.lookupSignal(CPAP_PSMin))) { R.min_ps = EventDataType(sig->data[rec]) * sig->gain + sig->offset; } + + EventDataType epr = -1, epr_level = -1; if ((sig = str.lookupSignal(RMS9_EPR))) { - R.epr = EventDataType(sig->data[rec]) * sig->gain + sig->offset; + epr= EventDataType(sig->data[rec]) * sig->gain + sig->offset; } if ((sig = str.lookupSignal(RMS9_EPRLevel))) { - R.epr_level = EventDataType(sig->data[rec]) * sig->gain + sig->offset; + epr_level= EventDataType(sig->data[rec]) * sig->gain + sig->offset; + } + + if ((epr >= 0) && (epr_level >= 0)) { + R.epr_level = epr_level; + R.epr = epr; + } else { + if (epr >= 0) { + static bool warn=false; + if (!warn) { // just nag once + qDebug() << "If you can read this, please tell Jedimark you found a ResMed with EPR but no EPR_LEVEL so he can remove this warning"; + warn = true; + } + + R.epr = (epr > 0) ? 1 : 0; + R.epr_level = epr; + } else if (epr_level >= 0) { + R.epr_level = epr_level; + R.epr = (epr_level > 0) ? 1 : 0; + } } if ((sig = str.lookupSignal(CPAP_Mode))) { int mod = EventDataType(sig->data[rec]) * sig->gain + sig->offset; CPAPMode mode; -// if (R.epap > 0) { -// if (R.max_epap < 0) R.max_epap = R.epap; -// if (R.min_epap < 0) R.min_epap = R.epap; -// } -// if (R.ipap > 0) { -// if (R.max_ipap < 0) R.max_ipap = R.ipap; -// if (R.min_ipap < 0) R.min_ipap = R.ipap; -// } - if (mod >= 8) { // mod 8 == vpap adapt variable epap mode = MODE_ASV_VARIABLE_EPAP; if (!haveipap) { @@ -720,12 +735,9 @@ void ResmedImport::run() if (R.epr >= 0) { sess->settings[RMS9_EPR] = (int)R.epr; - sess->settings[CPAP_PresReliefType] = (int)PR_EPR; - sess->settings[CPAP_PresReliefSet] = (int)R.epr; } if (R.epr_level >= 0) { sess->settings[RMS9_EPRLevel] = (int)R.epr_level; - sess->settings[CPAP_PresReliefMode] = (int)R.epr_level; } // Ignore all the rest of the sumary data, because there is enough available to calculate it with higher accuracy. @@ -788,12 +800,9 @@ void ResmedImportStage2::run() if (R.mode >= 0) sess->settings[CPAP_Mode] = R.mode; if (R.epr >= 0) { sess->settings[RMS9_EPR] = (int)R.epr; - sess->settings[CPAP_PresReliefType] = (int)PR_EPR; - sess->settings[CPAP_PresReliefSet] = (int)R.epr; } if (R.epr_level >= 0) { sess->settings[RMS9_EPRLevel] = (int)R.epr_level; - sess->settings[CPAP_PresReliefMode] = (int)R.epr_level; } if (R.leakmax >= 0) sess->setMax(CPAP_Leak, R.leakmax); if (R.leakmax >= 0) sess->setMin(CPAP_Leak, 0); @@ -2341,6 +2350,21 @@ void ResInitModelMap() resmed_codes[CPAP_PressureMin].push_back("Min tryck"); } +// +// +// +// + +ChannelID ResmedLoader::PresReliefMode() { return RMS9_EPR; } +ChannelID ResmedLoader::PresReliefLevel() { return RMS9_EPRLevel; } bool resmed_initialized = false; void ResmedLoader::Register() @@ -2351,6 +2375,31 @@ void ResmedLoader::Register() RegisterLoader(new ResmedLoader()); ResInitModelMap(); resmed_initialized = true; + + using namespace schema; + Channel * chan = nullptr; + channel.add(GRP_CPAP, chan = new Channel(RMS9_EPR = 0xe201, SETTING, SESSION, + "EPR", QObject::tr("EPR Mode"), + QObject::tr("ResMed Exhale Pressure Relief Mode."), + QObject::tr("EPR Mode"), + "", DEFAULT, Qt::green)); + + + chan->addOption(0, STR_TR_Off); + chan->addOption(1, QObject::tr("Ramp Only")); + chan->addOption(2, QObject::tr("Full Time")); + + channel.add(GRP_CPAP, chan = new Channel(RMS9_EPRLevel = 0xe202, SETTING, SESSION, + "EPRLevel", QObject::tr("EPR Level"), + QObject::tr("Exhale Pressure Relief Level"), + QObject::tr("EPR Level"), + "", DEFAULT, Qt::blue)); + + chan->addOption(0, QObject::tr("0cmH2O")); + chan->addOption(1, QObject::tr("1cmH2O")); + chan->addOption(2, QObject::tr("2cmH2O")); + chan->addOption(3, QObject::tr("3cmH2O")); + chan->addOption(4, QObject::tr("Patient")); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/sleepyhead/SleepLib/loader_plugins/resmed_loader.h b/sleepyhead/SleepLib/loader_plugins/resmed_loader.h index 97586f5c..6312ceff 100644 --- a/sleepyhead/SleepLib/loader_plugins/resmed_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/resmed_loader.h @@ -328,7 +328,7 @@ protected: /*! \class ResmedLoader \brief Importer for ResMed S9 Data */ -class ResmedLoader : public MachineLoader +class ResmedLoader : public CPAPLoader { friend class ResmedImport; friend class ResmedImportStage2; @@ -377,10 +377,20 @@ class ResmedLoader : public MachineLoader bool LoadPLD(Session *sess, const QString & path); virtual MachineInfo newInfo() { - return MachineInfo(MT_CPAP, resmed_class_name, QObject::tr("ResMed"), QString(), QString(), QString(), QObject::tr("S9"), QDateTime::currentDateTime(), resmed_data_version); + return MachineInfo(MT_CPAP, 0, resmed_class_name, QObject::tr("ResMed"), QString(), QString(), QString(), QObject::tr("S9"), QDateTime::currentDateTime(), resmed_data_version); } + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Now for some CPAPLoader overrides + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual QString PresReliefLabel() { return QObject::tr("EPR: "); } + + virtual ChannelID PresReliefMode(); + virtual ChannelID PresReliefLevel(); + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + + protected: void ParseSTR(Machine *mach, QStringList strfiles); diff --git a/sleepyhead/SleepLib/loader_plugins/somnopose_loader.h b/sleepyhead/SleepLib/loader_plugins/somnopose_loader.h index c8e2eb0c..63e0956b 100644 --- a/sleepyhead/SleepLib/loader_plugins/somnopose_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/somnopose_loader.h @@ -36,7 +36,7 @@ class SomnoposeLoader : public MachineLoader virtual const QString &loaderName() { return somnopose_class_name; } virtual MachineInfo newInfo() { - return MachineInfo(MT_POSITION, somnopose_class_name, QObject::tr("Somnopose"), QString(), QString(), QString(), QObject::tr("Somnopose Software"), QDateTime::currentDateTime(), somnopose_data_version); + return MachineInfo(MT_POSITION, 0, somnopose_class_name, QObject::tr("Somnopose"), QString(), QString(), QString(), QObject::tr("Somnopose Software"), QDateTime::currentDateTime(), somnopose_data_version); } diff --git a/sleepyhead/SleepLib/loader_plugins/weinmann_loader.cpp b/sleepyhead/SleepLib/loader_plugins/weinmann_loader.cpp new file mode 100644 index 00000000..7e176cf7 --- /dev/null +++ b/sleepyhead/SleepLib/loader_plugins/weinmann_loader.cpp @@ -0,0 +1,556 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * SleepLib (DeVilbiss) Weinmann Loader Implementation + * Notes: Weinmann requires the SmartLink attachment to access this data. + * + * Copyright (c) 2011-2014 Mark Watkins + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of the Linux + * distribution for more details. */ + +#include +#include +#include +#include +#include +#include + + +#include "weinmann_loader.h" + +extern QProgressBar *qprogress; + +Weinmann::Weinmann(MachineID id) + : CPAP(id) +{ +} + +Weinmann::~Weinmann() +{ +} + +WeinmannLoader::WeinmannLoader() +{ + m_buffer = nullptr; + m_type = MT_CPAP; +} + +WeinmannLoader::~WeinmannLoader() +{ +} + +bool WeinmannLoader::Detect(const QString & givenpath) +{ + QDir dir(givenpath); + + if (!dir.exists()) { + return false; + } + + // Weinman has a... + if (!dir.cd("SOMNOsoft2")) { + return false; + } + + // Check for the settings file inside the .. folder + if (!dir.exists("WM_DATA.TDF")) { + return false; + } + + return true; +} + +int WeinmannLoader::ParseIndex(QFile & wmdata) +{ + QByteArray xml; + do { + xml += wmdata.readLine(250); + } while (!wmdata.atEnd()); + + QDomDocument index_xml("weinmann"); + + index_xml.setContent(xml); + QDomElement docElem = index_xml.documentElement(); + + QDomNode n = docElem.firstChild(); + + index.clear(); + while (!n.isNull()) { + QDomElement e = n.toElement(); + if (!e.isNull()) { + bool ok; + int val = e.attribute("val").toInt(&ok); + if (ok) { + index[e.attribute("name")] = val; + qDebug() << e.attribute("name") << "=" << hex << val; + } + } + n = n.nextSibling(); + } + return index.size(); +} + +const QString DayComplianceCount = "DayComplianceCount"; +const QString CompOffset = "DayComplianceOffset"; +const QString FlowOffset = "TID_Flow_Offset"; +const QString StatusOffset = "TID_Status_Offset"; +const QString PresOffset = "TID_P_Offset"; +const QString AMVOffset = "TID_AMV_Offset"; +const QString EventsOffset = "TID_Events_Offset"; + + +void HighPass(char * data, int samples, float cutoff, float dt) +{ + float Y[samples]; + for (int i=0; i < samples; ++i) Y[i] = 0.0f; + + Y[0] = ((unsigned char *)data)[0] ; + + float RC = 1.0 / (cutoff * 2 * 3.1415926); + float alpha = RC / (RC + dt); + + for (int i=1; i < samples; ++i) { + float x = ((unsigned char *)data)[i] ; + float x1 = ((unsigned char *)data)[i-1] ; + Y[i] = alpha * (Y[i-1] + x - x1); + } + + for (int i=0; i< samples; ++i) { + data[i] = Y[i]; + } + +} + +int WeinmannLoader::Open(QString path) +{ + QString newpath; + + path = path.replace("\\", "/"); + + QString dirtag = "SOMNOsoft2"; + + if (path.endsWith("/" + dirtag)) { + return -1; + } else { + newpath = path + "/" + dirtag; + } + + QFile wmdata(newpath + "/WM_DATA.TDF"); + if (!wmdata.open(QFile::ReadOnly)) { + return -1; + } + + int res = ParseIndex(wmdata); + if (res < 0) return -1; + + + MachineInfo info = newInfo(); + info.serial = "141819"; + Machine * mach = CreateMachine(info); + + + ////////////////////////////////////////////////////////////////////// + // Read Day Compliance Information.... + ////////////////////////////////////////////////////////////////////// + + int comp_start = index[CompOffset]; + int comp_end = index[FlowOffset]; + int comp_size = comp_end - comp_start; + + unsigned char comp[comp_size]; + memset((char *)comp, 0, comp_size); + + wmdata.seek(comp_start); + wmdata.read((char *)comp, comp_size); + + unsigned char * p = comp; + + QDateTime dt_epoch(QDate(2000,1,1), QTime(0,0,0)); + int epoch = dt_epoch.toTime_t(); + epoch = 0; + + + float flow_sample_duration = 1000.0 / 5; + float pressure_sample_duration = 1000.0 / 2; + float amv_sample_duration = 200 * 10; + + int c = index[DayComplianceCount]; + for (int i=0; i < 5; i++) { + int year = QString().sprintf("%02i%02i", p[0], p[1]).toInt(); + int month = p[2]; + int day = p[3]; + int hour = p[5]; + int minute = p[6]; + int second = p[7]; + QDateTime date = QDateTime(QDate(year,month,day), QTime(hour,minute,second)); + quint32 ts = date.toTime_t(); + + if (mach->SessionExists(ts)) continue; + + Session * sess = new Session(mach, ts); + + // Flow Waveform + quint32 fs = p[8] | p[9] << 8 | p[10] << 16 | p[11] << 24; + quint32 fl = p[0x44] | p[0x45] << 8 | p[0x46] << 16 | p[0x47] << 24; + + // Status + quint32 ss = p[12] | p[13] << 8 | p[14] << 16 | p[15] << 24; + quint32 sl = p[0x48] | p[0x49] << 8 | p[0x4a] << 16 | p[0x4b] << 24; + + // Pressure + quint32 ps = p[16] | p[17] << 8 | p[18] << 16 | p[19] << 24; + quint32 pl = p[0x4c] | p[0x4d] << 8 | p[0x4e] << 16 | p[0x4f] << 24; + + // AMV + quint32 ms = p[20] | p[21] << 8 | p[22] << 16 | p[23] << 24; + quint32 ml = p[0x50] | p[0x51] << 8 | p[0x52] << 16 | p[0x53] << 24; + + // Events + quint32 es = p[24] | p[25] << 8 | p[26] << 16 | p[27] << 24; + quint32 er = p[0x54] | p[0x55] << 8 | p[0x56] << 16 | p[0x57] << 24; // number of records + + + compinfo.append(CompInfo(sess, date, fs, fl, ss, sl, ps, pl, ms, ml, es, er)); + + int dur = fl / 5; + + sess->really_set_first(qint64(ts) * 1000L); + sess->really_set_last(qint64(ts+dur) * 1000L); + sessions[ts] = sess; + + qDebug() << date << ts << dur << QString().sprintf("%02i:%02i:%02i", dur / 3600, dur/60 % 60, dur % 60); + + p += 0xd6; + } + + ////////////////////////////////////////////////////////////////////// + // Read Flow Waveform.... + ////////////////////////////////////////////////////////////////////// + + int flowstart = index[FlowOffset]; + int flowend = index[StatusOffset]; + + wmdata.seek(flowstart); + + int flowsize = flowend - flowstart; + char data[flowsize]; + memset((char *)data, 0, flowsize); + wmdata.read((char *)data, flowsize); + + float dt = 1.0 / (1000.0 / flow_sample_duration); // samples per second + + // Centre Waveform using High Pass Filter + HighPass(data, flowsize, 0.6, dt); + + ////////////////////////////////////////////////////////////////////// + // Read Status.... + ////////////////////////////////////////////////////////////////////// + + int st_start = index[StatusOffset]; + int st_end = index[PresOffset]; + int st_size = st_end - st_start; + + char st[st_size]; + memset(st, 0, st_size); + + wmdata.seek(st_start); + wmdata.read(st, st_size); + + + ////////////////////////////////////////////////////////////////////// + // Read Mask Pressure.... + ////////////////////////////////////////////////////////////////////// + + int pr_start = index[PresOffset]; + int pr_end = index[AMVOffset]; + int pr_size = pr_end - pr_start; + + char pres[pr_size]; + memset(pres, 0, pr_size); + + wmdata.seek(pr_start); + wmdata.read(pres, pr_size); + + ////////////////////////////////////////////////////////////////////// + // Read AMV.... + ////////////////////////////////////////////////////////////////////// + + int mv_start = index[AMVOffset]; + int mv_end = index[EventsOffset]; + int mv_size = mv_end - mv_start; + + char mv[mv_size]; + memset(mv, 0, mv_size); + + wmdata.seek(mv_start); + wmdata.read(mv, mv_size); + + ////////////////////////////////////////////////////////////////////// + // Read Events.... + ////////////////////////////////////////////////////////////////////// + + int ev_start = index[EventsOffset]; + int ev_end = wmdata.size(); + int ev_size = ev_end - ev_start; + + unsigned char ev[ev_size]; + memset((char *) ev, 0, ev_size); + + wmdata.seek(ev_start); + wmdata.read((char *) ev, ev_size); + + + ////////////////////////////////////////////////////////////////////// + // Process sessions + ////////////////////////////////////////////////////////////////////// + + for (int i=0; i< compinfo.size(); ++i) { + const CompInfo & ci = compinfo.at(i); + Session * sess = ci.session; + + qint64 ti = sess->first(); + + EventList * flow = sess->AddEventList(CPAP_FlowRate, EVL_Waveform, 1.0, 0.0, 0.0, 0.0, flow_sample_duration); + flow->AddWaveform(ti, &data[ci.flow_start], ci.flow_size, (ci.flow_size/(1000.0/flow_sample_duration)) * 1000.0); + + EventList * PR = sess->AddEventList(CPAP_MaskPressure, EVL_Waveform, 0.1f, 0.0, 0.0, 0.0, pressure_sample_duration); + PR->AddWaveform(ti, (unsigned char *)&pres[ci.pres_start], ci.pres_size, (ci.pres_size/(1000.0/pressure_sample_duration)) * 1000.0); + + // Weinmann's MV graph is pretty dodgy... commenting this out and using my flow calced ones instead (the code below is mapped to snore for comparison purposes) + //EventList * MV = sess->AddEventList(CPAP_Snore, EVL_Waveform, 1.0f, 0.0, 0.0, 0.0, amv_sample_duration); + //MV->AddWaveform(ti, (unsigned char *)&mv[ci.amv_start], ci.amv_size, (ci.amv_size/(1000/amv_sample_duration)) * 1000L); + + EventList * L = sess->AddEventList(CPAP_Leak, EVL_Event); + EventList * S = sess->AddEventList(CPAP_Snore, EVL_Event); + EventList * OA = sess->AddEventList(CPAP_Obstructive, EVL_Event); + EventList * A = sess->AddEventList(CPAP_Apnea, EVL_Event); + EventList * H = sess->AddEventList(CPAP_Hypopnea, EVL_Event); + EventList * FL = sess->AddEventList(CPAP_FlowLimit, EVL_Event); + EventList * VS = sess->AddEventList(CPAP_VSnore, EVL_Event); + quint64 tt = ti; + quint64 step = sess->length() / ci.event_recs; + unsigned char *p = &ev[ci.event_start]; + for (quint32 j=0; j < ci.event_recs; ++j) { + QDate evdate = ci.time.date(); + QTime evtime(p[1], p[2], p[3]); + if (evtime < ci.time.time()) { + evdate = evdate.addDays(1); + } + quint64 ts = QDateTime(evdate, evtime).toMSecsSinceEpoch(); + + // I think p[0] is amount of flow restriction.. + + unsigned char evcode = p[0]; + EventStoreType data = p[4] | p[5] << 8; + + if (evcode == '@') { + OA->AddEvent(ts,data/10.0); + } else if (evcode =='A') { + A->AddEvent(ts,data/10.0); + } else if (evcode == 'F') { + FL->AddEvent(ts,data/10.0); + } else if (evcode == '*') { + H->AddEvent(ts,data/10.0); + } +/* switch (evcode) { + case 0x03: + break; + case 0x04: + break; + case 0x08: + break; + case 0x09: + break; + case 0x0a: + break; + case 0x0b: + break; + case 0x0c: + break; + case 0x10: + break; + case 0x11: + break; + case 0x12: + break; + case 0x13: + S->AddEvent(ts, data); + break; + case 0x22: + // VS->AddEvent(ts, data/10.0); + + break; + case 0x28: + VS->AddEvent(ts, data/10.0); + + break; + case 'F': + FL->AddEvent(ts, data/10.0); + break; + case '@': + OA->AddEvent(ts, data/10.0); + break; + case '\'': + //A->AddEvent(ts, data/10.0); + break; + case 'a': + A->AddEvent(ts, data/10.0); + break; + case 'A': + // A->AddEvent(ts, data/10.0); + break; + case '*': + H->AddEvent(ts, data/10.0); + break; + case 'd': + break; + case 0x91: + break; + case 0x96: + break; + case 0x84: + break; + default: + qDebug() << (int)evcode << endl; + }*/ + + // S->AddEvent(ts, p[5]); + + + + // p[5] == 0 corresponds to peak events + // p[5] == 1 corresponds to hypopnea/bstructive events + + //if (p[5] == 2) OA->AddEvent(ts, p[4]); + + + // This is ugggggly... + + + tt += step; + p += 6; + } + + + + sess->UpdateSummaries(); + mach->AddSession(sess); + + + + } + + return 1; + +/* + + // Center the waveform + HighPass(data, flowsize, 0.6, dt); + + EventList * flow = sess->AddEventList(CPAP_FlowRate, EVL_Waveform, 1.0, 0.0, 0.0, 0.0, sample_duration); + flow->AddWaveform(tt, (char *)data, flowsize, (flowsize/(1000/sample_duration)) * 1000L); + + + qint64 ti = tt; + for (int i=0; i < pr_size; ++i) { + EventStoreType c = ((unsigned char *)pres)[i]; + PR->AddEvent(ti, c); + ti += sample_duration * 2.5; //46296296296296; + } + // Their calcs is uglier than mine! + EventList * MV = sess->AddEventList(CPAP_Snore, EVL_Event, 1.0); + + + ti = tt; + for (int i=0; i < mv_size; ++i) { + EventStoreType c = ((unsigned char *)mv)[i]; + MV->AddEvent(ti, c); + ti += sample_duration * 9; + } + + // Their calcs is uglier than mine! + EventList * ST = sess->AddEventList(CPAP_Leak, EVL_Event, 1.0); + int st_start = index[StatusOffset]; + int st_end = index[PresOffset]; + int st_size = st_end - st_start; + + char st[st_size]; + memset(st, 0, st_size); + + wmdata.seek(st_start); + wmdata.read(st, st_size); + + ti = tt; + for (int i=0; i < st_size; ++i) { + EventStoreType c = ((unsigned char *)st)[i]; + // if (c & 0x80) { + ST->AddEvent(ti, c & 0x10); + // } + ti += sample_duration*4; // *9 + } + + +// EventList * LEAK = sess->AddEventList(CPAP_Leak, EVL_Event); +// EventList * SNORE = sess->AddEventList(CPAP_Snore, EVL_Event); + +// int ev_start = index[EventsOffset]; +// int ev_end = wmdata.size(); +// int ev_size = ev_end - ev_start; +// int recs = ev_size / 0x12; + +// unsigned char ev[ev_size]; +// memset((char *) ev, 0, ev_size); + +// wmdata.seek(ev_start); +// wmdata.read((char *) ev, ev_size); + + sess->really_set_last(flow->last()); + +// int pos = 0; +// ti = tt; + +// // 6 byte repeating structure.. No Leaks :( +// do { +// //EventStoreType c = ((unsigned char*)ev)[pos+0]; // TV? +// //c = ((unsigned char*)ev)[pos+6]; // MV? + +// EventStoreType c = ((EventStoreType*)ev)[pos+0]; +// LEAK->AddEvent(ti, c); +// SNORE->AddEvent(ti, ((unsigned char*)ev)[pos+2]); +// pos += 0x6; +// ti += 30000; +// if (ti > sess->last()) +// break; +// } while (pos < (ev_size - 0x12)); + + + + m->AddSession(sess); + sess->UpdateSummaries(); + + return 1;*/ +} + +bool weinmann_initialized = false; +void WeinmannLoader::Register() +{ + if (weinmann_initialized) { return; } + + qDebug() << "Registering WeinmannLoader"; + RegisterLoader(new WeinmannLoader()); + //InitModelMap(); + weinmann_initialized = true; + + using namespace schema; + Channel * chan = nullptr; +// channel.add(GRP_CPAP, chan = new Channel(INTP_SmartFlex = 0x1165, SETTING, SESSION, +// "INTPSmartFlex", QObject::tr("SmartFlex"), +// QObject::tr("Weinmann pressure relief setting."), +// QObject::tr("SmartFlex"), +// "", DEFAULT, Qt::green)); + + +// chan->addOption(1, STR_TR_None); +} diff --git a/sleepyhead/SleepLib/loader_plugins/weinmann_loader.h b/sleepyhead/SleepLib/loader_plugins/weinmann_loader.h new file mode 100644 index 00000000..d2bd5b45 --- /dev/null +++ b/sleepyhead/SleepLib/loader_plugins/weinmann_loader.h @@ -0,0 +1,142 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * Weiman Loader Header + * + * Copyright (c) 2011-2014 Mark Watkins + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of the Linux + * distribution for more details. */ + +#ifndef WEINMANN_LOADER_H +#define WEINMANN_LOADER_H + +#include "SleepLib/machine.h" // Base class: MachineLoader +#include "SleepLib/machine_loader.h" +#include "SleepLib/profiles.h" + + +//******************************************************************************************** +/// IMPORTANT!!! +//******************************************************************************************** +// Please INCREMENT the following value when making changes to this loaders implementation. +// +const int weinmann_data_version = 3; +// +//******************************************************************************************** + +/*! \class Weinmann + \brief Weinmann customized machine object + */ +class Weinmann: public CPAP +{ + public: + Weinmann(MachineID id = 0); + virtual ~Weinmann(); +}; + + +struct CompInfo +{ + CompInfo() { + session = nullptr; + flow_start = 0; flow_size = 0; + stat_start = 0; stat_size = 0; + pres_start = 0; pres_size = 0; + amv_start = 0; amv_size =0; + event_start = 0; event_recs = 0; + } + CompInfo(const CompInfo & copy) { + session = copy.session; + time = copy.time; + flow_start = copy.flow_start; + flow_size= copy.flow_size; + stat_start = copy.flow_start; + stat_size= copy.flow_size; + pres_start = copy.pres_start; + pres_size = copy.pres_size; + amv_start = copy.amv_start; + amv_size = copy.amv_size; + event_start = copy.event_start; + event_recs = copy.event_recs; + } + CompInfo(Session * sess, QDateTime dt, quint32 fs, quint32 fl, quint32 ss, quint32 sl,quint32 ps, quint32 pl, quint32 ms, quint32 ml, quint32 es, quint32 er): + session(sess), time(dt), + flow_start(fs), flow_size(fl), + stat_start(ss), stat_size(sl), + pres_start(ps), pres_size(pl), + amv_start(ms), amv_size(ml), + event_start(es), event_recs(er) {} + Session * session; + QDateTime time; + quint32 flow_start; + quint32 flow_size; + quint32 stat_start; + quint32 stat_size; + quint32 pres_start; + quint32 pres_size; + quint32 amv_start; + quint32 amv_size; + quint32 event_start; + quint32 event_recs; +}; + +const QString weinmann_class_name = STR_MACH_Weinmann; + +/*! \class WeinmannLoader + \brief Loader for Weinmann CPAP data + This is only relatively recent addition and still needs more work + */ +class WeinmannLoader : public CPAPLoader +{ + public: + WeinmannLoader(); + virtual ~WeinmannLoader(); + + //! \brief Detect if the given path contains a valid Folder structure + virtual bool Detect(const QString & path); + + //! \brief Scans path for Weinmann data signature, and Loads any new data + virtual int Open(QString path); + + //! \brief Returns SleepLib database version of this Weinmann loader + virtual int Version() { return weinmann_data_version; } + + //! \brief Returns the machine loader name of this class + virtual const QString &loaderName() { return weinmann_class_name; } + + int ParseIndex(QFile & wmdata); + + + //! \brief Creates a machine object, indexed by serial number + // Machine *CreateMachine(QString serial); + + //! \brief Registers this MachineLoader with the master list, so Weinmann data can load + static void Register(); + + virtual MachineInfo newInfo() { + return MachineInfo(MT_CPAP, 0, weinmann_class_name, QObject::tr("Weinmann"), QString(), QString(), QString(), QObject::tr("Weinmann"), QDateTime::currentDateTime(), weinmann_data_version); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Now for some CPAPLoader overrides + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual QString presRelType() { return QObject::tr("Unknown"); } // might not need this one + + virtual ChannelID presRelSet() { return NoChannel; } + virtual ChannelID presRelLevel() { return NoChannel; } + //////////////////////////////////////////////////////////////////////////////////////////////////////////// + + protected: + QHash index; + QList compinfo; + QMap sessions; + + QString last; + + unsigned char *m_buffer; +}; + + +#endif // WEINMANN_LOADER_H diff --git a/sleepyhead/SleepLib/loader_plugins/zeo_loader.h b/sleepyhead/SleepLib/loader_plugins/zeo_loader.h index f752d3e2..85160e93 100644 --- a/sleepyhead/SleepLib/loader_plugins/zeo_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/zeo_loader.h @@ -37,7 +37,7 @@ class ZEOLoader : public MachineLoader //Machine *CreateMachine(); virtual MachineInfo newInfo() { - return MachineInfo(MT_SLEEPSTAGE, zeo_class_name, QObject::tr("Zeo"), QString(), QString(), QString(), QObject::tr("Personal Sleep Coach"), QDateTime::currentDateTime(), zeo_data_version); + return MachineInfo(MT_SLEEPSTAGE, 0, zeo_class_name, QObject::tr("Zeo"), QString(), QString(), QString(), QObject::tr("Personal Sleep Coach"), QDateTime::currentDateTime(), zeo_data_version); } protected: diff --git a/sleepyhead/SleepLib/machine.cpp b/sleepyhead/SleepLib/machine.cpp index 6f51623e..2910f86a 100644 --- a/sleepyhead/SleepLib/machine.cpp +++ b/sleepyhead/SleepLib/machine.cpp @@ -45,6 +45,7 @@ Machine::Machine(MachineID id) m_id = temp; } else { m_id = id; } + m_loader = nullptr; // qDebug() << "Create Machine: " << hex << m_id; //%lx",m_id); m_type = MT_UNKNOWN; @@ -363,6 +364,19 @@ bool Machine::Purge(int secret) return true; } +void Machine::setLoaderName(QString value) +{ + info.loadername = value; + m_loader = GetLoader(value); +} + +void Machine::setInfo(MachineInfo inf) +{ + info = inf; + m_loader = GetLoader(inf.loadername); +} + + //const quint32 channel_version=1; const QString Machine::getDataPath() @@ -701,44 +715,3 @@ PositionSensor::PositionSensor(MachineID id): Machine(id) PositionSensor::~PositionSensor() { } - - -ChannelID NoChannel, SESSION_ENABLED, CPAP_SummaryOnly; -ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CPAP_EPAPHi, CPAP_Pressure, - CPAP_PS, CPAP_Mode, CPAP_AHI, - CPAP_PressureMin, CPAP_PressureMax, CPAP_Ramp, CPAP_RampTime, CPAP_RampPressure, CPAP_Obstructive, - CPAP_Hypopnea, - CPAP_ClearAirway, CPAP_Apnea, CPAP_CSR, CPAP_LeakFlag, CPAP_ExP, CPAP_NRI, CPAP_VSnore, - CPAP_VSnore2, - CPAP_RERA, CPAP_PressurePulse, CPAP_FlowLimit, CPAP_SensAwake, CPAP_FlowRate, CPAP_MaskPressure, - CPAP_MaskPressureHi, - CPAP_RespEvent, CPAP_Snore, CPAP_MinuteVent, CPAP_RespRate, CPAP_TidalVolume, CPAP_PTB, CPAP_Leak, - CPAP_LeakMedian, CPAP_LeakTotal, CPAP_MaxLeak, CPAP_FLG, CPAP_IE, CPAP_Te, CPAP_Ti, CPAP_TgMV, - CPAP_UserFlag1, CPAP_UserFlag2, CPAP_UserFlag3, CPAP_BrokenSummary, CPAP_BrokenWaveform, CPAP_RDI, - CPAP_PresReliefSet, CPAP_PresReliefMode, CPAP_PresReliefType, CPAP_PSMin, CPAP_PSMax, CPAP_Test1, - CPAP_Test2; - - -ChannelID RMS9_E01, RMS9_E02, RMS9_EPR, RMS9_EPRLevel, RMS9_SetPressure, RMS9_MaskOnTime; -ChannelID INTP_SmartFlex; -ChannelID INTELLIPAP_Unknown1, INTELLIPAP_Unknown2; - -ChannelID PRS1_00, PRS1_01, PRS1_08, PRS1_0A, PRS1_0B, PRS1_0C, PRS1_0E, PRS1_0F, CPAP_LargeLeak, PRS1_12, - PRS1_FlexMode, PRS1_FlexSet, PRS1_HumidStatus, CPAP_HumidSetting, PRS1_SysLock, - PRS1_SysOneResistStat, - PRS1_SysOneResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI; - -ChannelID OXI_Pulse, OXI_SPO2, OXI_PulseChange, OXI_SPO2Drop, OXI_Plethy; - -ChannelID Journal_Notes, Journal_Weight, Journal_BMI, Journal_ZombieMeter, Bookmark_Start, - Bookmark_End, Bookmark_Notes; - - -ChannelID ZEO_SleepStage, ZEO_ZQ, ZEO_TotalZ, ZEO_TimeToZ, ZEO_TimeInWake, ZEO_TimeInREM, - ZEO_TimeInLight, ZEO_TimeInDeep, ZEO_Awakenings, - ZEO_AlarmReason, ZEO_SnoozeTime, ZEO_WakeTone, ZEO_WakeWindow, ZEO_AlarmType, ZEO_MorningFeel, - ZEO_FirmwareVersion, - ZEO_FirstAlarmRing, ZEO_LastAlarmRing, ZEO_FirstSnoozeTime, ZEO_LastSnoozeTime, ZEO_SetAlarmTime, - ZEO_RiseTime; - -ChannelID POS_Orientation, POS_Inclination; diff --git a/sleepyhead/SleepLib/machine.h b/sleepyhead/SleepLib/machine.h index 0edbb634..4203ad01 100644 --- a/sleepyhead/SleepLib/machine.h +++ b/sleepyhead/SleepLib/machine.h @@ -68,7 +68,7 @@ public: virtual void run() {} }; -class MachineLaoder; +class MachineLoader; /*! \class Machine \brief This Machine class is the Heart of SleepyLib, representing a single Machine and holding it's data @@ -182,20 +182,26 @@ class Machine inline QString modelnumber() const { return info.modelnumber; } inline QString serial() const { return info.serial; } inline QString series() const { return info.series; } + inline quint32 cap() const { return info.cap; } + inline int version() const { return info.version; } inline QDateTime lastImported() const { return info.lastimported; } inline void setModel(QString value) { info.model = value; } inline void setSerial(QString value) { info.serial = value; } inline void setType(MachineType type) { info.type = type; } - inline void setLoaderName(QString value) { info.loadername = value; } + inline void setCap(quint32 value) { info.cap = value; } + + void setLoaderName(QString value); + + MachineLoader * loader() { return m_loader; } // much more simpler multithreading... void queTask(ImportTask * task); void runTasks(); QMutex saveMutex; - void setInfo(MachineInfo inf) { info = inf; } + void setInfo(MachineInfo inf); const MachineInfo getInfo() { return info; } protected: @@ -206,6 +212,8 @@ class Machine MachineType m_type; QString m_path; + MachineLoader * m_loader; + bool changed; bool firstsession; int m_totaltasks; @@ -226,6 +234,7 @@ class CPAP: public Machine public: CPAP(MachineID id = 0); virtual ~CPAP(); + }; diff --git a/sleepyhead/SleepLib/machine_common.cpp b/sleepyhead/SleepLib/machine_common.cpp new file mode 100644 index 00000000..7cf839c2 --- /dev/null +++ b/sleepyhead/SleepLib/machine_common.cpp @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * + * SleepLib Common Machine Stuff + * + * Copyright (c) 2011-2014 Mark Watkins + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of the Linux + * distribution for more details. */ + + +#include "machine_common.h" + + +ChannelID NoChannel, SESSION_ENABLED, CPAP_SummaryOnly; +ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CPAP_EPAPHi, CPAP_Pressure, + CPAP_PS, CPAP_Mode, CPAP_AHI, + CPAP_PressureMin, CPAP_PressureMax, CPAP_Ramp, CPAP_RampTime, CPAP_RampPressure, CPAP_Obstructive, + CPAP_Hypopnea, + CPAP_ClearAirway, CPAP_Apnea, CPAP_CSR, CPAP_LeakFlag, CPAP_ExP, CPAP_NRI, CPAP_VSnore, + CPAP_VSnore2, + CPAP_RERA, CPAP_PressurePulse, CPAP_FlowLimit, CPAP_SensAwake, CPAP_FlowRate, CPAP_MaskPressure, + CPAP_MaskPressureHi, + CPAP_RespEvent, CPAP_Snore, CPAP_MinuteVent, CPAP_RespRate, CPAP_TidalVolume, CPAP_PTB, CPAP_Leak, + CPAP_LeakMedian, CPAP_LeakTotal, CPAP_MaxLeak, CPAP_FLG, CPAP_IE, CPAP_Te, CPAP_Ti, CPAP_TgMV, + CPAP_UserFlag1, CPAP_UserFlag2, CPAP_UserFlag3, CPAP_BrokenSummary, CPAP_BrokenWaveform, CPAP_RDI, + CPAP_PresReliefMode, CPAP_PresReliefLevel, CPAP_PSMin, CPAP_PSMax, CPAP_Test1, + CPAP_Test2, CPAP_HumidSetting; + + +ChannelID RMS9_E01, RMS9_E02, RMS9_SetPressure, RMS9_MaskOnTime; +ChannelID INTELLIPAP_Unknown1, INTELLIPAP_Unknown2; + +ChannelID PRS1_00, PRS1_01, PRS1_08, PRS1_0A, PRS1_0B, PRS1_0C, PRS1_0E, PRS1_0F, CPAP_LargeLeak, PRS1_12, + PRS1_FlexMode, PRS1_FlexLevel, PRS1_HumidStatus, PRS1_HumitSetting, PRS1_SysLock, + PRS1_SysOneResistStat, + PRS1_SysOneResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI; + +ChannelID OXI_Pulse, OXI_SPO2, OXI_PulseChange, OXI_SPO2Drop, OXI_Plethy; + +ChannelID Journal_Notes, Journal_Weight, Journal_BMI, Journal_ZombieMeter, Bookmark_Start, + Bookmark_End, Bookmark_Notes; + + +ChannelID ZEO_SleepStage, ZEO_ZQ, ZEO_TotalZ, ZEO_TimeToZ, ZEO_TimeInWake, ZEO_TimeInREM, + ZEO_TimeInLight, ZEO_TimeInDeep, ZEO_Awakenings, + ZEO_AlarmReason, ZEO_SnoozeTime, ZEO_WakeTone, ZEO_WakeWindow, ZEO_AlarmType, ZEO_MorningFeel, + ZEO_FirmwareVersion, + ZEO_FirstAlarmRing, ZEO_LastAlarmRing, ZEO_FirstSnoozeTime, ZEO_LastSnoozeTime, ZEO_SetAlarmTime, + ZEO_RiseTime; + +ChannelID POS_Orientation, POS_Inclination; diff --git a/sleepyhead/SleepLib/machine_common.h b/sleepyhead/SleepLib/machine_common.h index 92b6d070..ec69f61b 100644 --- a/sleepyhead/SleepLib/machine_common.h +++ b/sleepyhead/SleepLib/machine_common.h @@ -1,7 +1,7 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * - * SleepLib Machine Loader Class Header + * SleepLib Common Machine Header * * Copyright (c) 2011-2014 Mark Watkins * @@ -54,12 +54,31 @@ enum SummaryType { ST_CNT, ST_SUM, ST_AVG, ST_WAVG, ST_PERC, ST_90P, ST_MIN, ST_ enum MachineType { MT_UNKNOWN = 0, MT_CPAP, MT_OXIMETER, MT_SLEEPSTAGE, MT_JOURNAL, MT_POSITION }; //void InitMapsWithoutAwesomeInitializerLists(); +// PAP Device Capabilities +const quint32 CAP_Fixed = 0x0000001; // Constant PAP +const quint32 CAP_Variable = 0x0000002; // Variable Base (EPAP) pressure +const quint32 CAP_BiLevel = 0x0000004; // Fixed Pressure Support +const quint32 CAP_Variable_PS = 0x0000008; // Pressure support can range +const quint32 CAP_PressureRelief = 0x0000010; // Device has a pressure relief mode (EPR; Flex; SmartFlex) +const quint32 CAP_Humidification = 0x0000020; // Device has a humidifier attached + +// PAP Mode Capabilities +const quint32 PAP_CPAP = 0x0001; // Fixed Pressure PAP +const quint32 PAP_APAP = 0x0002; // Auto Ranging PAP +const quint32 PAP_BiLevelFixed = 0x0004; // Fixed BiLevel +const quint32 PAP_BiLevelAutoFixed = 0x0008; // Auto BiLevel with Fixed EPAP +const quint32 PAP_BiLevelAutoVariable = 0x0010; // Auto BiLevel with full ranging capabilities +const quint32 PAP_ASV_Fixed = 0x0020; // ASV with fixed EPAP +const quint32 PAP_ASV_Variable = 0x0040; // ASV with full ranging capabilities +const quint32 PAP_SplitNight = 0x8000; // Split night capabilities + + /*! \enum CPAPMode \brief CPAP Machines mode of operation */ enum CPAPMode { //:short - MODE_UNKNOWN = 0, MODE_CPAP, MODE_APAP, MODE_BILEVEL_FIXED, MODE_BILEVEL_AUTO_FIXED_PS, MODE_ASV, MODE_ASV_VARIABLE_EPAP + MODE_UNKNOWN = 0, MODE_CPAP, MODE_APAP, MODE_BILEVEL_FIXED, MODE_BILEVEL_AUTO_FIXED_PS, MODE_BILEVEL_AUTO_VARIABLE_PS, MODE_ASV, MODE_ASV_VARIABLE_EPAP }; /*! \enum PRTypes @@ -68,13 +87,13 @@ enum CPAPMode { //:short enum PRTypes { //:short PR_UNKNOWN = 0, PR_NONE, PR_CFLEX, PR_CFLEXPLUS, PR_AFLEX, PR_BIFLEX, PR_EPR, PR_SMARTFLEX, PR_EASYBREATHE, PR_SENSAWAKE }; -enum PRModes { //:short +enum PRTimeModes { //:short PM_UNKNOWN = 0, PM_RampOnly, PM_FullTime }; struct MachineInfo { - MachineInfo() { type = MT_UNKNOWN; version = 0; } + MachineInfo() { type = MT_UNKNOWN; version = 0; cap=0; } MachineInfo(const MachineInfo & copy) { type = copy.type; loadername = copy.loadername; @@ -85,12 +104,14 @@ struct MachineInfo { series = copy.series; version = copy.version; lastimported = copy.lastimported; + cap = copy.cap; } - MachineInfo(MachineType type, QString loadername, QString brand, QString model, QString modelnumber, QString serial, QString series, QDateTime lastimported, int version) : - type(type), loadername(loadername), brand(brand), model(model), modelnumber(modelnumber), serial(serial), series(series), lastimported(lastimported), version(version) {} + MachineInfo(MachineType type, quint32 cap, QString loadername, QString brand, QString model, QString modelnumber, QString serial, QString series, QDateTime lastimported, int version) : + type(type), cap(cap), loadername(loadername), brand(brand), model(model), modelnumber(modelnumber), serial(serial), series(series), lastimported(lastimported), version(version) {} MachineType type; + quint32 cap; QString loadername; QString brand; QString model; @@ -128,13 +149,12 @@ extern ChannelID CPAP_IPAP, CPAP_IPAPLo, CPAP_IPAPHi, CPAP_EPAP, CPAP_EPAPLo, CP CPAP_RespEvent, CPAP_Snore, CPAP_MinuteVent, CPAP_RespRate, CPAP_TidalVolume, CPAP_PTB, CPAP_Leak, CPAP_LeakMedian, CPAP_LeakTotal, CPAP_MaxLeak, CPAP_FLG, CPAP_IE, CPAP_Te, CPAP_Ti, CPAP_TgMV, CPAP_UserFlag1, CPAP_UserFlag2, CPAP_UserFlag3, CPAP_BrokenSummary, CPAP_BrokenWaveform, CPAP_RDI, - CPAP_PresReliefSet, CPAP_PresReliefMode, CPAP_PresReliefType, CPAP_Test1, CPAP_Test2; + CPAP_PresReliefMode, CPAP_PresReliefLevel, CPAP_Test1, CPAP_Test2; -extern ChannelID RMS9_E01, RMS9_E02, RMS9_EPR, RMS9_EPRLevel, RMS9_SetPressure, RMS9_MaskOnTime; -extern ChannelID INTP_SmartFlex; +extern ChannelID RMS9_E01, RMS9_E02, RMS9_SetPressure, RMS9_MaskOnTime; extern ChannelID PRS1_00, PRS1_01, PRS1_08, PRS1_0A, PRS1_0B, PRS1_0C, PRS1_0E, PRS1_0F, CPAP_LargeLeak, PRS1_12, - PRS1_FlexMode, PRS1_FlexSet, PRS1_HumidStatus, CPAP_HumidSetting, PRS1_SysLock, + PRS1_FlexMode, PRS1_FlexLevel, PRS1_HumidStatus, PRS1_HumitSetting, CPAP_HumidSetting, PRS1_SysLock, PRS1_SysOneResistStat, PRS1_SysOneResistSet, PRS1_HoseDiam, PRS1_AutoOn, PRS1_AutoOff, PRS1_MaskAlert, PRS1_ShowAHI; diff --git a/sleepyhead/SleepLib/machine_loader.cpp b/sleepyhead/SleepLib/machine_loader.cpp index 81851984..5de9034d 100644 --- a/sleepyhead/SleepLib/machine_loader.cpp +++ b/sleepyhead/SleepLib/machine_loader.cpp @@ -221,6 +221,26 @@ void MachineLoader::runTasks(bool threaded) } } + +QList CPAPLoader::eventFlags(Day * day) +{ + Machine * mach = day->machine; + + QList list; + + if (mach->loader() != this) { + qDebug() << "Trying to ask" << loaderName() << "for" << mach->loaderName() << "data"; + return list; + } + + list.push_back(CPAP_ClearAirway); + list.push_back(CPAP_Obstructive); + list.push_back(CPAP_Hypopnea); + list.push_back(CPAP_Apnea); + + return list; +} + /*const QString machine_profile_name="MachineList.xml"; void MachineLoader::LoadMachineList() diff --git a/sleepyhead/SleepLib/machine_loader.h b/sleepyhead/SleepLib/machine_loader.h index d62535d1..33724f72 100644 --- a/sleepyhead/SleepLib/machine_loader.h +++ b/sleepyhead/SleepLib/machine_loader.h @@ -59,15 +59,6 @@ class MachineLoader: public QObject virtual const QString &loaderName() = 0; inline MachineType type() { return m_type; } -// virtual bool openDevice() { return false; } -// virtual void closeDevice() {} -// virtual bool scanDevice(QString keyword="", quint16 vendor_id=0, quint16 product_id=0) { -// Q_UNUSED(keyword) -// Q_UNUSED(vendor_id) -// Q_UNUSED(product_id) -// return false; -// } - void queTask(ImportTask * task); void addSession(Session * sess) @@ -77,7 +68,6 @@ class MachineLoader: public QObject sessionMutex.unlock(); } - //! \brief Process Task list using all available threads. void runTasks(bool threaded=true); @@ -96,16 +86,6 @@ class MachineLoader: public QObject signals: void updateProgress(int cnt, int total); -// void updateDisplay(MachineLoader *); - -//protected slots: -// virtual void dataAvailable() {} -// virtual void resetImportTimeout() {} -// virtual void startImportTimeout() {} - -//protected: -// virtual void killTimers(){} -// virtual void resetDevice(){} protected: //! \brief Contains a list of Machine records known by this loader @@ -130,6 +110,21 @@ signals: }; +class CPAPLoader:public MachineLoader +{ + Q_OBJECT +public: + CPAPLoader() : MachineLoader() {} + virtual ~CPAPLoader() {} + + virtual QList eventFlags(Day * day); + + virtual QString PresReliefLabel() { return QString(""); } + virtual ChannelID PresReliefMode() { return NoChannel; } + virtual ChannelID PresReliefLevel() { return NoChannel; } + +}; + struct ImportPath { ImportPath() { diff --git a/sleepyhead/SleepLib/preferences.cpp b/sleepyhead/SleepLib/preferences.cpp index bccaae92..84efb2cb 100644 --- a/sleepyhead/SleepLib/preferences.cpp +++ b/sleepyhead/SleepLib/preferences.cpp @@ -218,7 +218,7 @@ bool Preferences::Open(QString filename) if (!e.isNull()) { QString name = e.tagName(); QString type = e.attribute("type").toLower(); - QString value = e.text();; + QString value = e.text(); if (type == "double") { double d; diff --git a/sleepyhead/SleepLib/profiles.cpp b/sleepyhead/SleepLib/profiles.cpp index 989fc5a7..5eb405cc 100644 --- a/sleepyhead/SleepLib/profiles.cpp +++ b/sleepyhead/SleepLib/profiles.cpp @@ -686,7 +686,7 @@ int Profile::Import(QString path) MachineLoader *GetLoader(QString name) { - QListloaders = GetLoaders(MT_CPAP); + QListloaders = GetLoaders(); Q_FOREACH(MachineLoader * loader, loaders) { if (loader->loaderName() == name) { @@ -848,7 +848,7 @@ Profile *Create(QString name) p_profile->Set(STR_GEN_DataFolder, QString("{home}/Profiles/{") + QString(STR_UI_UserName) + QString("}")); Machine *m = new Machine(0); - MachineInfo info(MT_JOURNAL, STR_MACH_Journal, "SleepyHead", STR_MACH_Journal, QString(), m->hexid(), QString(), QDateTime::currentDateTime(), 0); + MachineInfo info(MT_JOURNAL, 0, STR_MACH_Journal, "SleepyHead", STR_MACH_Journal, QString(), m->hexid(), QString(), QDateTime::currentDateTime(), 0); m->setInfo(info); p_profile->AddMachine(m); diff --git a/sleepyhead/SleepLib/schema.cpp b/sleepyhead/SleepLib/schema.cpp index f8d216ba..367e461a 100644 --- a/sleepyhead/SleepLib/schema.cpp +++ b/sleepyhead/SleepLib/schema.cpp @@ -392,9 +392,34 @@ void init() schema::channel.add(GRP_CPAP, new Channel(CPAP_SummaryOnly = 0x1026, SETTING, SESSION, "SummaryOnly", QObject::tr("Summary Only"), - QObject::tr("CPAP Session contains summary data onlyf"), QObject::tr("Summary Only"), STR_UNIT_Unknown, + QObject::tr("CPAP Session contains summary data only"), QObject::tr("Summary Only"), STR_UNIT_Unknown, DEFAULT, Qt::black)); + Channel *ch; + schema::channel.add(GRP_CPAP, ch = new Channel(CPAP_Mode = 0x1200, SETTING, SESSION, + "PAPMode", QObject::tr("PAP Mode"), + QObject::tr("PAP Mode"), QObject::tr("PAP_Mode"), STR_UNIT_Unknown, + LOOKUP, Qt::black)); + + ch->addOption(0, STR_TR_Unknown); + ch->addOption(1, STR_TR_CPAP); + ch->addOption(2, STR_TR_APAP); + ch->addOption(3, QObject::tr("Fixed Bi-Level")); + ch->addOption(4, QObject::tr("Auto Bi-Level (Fixed PS)")); + ch->addOption(5, QObject::tr("Auto Bi-Level (Variable PS)")); + ch->addOption(6, QObject::tr("ASV (Fixed EPAP)")); + ch->addOption(7, QObject::tr("ASV (Variable EPAP)")); + + +// +// + NoChannel = 0; // CPAP_IPAP=schema::channel["IPAP"].id(); @@ -405,7 +430,7 @@ void init() // CPAP_PS=schema::channel["PS"].id(); // CPAP_PSMin=schema::channel["PSMin"].id(); // CPAP_PSMax=schema::channel["PSMax"].id(); - CPAP_Mode = schema::channel["PAPMode"].id(); +// CPAP_Mode = schema::channel["PAPMode"].id(); CPAP_BrokenSummary = schema::channel["BrokenSummary"].id(); CPAP_BrokenWaveform = schema::channel["BrokenWaveform"].id(); // CPAP_PressureMin=schema::channel["PressureMin"].id(); @@ -446,20 +471,12 @@ void init() CPAP_Test1 = schema::channel["TestChan1"].id(); CPAP_Test2 = schema::channel["TestChan2"].id(); - CPAP_PresReliefSet = schema::channel["PresRelSet"].id(); - CPAP_PresReliefMode = schema::channel["PresRelMode"].id(); - CPAP_PresReliefType = schema::channel["PresRelType"].id(); - // CPAP_UserFlag1=schema::channel["UserFlag1"].id(); // CPAP_UserFlag2=schema::channel["UserFlag2"].id(); // CPAP_UserFlag3=schema::channel["UserFlag3"].id(); RMS9_E01 = schema::channel["RMS9_E01"].id(); RMS9_E02 = schema::channel["RMS9_E02"].id(); - RMS9_EPR = schema::channel["EPR"].id(); - RMS9_EPRLevel = schema::channel["EPRLevel"].id(); - RMS9_SetPressure = schema::channel["SetPressure"].id(); - PRS1_FlexMode = schema::channel["FlexMode"].id(); - PRS1_FlexSet = schema::channel["FlexSet"].id(); + RMS9_SetPressure = schema::channel["SetPressure"].id(); // TODO: this isn't needed anymore PRS1_HumidStatus = schema::channel["HumidStat"].id(); CPAP_HumidSetting = schema::channel["HumidSet"].id(); PRS1_SysLock = schema::channel["SysLock"].id(); diff --git a/sleepyhead/SleepLib/schema.h b/sleepyhead/SleepLib/schema.h index 73503c82..ec704216 100644 --- a/sleepyhead/SleepLib/schema.h +++ b/sleepyhead/SleepLib/schema.h @@ -38,7 +38,7 @@ enum ChanType { }; enum DataType { - DEFAULT = 0, INTEGER, BOOL, DOUBLE, STRING, RICHTEXT, DATE, TIME, DATETIME + DEFAULT = 0, INTEGER, BOOL, DOUBLE, STRING, RICHTEXT, DATE, TIME, DATETIME, LOOKUP }; enum ScopeType { GLOBAL = 0, MACHINE, DAY, SESSION diff --git a/sleepyhead/SleepLib/serialoximeter.h b/sleepyhead/SleepLib/serialoximeter.h index 17a5b83a..b55fdf26 100644 --- a/sleepyhead/SleepLib/serialoximeter.h +++ b/sleepyhead/SleepLib/serialoximeter.h @@ -48,7 +48,7 @@ public: virtual int Version()=0; virtual const QString &loaderName()=0; virtual MachineInfo newInfo() { - return MachineInfo(MT_OXIMETER, "", QString(), QString(), QString(), QString(), "Generic", QDateTime::currentDateTime(), 0); + return MachineInfo(MT_OXIMETER, 0, "", QString(), QString(), QString(), QString(), "Generic", QDateTime::currentDateTime(), 0); } // Serial Stuff diff --git a/sleepyhead/daily.cpp b/sleepyhead/daily.cpp index 8d2fec32..5bda16e4 100644 --- a/sleepyhead/daily.cpp +++ b/sleepyhead/daily.cpp @@ -154,13 +154,13 @@ Daily::Daily(QWidget *parent,gGraphView * shared) for (int i=0; i < cpapsize; ++i) { ChannelID code = cpapcodes[i]; - graphlist[schema::channel[code].label()] = new gGraph(schema::channel[code].code(), GraphView, schema::channel[code].label(), channelInfo(code), default_height); + graphlist[schema::channel[code].code()] = new gGraph(schema::channel[code].code(), GraphView, schema::channel[code].label(), channelInfo(code), default_height); } int oxigrp=p_profile->ExistsAndTrue("SyncOximetry") ? 0 : 1; // Contemplating killing this setting... for (int i=0; i < oxisize; ++i) { ChannelID code = oxicodes[i]; - graphlist[schema::channel[code].label()] = new gGraph(schema::channel[code].code(), GraphView, schema::channel[code].label(), channelInfo(code), default_height, oxigrp); + graphlist[schema::channel[code].code()] = new gGraph(schema::channel[code].code(), GraphView, schema::channel[code].label(), channelInfo(code), default_height, oxigrp); } if (p_profile->general->calculateRDI()) { @@ -251,7 +251,7 @@ Daily::Daily(QWidget *parent,gGraphView * shared) gLineOverlaySummary *los=new gLineOverlaySummary(tr("Selection AHI"),5,-4); AddCPAP(l); - gGraph *FRW = graphlist[schema::channel[CPAP_FlowRate].label()]; + gGraph *FRW = graphlist[schema::channel[CPAP_FlowRate].code()]; // Draw layer is important... spans first.. FRW->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_CSR, COLOR_CSR, STR_TR_CSR, FT_Span))); @@ -294,7 +294,10 @@ Daily::Daily(QWidget *parent,gGraphView * shared) bool square=p_profile->appearance->squareWavePlots(); gLineChart *pc=new gLineChart(CPAP_Pressure, COLOR_Pressure, square); - graphlist[schema::channel[CPAP_Pressure].label()]->AddLayer(AddCPAP(pc)); + graphlist[schema::channel[CPAP_Pressure].code()]->AddLayer(AddCPAP(pc)); + + graphlist[schema::channel[CPAP_Pressure].code()]->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_Ramp, COLOR_Ramp, schema::channel[CPAP_Ramp].label(), FT_Span))); + pc->addPlot(CPAP_EPAP, COLOR_EPAP, square); pc->addPlot(CPAP_IPAPLo, COLOR_IPAPLo, square); pc->addPlot(CPAP_IPAP, COLOR_IPAP, square); @@ -310,54 +313,54 @@ Daily::Daily(QWidget *parent,gGraphView * shared) // this is class wide because the leak redline can be reset in preferences.. // Better way would be having a search for linechart layers in graphlist[...] gLineChart *leakchart=new gLineChart(CPAP_Leak, COLOR_LeakTotal, square); - graphlist[schema::channel[CPAP_Leak].label()]->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_LargeLeak, COLOR_LargeLeak, STR_TR_LL, FT_Span))); + graphlist[schema::channel[CPAP_Leak].code()]->AddLayer(AddCPAP(new gLineOverlayBar(CPAP_LargeLeak, COLOR_LargeLeak, STR_TR_LL, FT_Span))); leakchart->addPlot(CPAP_LeakTotal, COLOR_Leak, square); leakchart->addPlot(CPAP_MaxLeak, COLOR_MaxLeak, square); schema::channel[CPAP_Leak].setUpperThresholdColor(Qt::red); schema::channel[CPAP_Leak].setLowerThresholdColor(Qt::green); - graphlist[schema::channel[CPAP_Leak].label()]->AddLayer(AddCPAP(leakchart)); + graphlist[schema::channel[CPAP_Leak].code()]->AddLayer(AddCPAP(leakchart)); //LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_Leak, COLOR_Leak,square))); //LEAK->AddLayer(AddCPAP(new gLineChart(CPAP_MaxLeak, COLOR_MaxLeak,square))); - graphlist[schema::channel[CPAP_Snore].label()]->AddLayer(AddCPAP(new gLineChart(CPAP_Snore, COLOR_Snore, true))); + graphlist[schema::channel[CPAP_Snore].code()]->AddLayer(AddCPAP(new gLineChart(CPAP_Snore, COLOR_Snore, true))); - graphlist[schema::channel[CPAP_PTB].label()]->AddLayer(AddCPAP(new gLineChart(CPAP_PTB, COLOR_PTB, square))); + graphlist[schema::channel[CPAP_PTB].code()]->AddLayer(AddCPAP(new gLineChart(CPAP_PTB, COLOR_PTB, square))); gLineChart *lc = nullptr; - graphlist[schema::channel[CPAP_MaskPressure].label()]->AddLayer(AddCPAP(new gLineChart(CPAP_MaskPressure, COLOR_MaskPressure, false))); - graphlist[schema::channel[CPAP_RespRate].label()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_RespRate, COLOR_RespRate, square))); + graphlist[schema::channel[CPAP_MaskPressure].code()]->AddLayer(AddCPAP(new gLineChart(CPAP_MaskPressure, COLOR_MaskPressure, false))); + graphlist[schema::channel[CPAP_RespRate].code()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_RespRate, COLOR_RespRate, square))); - graphlist[schema::channel[POS_Inclination].label()]->AddLayer(AddPOS(new gLineChart(POS_Inclination))); - graphlist[schema::channel[POS_Orientation].label()]->AddLayer(AddPOS(new gLineChart(POS_Orientation))); + graphlist[schema::channel[POS_Inclination].code()]->AddLayer(AddPOS(new gLineChart(POS_Inclination))); + graphlist[schema::channel[POS_Orientation].code()]->AddLayer(AddPOS(new gLineChart(POS_Orientation))); - graphlist[schema::channel[CPAP_MinuteVent].label()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_MinuteVent, COLOR_MinuteVent, square))); + graphlist[schema::channel[CPAP_MinuteVent].code()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_MinuteVent, COLOR_MinuteVent, square))); lc->addPlot(CPAP_TgMV,COLOR_TgMV,square); - graphlist[schema::channel[CPAP_TidalVolume].label()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_TidalVolume,COLOR_TidalVolume,square))); + graphlist[schema::channel[CPAP_TidalVolume].code()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_TidalVolume,COLOR_TidalVolume,square))); //lc->addPlot(CPAP_Test2,COLOR_DarkYellow,square); - //graphlist[schema::channel[CPAP_TidalVolume].label()]->AddLayer(AddCPAP(new gLineChart("TidalVolume2",COLOR_Magenta,square))); - graphlist[schema::channel[CPAP_FLG].label()]->AddLayer(AddCPAP(new gLineChart(CPAP_FLG, COLOR_FLG, true))); - //graphlist[schema::channel[CPAP_RespiratoryEvent].label()]->AddLayer(AddCPAP(new gLineChart(CPAP_RespiratoryEvent,COLOR_Magenta,true))); - graphlist[schema::channel[CPAP_IE].label()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_IE, COLOR_IE, square))); - graphlist[schema::channel[CPAP_Te].label()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_Te, COLOR_Te, square))); - graphlist[schema::channel[CPAP_Ti].label()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_Ti, COLOR_Ti, square))); + //graphlist[schema::channel[CPAP_TidalVolume].code()]->AddLayer(AddCPAP(new gLineChart("TidalVolume2",COLOR_Magenta,square))); + graphlist[schema::channel[CPAP_FLG].code()]->AddLayer(AddCPAP(new gLineChart(CPAP_FLG, COLOR_FLG, true))); + //graphlist[schema::channel[CPAP_RespiratoryEvent].code()]->AddLayer(AddCPAP(new gLineChart(CPAP_RespiratoryEvent,COLOR_Magenta,true))); + graphlist[schema::channel[CPAP_IE].code()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_IE, COLOR_IE, square))); + graphlist[schema::channel[CPAP_Te].code()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_Te, COLOR_Te, square))); + graphlist[schema::channel[CPAP_Ti].code()]->AddLayer(AddCPAP(lc=new gLineChart(CPAP_Ti, COLOR_Ti, square))); //lc->addPlot(CPAP_Test2,COLOR:DarkYellow,square); - graphlist[schema::channel[ZEO_SleepStage].label()]->AddLayer(AddSTAGE(new gLineChart(ZEO_SleepStage, COLOR_SleepStage, true))); + graphlist[schema::channel[ZEO_SleepStage].code()]->AddLayer(AddSTAGE(new gLineChart(ZEO_SleepStage, COLOR_SleepStage, true))); gLineOverlaySummary *los1=new gLineOverlaySummary(STR_UNIT_EventsPerHour,5,-4); gLineOverlaySummary *los2=new gLineOverlaySummary(STR_UNIT_EventsPerHour,5,-4); - graphlist[schema::channel[OXI_Pulse].label()]->AddLayer(AddOXI(los1->add(new gLineOverlayBar(OXI_PulseChange, COLOR_PulseChange, STR_TR_PC,FT_Span)))); - graphlist[schema::channel[OXI_Pulse].label()]->AddLayer(AddOXI(los1)); - graphlist[schema::channel[OXI_SPO2].label()]->AddLayer(AddOXI(los2->add(new gLineOverlayBar(OXI_SPO2Drop, COLOR_SPO2Drop, STR_TR_O2,FT_Span)))); - graphlist[schema::channel[OXI_SPO2].label()]->AddLayer(AddOXI(los2)); + graphlist[schema::channel[OXI_Pulse].code()]->AddLayer(AddOXI(los1->add(new gLineOverlayBar(OXI_PulseChange, COLOR_PulseChange, STR_TR_PC,FT_Span)))); + graphlist[schema::channel[OXI_Pulse].code()]->AddLayer(AddOXI(los1)); + graphlist[schema::channel[OXI_SPO2].code()]->AddLayer(AddOXI(los2->add(new gLineOverlayBar(OXI_SPO2Drop, COLOR_SPO2Drop, STR_TR_O2,FT_Span)))); + graphlist[schema::channel[OXI_SPO2].code()]->AddLayer(AddOXI(los2)); - graphlist[schema::channel[OXI_Pulse].label()]->AddLayer(AddOXI(new gLineChart(OXI_Pulse, COLOR_Pulse, square))); - graphlist[schema::channel[OXI_SPO2].label()]->AddLayer(AddOXI(new gLineChart(OXI_SPO2, COLOR_SPO2, true))); - graphlist[schema::channel[OXI_Plethy].label()]->AddLayer(AddOXI(new gLineChart(OXI_Plethy, COLOR_Plethy,false))); + graphlist[schema::channel[OXI_Pulse].code()]->AddLayer(AddOXI(new gLineChart(OXI_Pulse, COLOR_Pulse, square))); + graphlist[schema::channel[OXI_SPO2].code()]->AddLayer(AddOXI(new gLineChart(OXI_SPO2, COLOR_SPO2, true))); + graphlist[schema::channel[OXI_Plethy].code()]->AddLayer(AddOXI(new gLineChart(OXI_Plethy, COLOR_Plethy,false))); // Fix me @@ -370,8 +373,8 @@ Daily::Daily(QWidget *parent,gGraphView * shared) graphlist["INTSPO2"]->AddLayer(AddCPAP(los4)); graphlist["INTSPO2"]->AddLayer(AddCPAP(new gLineChart(OXI_SPO2, COLOR_SPO2, true))); - graphlist[schema::channel[CPAP_PTB].label()]->setForceMaxY(100); - graphlist[schema::channel[OXI_SPO2].label()]->setForceMaxY(100); + graphlist[schema::channel[CPAP_PTB].code()]->setForceMaxY(100); + graphlist[schema::channel[OXI_SPO2].code()]->setForceMaxY(100); for (it = graphlist.begin(); it != graphlist.end(); ++it) { if (skipgraph.contains(it.key())) continue; @@ -967,23 +970,23 @@ QString Daily::getMachineSettings(Day * cpap) { return html; } - if (cpap->settingExists(CPAP_PresReliefType)) { - int i=cpap->settings_max(CPAP_PresReliefType); - int j=cpap->settings_max(CPAP_PresReliefMode); - QString flexstr; + ChannelID pr_level_chan = NoChannel; + ChannelID pr_mode_chan = NoChannel; + CPAPLoader * loader = dynamic_cast(cpap->machine->loader()); + if (loader) { + pr_level_chan = loader->PresReliefLevel(); + pr_mode_chan = loader->PresReliefMode(); + } - if (cpap->machine->loaderName() == STR_MACH_ResMed) { - // this is temporary.. - flexstr = QString(tr("EPR:%1 EPR_LEVEL:%2")).arg(cpap->settings_max(RMS9_EPR)).arg(cpap->settings_max(RMS9_EPRLevel)); - } else { - flexstr = (i>1) ? schema::channel[CPAP_PresReliefType].option(i)+" x"+QString::number(j) : STR_TR_None; - } + if ((pr_level_chan != NoChannel) && (cpap->settingExists(pr_level_chan))) { + QString flexstr = cpap->getPressureRelief(); html+=QString("%1%2%3") - .arg(STR_TR_PrRelief) - .arg(schema::channel[CPAP_PresReliefType].description()) + .arg(schema::channel[pr_mode_chan].label()) + .arg(schema::channel[pr_mode_chan].description()) .arg(flexstr); } + QString mclass=cpap->machine->loaderName(); if (mclass==STR_MACH_PRS1 || mclass==STR_MACH_FPIcon) { int humid=round(cpap->settings_wavg(CPAP_HumidSetting)); diff --git a/sleepyhead/docs/channels.xml b/sleepyhead/docs/channels.xml index d699177d..f182da52 100644 --- a/sleepyhead/docs/channels.xml +++ b/sleepyhead/docs/channels.xml @@ -20,19 +20,10 @@ Important: One id code per item, DO NOT CHANGE ID NUMBERS!!! - - - - - - - - - - - diff --git a/sleepyhead/main.cpp b/sleepyhead/main.cpp index c770e0ee..b3b8afef 100644 --- a/sleepyhead/main.cpp +++ b/sleepyhead/main.cpp @@ -46,6 +46,7 @@ #include "SleepLib/loader_plugins/resmed_loader.h" #include "SleepLib/loader_plugins/intellipap_loader.h" #include "SleepLib/loader_plugins/icon_loader.h" +#include "SleepLib/loader_plugins/weinmann_loader.h" #ifdef Q_WS_X11 #include @@ -267,6 +268,7 @@ retry_directory: ResmedLoader::Register(); IntellipapLoader::Register(); FPIconLoader::Register(); + WeinmannLoader::Register(); CMS50Loader::Register(); MD300W1Loader::Register(); //ZEOLoader::Register(); // Use outside of directory importer.. diff --git a/sleepyhead/overview.cpp b/sleepyhead/overview.cpp index ec2a0988..871d204f 100644 --- a/sleepyhead/overview.cpp +++ b/sleepyhead/overview.cpp @@ -232,8 +232,9 @@ Overview::Overview(QWidget *parent, gGraphView *shared) : set = new SummaryChart("", GT_POINTS); //set->addSlice(PRS1_SysOneResistSet,COLOR_Gray,ST_SETAVG); set->addSlice(CPAP_HumidSetting, COLOR_Blue, ST_SETWAVG); - set->addSlice(CPAP_PresReliefSet, COLOR_Red, ST_SETWAVG); - set->addSlice(RMS9_EPRLevel,COLOR_Green,ST_SETWAVG); + set->addSlice(CPAP_PresReliefLevel, COLOR_Red, ST_SETWAVG); + set->addSlice(CPAP_PresReliefMode, COLOR_Red, ST_SETWAVG); +// set->addSlice(RMS9_EPRLevel,COLOR_Green,ST_SETWAVG); //set->addSlice(INTP_SmartFlex,COLOR_Purple,ST_SETWAVG); SET->AddLayer(set); diff --git a/sleepyhead/preferencesdialog.ui b/sleepyhead/preferencesdialog.ui index c1a78987..e32020a0 100644 --- a/sleepyhead/preferencesdialog.ui +++ b/sleepyhead/preferencesdialog.ui @@ -425,7 +425,7 @@ SleepyHead can keep a copy of this data if you ever need to reinstall. (Highly recomended, unless your short on disk space or don't care about the graph data) - Create SD Card Backups during Import (This is fairly important for ResMed and FP ICON) + Create SD Card Backups during Import (Turn this off at your own peril!) diff --git a/sleepyhead/sleepyhead.pro b/sleepyhead/sleepyhead.pro index 89bed396..b633f5a9 100644 --- a/sleepyhead/sleepyhead.pro +++ b/sleepyhead/sleepyhead.pro @@ -177,7 +177,9 @@ SOURCES += \ SleepLib/loader_plugins/md300w1_loader.cpp \ Graphs/gSessionTimesChart.cpp \ logger.cpp \ - welcome.cpp + welcome.cpp \ + SleepLib/machine_common.cpp \ + SleepLib/loader_plugins/weinmann_loader.cpp HEADERS += \ common_gui.h \ @@ -232,7 +234,8 @@ HEADERS += \ SleepLib/serialoximeter.h \ SleepLib/loader_plugins/md300w1_loader.h \ Graphs/gSessionTimesChart.h \ - logger.h + logger.h \ + SleepLib/loader_plugins/weinmann_loader.h FORMS += \ daily.ui \ diff --git a/sleepyhead/statistics.cpp b/sleepyhead/statistics.cpp index e47eafa6..9980efdf 100644 --- a/sleepyhead/statistics.cpp +++ b/sleepyhead/statistics.cpp @@ -261,8 +261,8 @@ struct RXChange { per2 = copy.per2; highlight = copy.highlight; weighted = copy.weighted; - prelief = copy.prelief; - prelset = copy.prelset; + pressure_string = copy.pressure_string; + pr_relief_string = copy.pr_relief_string; } QDate first; QDate last; @@ -270,6 +270,8 @@ struct RXChange { EventDataType ahi; EventDataType fl; CPAPMode mode; + QString pressure_string; + QString pr_relief_string; EventDataType min; EventDataType max; EventDataType ps; @@ -278,9 +280,7 @@ struct RXChange { EventDataType per1; EventDataType per2; EventDataType weighted; - PRTypes prelief; Machine *machine; - short prelset; short highlight; }; @@ -744,8 +744,7 @@ QString Statistics::GenerateHTML() EventDataType cmin = 0, cmax = 0, cps = 0, cpshi = 0, cmaxipap = 0, min = 0, max = 0, maxipap = 0, ps = 0, pshi = 0; Machine *mach = nullptr, *lastmach = nullptr; - PRTypes lastpr = PR_UNKNOWN, prelief = PR_UNKNOWN; - short prelset = 0, lastprelset = -1; + QString last_prel_str, last_pressure_str, prel_str, pressure_str; QDate date = lastcpap; Day *day; bool lastchanged = false; @@ -757,7 +756,12 @@ QString Statistics::GenerateHTML() do { day = p_profile->GetGoodDay(date, MT_CPAP); - if (day) { + + CPAPLoader * loader = nullptr; + + if (day) loader = dynamic_cast(day->machine->loader()); + + if (day && loader) { lastchanged = false; hours = day->hours(); @@ -766,17 +770,20 @@ QString Statistics::GenerateHTML() compliant++; } - EventDataType ahi = day->count(CPAP_Obstructive) + day->count(CPAP_Hypopnea) + day->count( - CPAP_Apnea) + day->count(CPAP_ClearAirway); + EventDataType ahi = day->count(CPAP_Obstructive) + day->count(CPAP_Hypopnea) + day->count(CPAP_Apnea) + day->count(CPAP_ClearAirway); if (p_profile->general->calculateRDI()) { ahi += day->count(CPAP_RERA); } ahi /= hours; AHI.push_back(UsageData(date, ahi, hours)); - prelief = (PRTypes)(int)round(day->settings_wavg(CPAP_PresReliefType)); - prelset = round(day->settings_max(CPAP_PresReliefSet)); + prel_str = day->getPressureRelief(); + pressure_str = day->getPressureSettings(); + mode = (CPAPMode)(int)round(day->settings_wavg(CPAP_Mode)); + if (mode ==0) { + mode = (CPAPMode)(int)round(day->settings_wavg(CPAP_Mode)); + } mach = day->machine; min = max = ps = pshi = maxipap = 0; @@ -794,6 +801,12 @@ QString Statistics::GenerateHTML() min = day->settings_min(CPAP_EPAPLo); maxipap = max = day->settings_max(CPAP_IPAPHi); ps = day->settings_min(CPAP_PS); + } else if (mode == MODE_BILEVEL_AUTO_VARIABLE_PS) { // Similar pressure control as ASV Variable EPAP + min = day->settings_min(CPAP_EPAPLo); + max = day->settings_min(CPAP_EPAPHi); + ps = day->settings_min(CPAP_PSMin); + pshi = day->settings_max(CPAP_PSMax); + maxipap = max = day->settings_max(CPAP_IPAPHi); } else if (mode == MODE_ASV) { min = day->settings_min(CPAP_EPAPLo); ps = day->settings_min(CPAP_PSMin); @@ -805,57 +818,66 @@ QString Statistics::GenerateHTML() ps = day->settings_min(CPAP_PSMin); pshi = day->settings_max(CPAP_PSMax); maxipap = max + pshi; + } - if ((mode != cmode) || (min != cmin) || (max != cmax) || (ps != cps) || (pshi != cpshi) - || (maxipap != cmaxipap) || (mach != lastmach) || (prelset != lastprelset)) { - if ((cmode != MODE_UNKNOWN) && (lastmach != nullptr)) { - first = date.addDays(1); - int days = p_profile->countDays(MT_CPAP, first, last); - RXChange rx; - rx.first = first; - rx.last = last; - rx.days = days; - rx.ahi = calcAHI(first, last); - rx.fl = calcFL(first, last); - rx.mode = cmode; - rx.min = cmin; - rx.max = cmax; - rx.ps = cps; - rx.pshi = cpshi; - rx.maxipap = cmaxipap; - rx.prelief = lastpr; - rx.prelset = lastprelset; - rx.machine = lastmach; - rx.per1 = 0; + if (lastmach == nullptr) { + lastmach = mach; + cmode = mode; + last_pressure_str = pressure_str; + last_prel_str = prel_str; + + } + + if ((mode != cmode) || (pressure_str != last_pressure_str) || (prel_str != last_prel_str) || (mach != lastmach)) { + first = date.addDays(1); + int days = p_profile->countDays(MT_CPAP, first, last); + RXChange rx; + rx.first = first; + rx.last = last; + rx.days = days; + rx.ahi = calcAHI(first, last); + rx.fl = calcFL(first, last); + rx.mode = cmode; + rx.pressure_string = last_pressure_str; + rx.pr_relief_string = last_prel_str; + rx.min = cmin; + rx.max = cmax; + rx.ps = cps; + rx.pshi = cpshi; + rx.maxipap = cmaxipap; + rx.machine = lastmach; + rx.per1 = 0; + rx.per2 = 0; + + if (mode == MODE_APAP) { + rx.per1 = p_profile->calcPercentile(CPAP_Pressure, percentile, MT_CPAP, first, last); rx.per2 = 0; - - if (mode == MODE_APAP) { - rx.per1 = p_profile->calcPercentile(CPAP_Pressure, percentile, MT_CPAP, first, last); - rx.per2 = 0; - } else if (mode == MODE_BILEVEL_AUTO_FIXED_PS) { - rx.per1 = p_profile->calcPercentile(CPAP_EPAP, percentile, MT_CPAP, first, last); - rx.per2 = p_profile->calcPercentile(CPAP_IPAP, percentile, MT_CPAP, first, last); - } else if (mode == MODE_ASV) { - rx.per1 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); - rx.per2 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); - } else if (mode == MODE_ASV_VARIABLE_EPAP) { - rx.per1 = p_profile->calcPercentile(CPAP_EPAP, percentile, MT_CPAP, first, last); - rx.per2 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); - } - - rx.weighted = float(rx.days) / float(cpapdays) * rx.ahi; - rxchange.push_back(rx); + } else if (mode == MODE_BILEVEL_AUTO_FIXED_PS) { + rx.per1 = p_profile->calcPercentile(CPAP_EPAP, percentile, MT_CPAP, first, last); + rx.per2 = p_profile->calcPercentile(CPAP_IPAP, percentile, MT_CPAP, first, last); + } else if (mode == MODE_BILEVEL_AUTO_VARIABLE_PS) { + rx.per1 = p_profile->calcPercentile(CPAP_EPAP, percentile, MT_CPAP, first, last); + rx.per2 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); + } else if (mode == MODE_ASV) { + rx.per1 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); + rx.per2 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); + } else if (mode == MODE_ASV_VARIABLE_EPAP) { + rx.per1 = p_profile->calcPercentile(CPAP_EPAP, percentile, MT_CPAP, first, last); + rx.per2 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); } + rx.weighted = float(rx.days) / float(cpapdays) * rx.ahi; + rxchange.push_back(rx); + cmode = mode; cmin = min; cmax = max; cps = ps; cpshi = pshi; cmaxipap = maxipap; - lastpr = prelief; - lastprelset = prelset; + last_prel_str = prel_str; + last_pressure_str = pressure_str; last = date; lastmach = mach; lastchanged = true; @@ -869,8 +891,9 @@ QString Statistics::GenerateHTML() // Sort list by AHI qSort(AHI); - lastchanged = false; + lastchanged = false; + // Add the final entry if (!lastchanged && (mach != nullptr)) { // last=date.addDays(1); first = firstcpap; @@ -887,16 +910,20 @@ QString Statistics::GenerateHTML() rx.ps = ps; rx.pshi = pshi; rx.maxipap = maxipap; - rx.prelief = prelief; - rx.prelset = prelset; rx.machine = mach; + rx.pressure_string = pressure_str; + rx.pr_relief_string = prel_str; + // Todo: Clean up by Calculating this crap later.. if (mode == MODE_APAP) { rx.per1 = p_profile->calcPercentile(CPAP_Pressure, percentile, MT_CPAP, first, last); rx.per2 = 0; } else if (mode == MODE_BILEVEL_AUTO_FIXED_PS) { rx.per1 = p_profile->calcPercentile(CPAP_EPAP, percentile, MT_CPAP, first, last); rx.per2 = p_profile->calcPercentile(CPAP_IPAP, percentile, MT_CPAP, first, last); + } else if (mode == MODE_BILEVEL_AUTO_VARIABLE_PS) { + rx.per1 = p_profile->calcPercentile(CPAP_EPAP, percentile, MT_CPAP, first, last); + rx.per2 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); } else if (mode == MODE_ASV) { rx.per1 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); rx.per2 = p_profile->calcPercentile(CPAP_PS, percentile, MT_CPAP, first, last); @@ -1013,12 +1040,15 @@ QString Statistics::GenerateHTML() modestr = STR_TR_BiLevel; } else if (mode == MODE_BILEVEL_AUTO_FIXED_PS) { modestr = QObject::tr("Auto Bi-Level"); + } else if (mode == MODE_BILEVEL_AUTO_VARIABLE_PS) { + modestr = QObject::tr("Auto Bi-Level"); } else if (mode == MODE_ASV) { - modestr = QObject::tr("ASV"); + modestr = QObject::tr("ASV Fixed EPAP"); } else if (mode == MODE_ASV_VARIABLE_EPAP) { - modestr = QObject::tr("ASV AutoEPAP"); + modestr = QObject::tr("ASV Auto EPAP"); } else modestr = STR_TR_Unknown; + recbox += QString("") .arg(idxstr[i]); recbox += QString("").arg(rx.min, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); - } else if (mode == MODE_APAP) { - extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.max, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); - } else if (mode == MODE_BILEVEL_FIXED) { - extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.max, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); - } else if (mode == MODE_BILEVEL_AUTO_FIXED_PS) { - extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.max, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()).arg(rx.ps, 4, 'f', 1); - } else if (mode == MODE_ASV) { - extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.ps, 4, 'f', 1).arg(rx.pshi, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); - tooltip = QString("%1 %2% ").arg(machstr).arg(percentile * 100.0) + - STR_TR_EPAP + - QString("=%1
%2% ").arg(rx.per1, 0, 'f', decimals) - .arg(percentile * 100.0) - + STR_TR_IPAP + QString("=%1").arg(rx.per2, 0, 'f', decimals); - } else if (mode == MODE_ASV_VARIABLE_EPAP) { - extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.max, 4, 'f', 1).arg(rx.ps, 4, 'f', 1).arg(rx.pshi, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); - tooltip = QString("%1 %2% ").arg(machstr).arg(percentile * 100.0) + - STR_TR_EPAP + - QString("=%1
%2% ").arg(rx.per1, 0, 'f', decimals) - .arg(percentile * 100.0) - + STR_TR_IPAP + QString("=%1").arg(rx.per2, 0, 'f', decimals); - } else { - extratxt += ""; - tooltip = ""; - } + + extratxt += ""; +// if (mode == MODE_CPAP) { +// extratxt += "").arg(rx.min, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); +// } else if (mode == MODE_APAP) { +// extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.max, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); +// } else if (mode == MODE_BILEVEL_FIXED) { +// extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.max, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); +// } else if (mode == MODE_BILEVEL_AUTO_FIXED_PS) { +// extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.max, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()).arg(rx.ps, 4, 'f', 1); +// } else if (mode == MODE_BILEVEL_AUTO_VARIABLE_PS) { +// extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.max, 4, 'f', 1).arg(rx.ps, 4, 'f', 1).arg(rx.pshi, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); +// } else if (mode == MODE_ASV) { +// extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.ps, 4, 'f', 1).arg(rx.pshi, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); +// tooltip = QString("%1 %2% ").arg(machstr).arg(percentile * 100.0) + +// STR_TR_EPAP + +// QString("=%1
%2% ").arg(rx.per1, 0, 'f', decimals) +// .arg(percentile * 100.0) +// + STR_TR_IPAP + QString("=%1").arg(rx.per2, 0, 'f', decimals); +// } else if (mode == MODE_ASV_VARIABLE_EPAP) { +// extratxt += "").arg(rx.min, 4, 'f', 1).arg(rx.max, 4, 'f', 1).arg(rx.ps, 4, 'f', 1).arg(rx.pshi, 4, 'f', 1).arg(schema::channel[CPAP_Pressure].units()); +// tooltip = QString("%1 %2% ").arg(machstr).arg(percentile * 100.0) + +// STR_TR_EPAP + +// QString("=%1
%2% ").arg(rx.per1, 0, 'f', decimals) +// .arg(percentile * 100.0) +// + STR_TR_IPAP + QString("=%1").arg(rx.per2, 0, 'f', decimals); +// } else { +// extratxt += ""; +// tooltip = ""; +// } extratxt += "
%3
") + STR_TR_Start + "
" + STR_TR_End + @@ -1080,7 +1110,7 @@ QString Statistics::GenerateHTML() hdrlist.push_back(STR_TR_SA); } hdrlist.push_back(STR_TR_Machine); - hdrlist.push_back(tr("Pr. Rel.")); + hdrlist.push_back(tr("Pressure Relief")); hdrlist.push_back(STR_TR_Mode); hdrlist.push_back(tr("Pressure Settings")); @@ -1135,40 +1165,38 @@ QString Statistics::GenerateHTML() // QString("=%1
%2% ").arg(rx.per1,0,'f',decimals).arg(percentile*100.0)+ // STR_TR_IPAP+QString("=%1").arg(rx.per2,0,'f',decimals); tooltip = QString("%1").arg(machstr); - if (mode == MODE_CPAP) { - extratxt += "
"+QString(tr("Fixed %1 %2")+""+QString(tr("%1 - %2 %3")+""+QString(tr("EPAP %1 %3 IPAP %2 %3")+""+QString(tr("PS %4 over %1 - %2 %3")+""+QString(tr("EPAP %1, PS %2-%3 %4")+""+QString(tr("EPAP %1-%2, PS %3-%4 %5")+""+rx.pressure_string+""+QString(tr("Fixed %1 %2")+""+QString(tr("%1 - %2 %3")+""+QString(tr("EPAP %1 %3 IPAP %2 %3")+""+QString(tr("PS %4 over %1 - %2 %3")+""+QString(tr("EPAP %1-%2, PS %3-%4 %5")+""+QString(tr("EPAP %1, PS %2-%3 %4")+""+QString(tr("EPAP %1-%2, PS %3-%4 %5")+"
"; - QString presrel; - - if (rx.prelset > 0) { - presrel = schema::channel[CPAP_PresReliefType].option(int(rx.prelief)); - presrel += QString(" x%1").arg(rx.prelset); - } else { presrel = STR_TR_None; } QString tooltipshow, tooltiphide; @@ -1198,8 +1226,8 @@ QString Statistics::GenerateHTML() html += QString("%1").arg(calcSA(rx.first, rx.last), 0, 'f', decimals); } html += QString("%1").arg(rx.machine->loaderName()); - html += QString("%1").arg(presrel); - html += QString("%1").arg(schema::channel[CPAP_Mode].option(int(rx.mode) - 1)); + html += QString("%1").arg(rx.pr_relief_string); + html += QString("%1").arg(schema::channel[CPAP_Mode].option(int(rx.mode))); html += QString("%1").arg(extratxt); html += ""; }