mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-05 10:40:42 +00:00
Add AirSense 10 CSR flags. Fixed summary load order messing up day splitting.
This commit is contained in:
parent
55390d9831
commit
f2facb9da9
@ -176,11 +176,17 @@ void gFlagsGroup::paint(QPainter &painter, gGraph &g, const QRegion ®ion)
|
||||
QColor barcol;
|
||||
|
||||
for (int i = 0; i < visflags.size(); i++) {
|
||||
schema::Channel & chan = schema::channel[visflags.at(i)->code()];
|
||||
|
||||
// Alternating box color
|
||||
if (i & 1) { barcol = COLOR_ALT_BG1; }
|
||||
else { barcol = COLOR_ALT_BG2; }
|
||||
|
||||
painter.fillRect(left, linetop, width-1, m_barh, QBrush(barcol));
|
||||
painter.fillRect(left, floor(linetop), width-1, ceil(m_barh), QBrush(barcol));
|
||||
|
||||
// barcol = chan.defaultColor();
|
||||
// barcol.setAlpha(16);
|
||||
// painter.fillRect(left, floor(linetop), width-1, ceil(m_barh), QBrush(barcol));
|
||||
|
||||
// Paint the actual flags
|
||||
QRect rect(left, linetop, width, m_barh);
|
||||
@ -350,12 +356,12 @@ void gFlagsLine::paint(QPainter &painter, gGraph &w, const QRegion ®ion)
|
||||
for (; dptr < eptr; dptr++) {
|
||||
X = start + * tptr++;
|
||||
|
||||
if (X > maxx) {
|
||||
break;
|
||||
}
|
||||
|
||||
L = *dptr * 1000L;
|
||||
X2 = X - L;
|
||||
if (X2 > maxx) {
|
||||
break;
|
||||
}
|
||||
|
||||
x1 = double(X - minx) * xmult + left;
|
||||
x2 = double(X2 - minx) * xmult + left;
|
||||
|
@ -50,7 +50,7 @@ const QColor COLOR_Brown = QColor("brown");
|
||||
const QColor COLOR_Text = Qt::black;
|
||||
const QColor COLOR_Outline = Qt::black;
|
||||
|
||||
const QColor COLOR_ALT_BG1 = QColor(0xd8, 0xff, 0xd8, 0xff); // Alternating Background Color 1 (Event Flags)
|
||||
const QColor COLOR_ALT_BG1 = QColor(0xc8, 0xff, 0xc8, 0x7f); // Alternating Background Color 1 (Event Flags)
|
||||
const QColor COLOR_ALT_BG2 = COLOR_White; // Alternating Background Color 2 (Event Flags)
|
||||
|
||||
|
||||
|
@ -764,9 +764,11 @@ void ResmedImport::run()
|
||||
#endif
|
||||
}
|
||||
|
||||
// Load annotations afterwards so durations are set correctly
|
||||
Q_FOREACH(QString file, files[EDF_CSL]) {
|
||||
// loader->LoadCSL(sess, file);
|
||||
loader->LoadCSL(sess, file);
|
||||
#ifdef SESSION_DEBUG
|
||||
sess->session_files.append(file);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool haveeve = false;
|
||||
@ -1198,7 +1200,7 @@ EDFType lookupEDFType(QString text)
|
||||
}
|
||||
|
||||
// Pretend to parse the EVE file to get the duration out of it.
|
||||
int PeekEVE(const QString & path, quint32 &start, quint32 &end)
|
||||
int PeekAnnotations(const QString & path, quint32 &start, quint32 &end)
|
||||
{
|
||||
EDFParser edf(path);
|
||||
if (!edf.Parse())
|
||||
@ -1251,7 +1253,7 @@ int PeekEVE(const QString & path, quint32 &start, quint32 &end)
|
||||
d = t.toDouble(&ok);
|
||||
|
||||
if (!ok) {
|
||||
qDebug() << "Faulty EDF EVE file " << edf.filename;
|
||||
qDebug() << "Faulty EDF Annotations file " << edf.filename;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1275,7 +1277,7 @@ int PeekEVE(const QString & path, quint32 &start, quint32 &end)
|
||||
duration = t.toDouble(&ok);
|
||||
|
||||
if (!ok) {
|
||||
qDebug() << "Faulty EDF EVE file (at %" << pos << ") " << edf.filename;
|
||||
qDebug() << "Faulty EDF Annotations file (at %" << pos << ") " << edf.filename;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1300,23 +1302,13 @@ int PeekEVE(const QString & path, quint32 &start, quint32 &end)
|
||||
|
||||
if (!t.isEmpty() && (t!="recording starts")) {
|
||||
goodrecs++;
|
||||
// if (matchSignal(CPAP_Obstructive, t)) {
|
||||
// } else if (matchSignal(CPAP_Hypopnea, t)) {
|
||||
// } else if (matchSignal(CPAP_Apnea, t)) {
|
||||
// } else if (matchSignal(CPAP_ClearAirway, t)) {
|
||||
// } else {
|
||||
// if (t != "recording starts") {
|
||||
// qDebug() << "Unobserved ResMed annotation field: " << t;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
if (pos >= recs) {
|
||||
qDebug() << "Short EDF EVE file" << edf.filename;
|
||||
qDebug() << "Short EDF Annotations file" << edf.filename;
|
||||
break;
|
||||
}
|
||||
|
||||
// pos++;
|
||||
}
|
||||
|
||||
while ((data[pos] == 0) && (pos < recs)) { pos++; }
|
||||
@ -1429,12 +1421,12 @@ EDFduration getEDFDuration(QString filename)
|
||||
|
||||
if (end < start) end = qMax(st2, start);
|
||||
|
||||
if (ext == "EVE") {
|
||||
if ((ext == "EVE") || (ext == "CSL")) {
|
||||
// S10 Forces us to parse EVE files to find their real durations
|
||||
quint32 en2;
|
||||
|
||||
// Have to get the actual duration of the EVE file by parsing the annotations. :(
|
||||
int recs = PeekEVE(filename, st2, en2);
|
||||
int recs = PeekAnnotations(filename, st2, en2);
|
||||
if (recs > 0) {
|
||||
start = qMin(st2, start);
|
||||
end = qMax(en2, end);
|
||||
@ -1444,7 +1436,7 @@ EDFduration getEDFDuration(QString filename)
|
||||
|
||||
return dur;
|
||||
} else {
|
||||
// empty EVE file, don't give a crap about it...
|
||||
// empty annotations file, don't give a crap about it...
|
||||
return EDFduration(0, 0, filename);
|
||||
}
|
||||
// A Firmware bug causes (perhaps with failing SD card) sessions to sometimes take a long time to write
|
||||
@ -1593,9 +1585,8 @@ int ResmedLoader::scanFiles(Machine * mach, QString datalog_path)
|
||||
EDForder.push_back(EDF_PLD);
|
||||
EDForder.push_back(EDF_BRP);
|
||||
EDForder.push_back(EDF_SAD);
|
||||
EDForder.push_back(EDF_CSL);
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
for (int i=0; i<3; i++) {
|
||||
EDFType basetype = EDForder.takeFirst();
|
||||
|
||||
// Process PLD files
|
||||
@ -1659,6 +1650,24 @@ int ResmedLoader::scanFiles(Machine * mach, QString datalog_path)
|
||||
}
|
||||
}
|
||||
|
||||
// CSL files contain CSR flags
|
||||
QList<EDFduration *> & CSL_list = filesbytype[EDF_CSL];
|
||||
list_end = CSL_list.end();
|
||||
for (item = CSL_list.begin(); item != list_end; ++item) {
|
||||
const EDFduration * dur2 = *item;
|
||||
if (dur2->start == 0) continue;
|
||||
|
||||
// Do the sessions Overlap?
|
||||
if ((start < dur2->end) && ( dur2->start < end)) {
|
||||
// start = qMin(start, dur2->start);
|
||||
// end = qMax(end, dur2->end);
|
||||
|
||||
files.append(dur2->filename);
|
||||
|
||||
grp[EDF_CSL].append(create_backups ? backup(dur2->path, backup_path) : dur2->path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (mach->SessionExists(start) == nullptr) {
|
||||
@ -2257,6 +2266,147 @@ QString ResmedLoader::backup(QString fullname, QString backup_path)
|
||||
return newname;
|
||||
}
|
||||
|
||||
bool ResmedLoader::LoadCSL(Session *sess, const QString & path)
|
||||
{
|
||||
EDFParser edf(path);
|
||||
if (!edf.Parse())
|
||||
return false;
|
||||
|
||||
QString t;
|
||||
|
||||
long recs;
|
||||
double duration;
|
||||
char *data;
|
||||
char c;
|
||||
long pos;
|
||||
bool sign, ok;
|
||||
double d;
|
||||
double tt;
|
||||
|
||||
// Notes: Event records have useless duration record.
|
||||
// sess->updateFirst(edf.startdate);
|
||||
|
||||
EventList *CSR = nullptr;
|
||||
|
||||
// Allow for empty sessions..
|
||||
qint64 csr_starts = 0;
|
||||
|
||||
|
||||
// Process event annotation records
|
||||
for (int s = 0; s < edf.GetNumSignals(); s++) {
|
||||
recs = edf.edfsignals[s].nr * edf.GetNumDataRecords() * 2;
|
||||
|
||||
data = (char *)edf.edfsignals[s].data;
|
||||
pos = 0;
|
||||
tt = edf.startdate;
|
||||
// sess->updateFirst(tt);
|
||||
duration = 0;
|
||||
|
||||
while (pos < recs) {
|
||||
c = data[pos];
|
||||
|
||||
if ((c != '+') && (c != '-')) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (data[pos++] == '+') { sign = true; }
|
||||
else { sign = false; }
|
||||
|
||||
t = "";
|
||||
c = data[pos];
|
||||
|
||||
do {
|
||||
t += c;
|
||||
pos++;
|
||||
c = data[pos];
|
||||
} while ((c != 20) && (c != 21)); // start code
|
||||
|
||||
d = t.toDouble(&ok);
|
||||
|
||||
if (!ok) {
|
||||
qDebug() << "Faulty EDF CSL file " << edf.filename;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!sign) { d = -d; }
|
||||
|
||||
tt = edf.startdate + qint64(d * 1000.0);
|
||||
duration = 0;
|
||||
// First entry
|
||||
|
||||
if (data[pos] == 21) {
|
||||
pos++;
|
||||
// get duration.
|
||||
t = "";
|
||||
|
||||
do {
|
||||
t += data[pos];
|
||||
pos++;
|
||||
} while ((data[pos] != 20) && (pos < recs)); // start code
|
||||
|
||||
duration = t.toDouble(&ok);
|
||||
|
||||
if (!ok) {
|
||||
qDebug() << "Faulty EDF CSL file (at %" << pos << ") " << edf.filename;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while ((data[pos] == 20) && (pos < recs)) {
|
||||
t = "";
|
||||
pos++;
|
||||
|
||||
if (data[pos] == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (data[pos] == 20) {
|
||||
pos++;
|
||||
break;
|
||||
}
|
||||
|
||||
do {
|
||||
t += tolower(data[pos++]);
|
||||
} while ((data[pos] != 20) && (pos < recs)); // start code
|
||||
|
||||
if (!t.isEmpty()) {
|
||||
if (t == "csr start") {
|
||||
csr_starts = tt;
|
||||
} else if (t == "csr end") {
|
||||
if (!CSR) {
|
||||
CSR = sess->AddEventList(CPAP_CSR, EVL_Event);
|
||||
}
|
||||
|
||||
if (csr_starts > 0) {
|
||||
if (sess->checkInside(csr_starts))
|
||||
CSR->AddEvent(tt, double(tt - csr_starts) / 1000.0);
|
||||
csr_starts = 0;
|
||||
} else {
|
||||
qDebug() << "If you can read this, ResMed sucks and split CSR flagging!";
|
||||
}
|
||||
} else if (t != "recording starts") {
|
||||
qDebug() << "Unobserved ResMed CSL annotation field: " << t;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos >= recs) {
|
||||
qDebug() << "Short EDF CSL file" << edf.filename;
|
||||
break;
|
||||
}
|
||||
|
||||
// pos++;
|
||||
}
|
||||
|
||||
while ((data[pos] == 0) && (pos < recs)) { pos++; }
|
||||
|
||||
if (pos >= recs) { break; }
|
||||
}
|
||||
|
||||
// sess->updateLast(tt);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ResmedLoader::LoadEVE(Session *sess, const QString & path)
|
||||
{
|
||||
|
@ -424,6 +424,10 @@ class ResmedLoader : public CPAPLoader
|
||||
//! This contains all Hypopnea, Obstructive Apnea, Central and Apnea codes
|
||||
bool LoadEVE(Session *sess, const QString & path);
|
||||
|
||||
//! \brief Parse the CSL Event annotation data, and save to Session * sess
|
||||
//! This contains Cheyne Stokes Respiration flagging on the AirSense 10
|
||||
bool LoadCSL(Session *sess, const QString & path);
|
||||
|
||||
//! \brief Parse the BRP High Resolution data, and save to Session * sess
|
||||
//! This contains Flow Rate, Mask Pressure, and Resp. Event data
|
||||
bool LoadBRP(Session *sess, const QString & path);
|
||||
|
@ -844,7 +844,7 @@ bool Machine::hasModifiedSessions()
|
||||
|
||||
const QString summaryFileName = "Summaries.xml";
|
||||
|
||||
bool Machine::LoadSummary(bool everything)
|
||||
bool Machine::LoadSummary()
|
||||
{
|
||||
QTime time;
|
||||
time.start();
|
||||
@ -885,6 +885,9 @@ bool Machine::LoadSummary(bool everything)
|
||||
QDomNodeList sessionlist = root.childNodes();
|
||||
|
||||
int size = sessionlist.size();
|
||||
|
||||
QMap<qint64, Session *> sess_order;
|
||||
|
||||
for (int s=0; s < size; ++s) {
|
||||
node = sessionlist.at(s);
|
||||
QDomElement e = node.toElement();
|
||||
@ -899,9 +902,21 @@ bool Machine::LoadSummary(bool everything)
|
||||
sess->really_set_last(last);
|
||||
sess->setEnabled(enabled);
|
||||
sess->setSummaryOnly(!events);
|
||||
if (!AddSession(sess))
|
||||
delete sess;
|
||||
// sess->LoadSummary();
|
||||
|
||||
sess_order[first] = sess;
|
||||
}
|
||||
}
|
||||
QMap<qint64, Session *>::iterator it_end = sess_order.end();
|
||||
QMap<qint64, Session *>::iterator it;
|
||||
int cnt = 0;
|
||||
bool loadSummaries = p_profile->session->preloadSummaries();
|
||||
|
||||
for (it = sess_order.begin(); it != it_end; ++it, ++cnt) {
|
||||
Session * sess = it.value();
|
||||
if (!AddSession(sess)) {
|
||||
delete sess;
|
||||
} else {
|
||||
if (loadSummaries) sess->LoadSummary();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ class Machine
|
||||
|
||||
//! \brief Load all Machine summary data
|
||||
bool Load();
|
||||
bool LoadSummary(bool everything = false);
|
||||
bool LoadSummary();
|
||||
|
||||
//! \brief Save all Sessions where changed bit is set.
|
||||
bool Save();
|
||||
|
@ -728,7 +728,11 @@ bool PreferencesDialog::Save()
|
||||
profile->cpap->setShowComplianceInfo(ui->complianceCheckBox->isChecked());
|
||||
profile->cpap->setComplianceHours(ui->complianceHours->value());
|
||||
|
||||
profile->appearance->setGraphHeight(ui->graphHeight->value());
|
||||
if (ui->graphHeight->value() != profile->appearance->graphHeight()) {
|
||||
profile->appearance->setGraphHeight(ui->graphHeight->value());
|
||||
mainwin->getDaily()->ResetGraphLayout();
|
||||
mainwin->getOverview()->ResetGraphLayout();
|
||||
}
|
||||
|
||||
profile->general->setPrefCalcMiddle(ui->prefCalcMiddle->currentIndex());
|
||||
profile->general->setPrefCalcMax(ui->prefCalcMax->currentIndex());
|
||||
|
Loading…
Reference in New Issue
Block a user